Merge "Import translations. DO NOT MERGE ANYWHERE"
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index bf9059f..a48e2c0 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -103,16 +103,10 @@
              android:clearTaskOnLaunch="true"
              android:exported="false"/>
 
-        <!--
-        Activity for gesture nav onboarding.
-        It's protected by android.permission.REBOOT to ensure that only system apps can start it
-        (setup wizard already has this permission)
-        -->
         <activity android:name="com.android.quickstep.interaction.GestureSandboxActivity"
             android:autoRemoveFromRecents="true"
             android:excludeFromRecents="true"
             android:screenOrientation="portrait"
-            android:permission="android.permission.REBOOT"
             android:exported="true">
             <intent-filter>
                 <action android:name="com.android.quickstep.action.GESTURE_SANDBOX"/>
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-az/strings.xml b/quickstep/res/values-az/strings.xml
index dceab09..3c5587c 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -83,7 +83,7 @@
     <string name="allset_description" msgid="6350320429953234580">"Telefondan istifadəyə başlamağa hazırsınız"</string>
     <string name="allset_navigation_settings" msgid="417773244979225071"><annotation id="link">"Əlçatımlıq üzrə naviqasiya ayarları"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Paylaşın"</string>
-    <string name="action_screenshot" msgid="8171125848358142917">"Ekran şəkli"</string>
+    <string name="action_screenshot" msgid="8171125848358142917">"Skrinşot"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Bu əməliyyata tətbiq və ya təşkilatınız tərəfindən icazə verilmir"</string>
     <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Naviqasiya dərsliyi ötürülsün?"</string>
     <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Bunu sonra <xliff:g id="NAME">%1$s</xliff:g> tətbiqində tapa bilərsiniz"</string>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 4f62b34..a2c0a7d 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 82eaecd..deeaaa6 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -42,6 +42,7 @@
 import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
 import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
 import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
+import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_CANCELED;
 import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
@@ -378,6 +379,17 @@
             activity.runOnceOnStart(this::onLauncherStart);
         }
 
+        // Set up a entire animation lifecycle callback to notify the current recents view when
+        // the animation is canceled
+        mGestureState.runOnceAtState(STATE_RECENTS_ANIMATION_CANCELED, () -> {
+                ThumbnailData snapshot = mGestureState.getRecentsAnimationCanceledSnapshot();
+                if (snapshot != null) {
+                    RecentsModel.INSTANCE.get(mContext).onTaskSnapshotChanged(
+                            mRecentsView.getRunningTaskId(), snapshot);
+                    mRecentsView.onRecentsAnimationComplete();
+                }
+            });
+
         setupRecentsViewUi();
         linkRecentsViewScroll();
 
@@ -671,6 +683,9 @@
             mRecentsAnimationController.setUseLauncherSystemBarFlags(swipeUpThresholdPassed
                     ||  (quickswitchThresholdPassed && centermostTaskFlags != 0));
             mRecentsAnimationController.setSplitScreenMinimized(swipeUpThresholdPassed);
+            // Provide a hint to WM the direction that we will be settling in case the animation
+            // needs to be canceled
+            mRecentsAnimationController.setWillFinishToHome(swipeUpThresholdPassed);
 
             if (swipeUpThresholdPassed) {
                 mActivity.getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0);
@@ -1468,9 +1483,6 @@
             final boolean refreshView = !LIVE_TILE.get() /* refreshView */;
             boolean finishTransitionPosted = false;
             if (mRecentsAnimationController != null) {
-                if (LIVE_TILE.get()) {
-                    mRecentsAnimationController.getController().setWillFinishToHome(true);
-                }
                 // Update the screenshot of the task
                 if (mTaskSnapshot == null) {
                     UI_HELPER_EXECUTOR.execute(() -> {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 6ad7f55..a302a07 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -142,6 +142,8 @@
     private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
     private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
     private int mLastStartedTaskId = -1;
+    private RecentsAnimationController mRecentsAnimationController;
+    private ThumbnailData mRecentsAnimationCanceledSnapshot;
 
     /** The time when the swipe up gesture is triggered. */
     private long mSwipeUpStartTimeMs;
@@ -351,13 +353,20 @@
     @Override
     public void onRecentsAnimationStart(RecentsAnimationController controller,
             RecentsAnimationTargets targets) {
+        mRecentsAnimationController = controller;
         mStateCallback.setState(STATE_RECENTS_ANIMATION_STARTED);
     }
 
     @Override
     public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+        mRecentsAnimationCanceledSnapshot = thumbnailData;
         mStateCallback.setState(STATE_RECENTS_ANIMATION_CANCELED);
         mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
+        if (mRecentsAnimationCanceledSnapshot != null) {
+            // Clean up the screenshot to finalize the recents animation cancel
+            mRecentsAnimationController.cleanupScreenshot();
+            mRecentsAnimationCanceledSnapshot = null;
+        }
     }
 
     @Override
@@ -366,6 +375,14 @@
         mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
     }
 
+    /**
+     * Returns the canceled animation thumbnail data. This call only returns a value while
+     * STATE_RECENTS_ANIMATION_CANCELED state is being set.
+     */
+    ThumbnailData getRecentsAnimationCanceledSnapshot() {
+        return mRecentsAnimationCanceledSnapshot;
+    }
+
     void setSwipeUpStartTimeMs(long uptimeMs) {
         mSwipeUpStartTimeMs = uptimeMs;
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 9e69ef9..f343485 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -159,6 +159,14 @@
     }
 
     /**
+     * @see IRecentsAnimationController#cleanupScreenshot()
+     */
+    @UiThread
+    public void cleanupScreenshot() {
+        UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
+    }
+
+    /**
      * @see RecentsAnimationControllerCompat#detachNavigationBarFromApp
      */
     @UiThread
@@ -175,6 +183,14 @@
     }
 
     /**
+     * @see IRecentsAnimationController#setWillFinishToHome(boolean)
+     */
+    @UiThread
+    public void setWillFinishToHome(boolean willFinishToHome) {
+        UI_HELPER_EXECUTOR.execute(() -> mController.setWillFinishToHome(willFinishToHome));
+    }
+
+    /**
      * Sets the final surface transaction on a Task. This is used by Launcher to notify the system
      * that animating Activity to PiP has completed and the associated task surface should be
      * updated accordingly. This should be called before `finish`
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 37cb979..e8324f7 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;
@@ -323,10 +324,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;
@@ -655,7 +656,7 @@
                 // launched while device is locked even after exiting direct boot mode (e.g. camera).
                 return createDeviceLockedInputConsumer(newGestureState);
             } else {
-                return mResetGestureInputConsumer;
+                return getDefaultInputConsumer();
             }
         }
 
@@ -664,7 +665,7 @@
         InputConsumer base = canStartSystemGesture
                 || previousGestureState.isRecentsAnimationRunning()
                         ? newBaseConsumer(previousGestureState, newGestureState, event)
-                        : mResetGestureInputConsumer;
+                        : getDefaultInputConsumer();
         if (mDeviceState.isGesturalNavMode()) {
             handleOrientationSetup(base);
         }
@@ -726,7 +727,7 @@
             }
         } else {
             if (mDeviceState.isScreenPinningActive()) {
-                base = mResetGestureInputConsumer;
+                base = getDefaultInputConsumer();
             }
 
             if (mDeviceState.canTriggerOneHandedAction(event)) {
@@ -768,14 +769,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);
         }
@@ -803,7 +804,7 @@
             return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager,
                     gestureState, mInputMonitorCompat);
         } else {
-            return mResetGestureInputConsumer;
+            return getDefaultInputConsumer();
         }
     }
 
@@ -812,7 +813,7 @@
             boolean forceOverviewInputConsumer) {
         StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity();
         if (activity == null) {
-            return mResetGestureInputConsumer;
+            return getDefaultInputConsumer();
         }
 
         if (activity.getRootView().hasWindowFocus()
@@ -841,13 +842,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
@@ -856,6 +851,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/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index cf523d0..bc49133 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -18,6 +18,7 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.view.Display;
 import android.view.View;
@@ -122,9 +123,9 @@
         mCurrentTutorialStep = mTutorialSteps[mCurrentStep];
         mFragment = TutorialFragment.newInstance(mCurrentTutorialStep);
         getSupportFragmentManager().beginTransaction()
-            .replace(R.id.gesture_tutorial_fragment_container, mFragment)
-            .runOnCommit(() -> mFragment.onAttachedToWindow())
-            .commit();
+                .replace(R.id.gesture_tutorial_fragment_container, mFragment)
+                .runOnCommit(() -> mFragment.onAttachedToWindow())
+                .commit();
         mCurrentStep++;
     }
 
@@ -141,21 +142,33 @@
 
     private TutorialType[] getTutorialSteps(Bundle extras) {
         TutorialType[] defaultSteps = new TutorialType[] {TutorialType.LEFT_EDGE_BACK_NAVIGATION};
+        mCurrentStep = 1;
+        mNumSteps = 1;
 
         if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) {
             return defaultSteps;
         }
 
-        String[] tutorialStepNames = extras.getStringArray(KEY_TUTORIAL_STEPS);
+        Object savedSteps = extras.get(KEY_TUTORIAL_STEPS);
         int currentStep = extras.getInt(KEY_CURRENT_STEP, -1);
+        String[] savedStepsNames;
 
-        if (tutorialStepNames == null) {
+        if (savedSteps instanceof String) {
+            savedStepsNames = TextUtils.isEmpty((String) savedSteps)
+                    ? null : ((String) savedSteps).split(",");
+        } else if (savedSteps instanceof String[]) {
+            savedStepsNames = (String[]) savedSteps;
+        } else {
             return defaultSteps;
         }
 
-        TutorialType[] tutorialSteps = new TutorialType[tutorialStepNames.length];
-        for (int i = 0; i < tutorialStepNames.length; i++) {
-            tutorialSteps[i] = TutorialType.valueOf(tutorialStepNames[i]);
+        if (savedStepsNames == null) {
+            return defaultSteps;
+        }
+
+        TutorialType[] tutorialSteps = new TutorialType[savedStepsNames.length];
+        for (int i = 0; i < savedStepsNames.length; i++) {
+            tutorialSteps[i] = TutorialType.valueOf(savedStepsNames[i]);
         }
 
         mCurrentStep = Math.max(currentStep, 1);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 9c20789..3a445c3 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -404,8 +404,6 @@
 
     private final TaskOverlayFactory mTaskOverlayFactory;
 
-    private int mOrientation;
-
     protected boolean mDisallowScrollToClearAll;
     private boolean mOverlayEnabled;
     protected boolean mFreezeViewVisibility;
@@ -599,7 +597,6 @@
                 .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
         mModel = RecentsModel.INSTANCE.get(context);
         mIdp = InvariantDeviceProfile.INSTANCE.get(context);
-        mOrientation = getResources().getConfiguration().orientation;
 
         mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
                 .inflate(R.layout.overview_clear_all_button, this, false);
@@ -2701,15 +2698,7 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        if (LIVE_TILE.get() && mEnableDrawingLiveTile && newConfig.orientation != mOrientation) {
-            switchToScreenshot(
-                    () -> finishRecentsAnimation(true /* toRecents */, false /* showPip */,
-                            this::updateRecentsRotation));
-            mEnableDrawingLiveTile = false;
-        } else {
-            updateRecentsRotation();
-        }
-        mOrientation = newConfig.orientation;
+        updateRecentsRotation();
     }
 
     /**
@@ -3477,16 +3466,26 @@
             if (onFinishComplete != null) {
                 onFinishComplete.run();
             }
-            // After we finish the recents animation, the current task id should be correctly
-            // reset so that when the task is launched from Overview later, it goes through the
-            // flow of starting a new task instead of finishing recents animation to app. A
-            // typical example of this is (1) user swipes up from app to Overview (2) user
-            // taps on QSB (3) user goes back to Overview and launch the most recent task.
-            setCurrentTask(-1);
-            mRecentsAnimationController = null;
+            onRecentsAnimationComplete();
         }, sendUserLeaveHint);
     }
 
+    /**
+     * Called when a running recents animation has finished or canceled.
+     */
+    public void onRecentsAnimationComplete() {
+        // At this point, the recents animation is not running and if the animation was canceled
+        // by a display rotation then reset this state to show the screenshot
+        setRunningTaskViewShowScreenshot(true);
+        // After we finish the recents animation, the current task id should be correctly
+        // reset so that when the task is launched from Overview later, it goes through the
+        // flow of starting a new task instead of finishing recents animation to app. A
+        // typical example of this is (1) user swipes up from app to Overview (2) user
+        // taps on QSB (3) user goes back to Overview and launch the most recent task.
+        setCurrentTask(-1);
+        mRecentsAnimationController = null;
+    }
+
     public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
         if (mDisallowScrollToClearAll != disallowScrollToClearAll) {
             mDisallowScrollToClearAll = disallowScrollToClearAll;
diff --git a/res/color/drop_target_text.xml b/res/color/drop_target_text.xml
new file mode 100644
index 0000000..18d78e7
--- /dev/null
+++ b/res/color/drop_target_text.xml
@@ -0,0 +1,19 @@
+<?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="?workspaceAccentColor" android:state_selected="false" />
+    <item android:color="?dropTargetHoverTextColor" android:state_selected="true" />
+</selector>
\ No newline at end of file
diff --git a/res/drawable/drop_target_frame.xml b/res/drawable/drop_target_frame.xml
index fa6dafd..666a96e 100644
--- a/res/drawable/drop_target_frame.xml
+++ b/res/drawable/drop_target_frame.xml
@@ -18,5 +18,5 @@
     android:shape="rectangle">
     <solid android:color="@android:color/transparent" />
     <corners android:radius="28dp" />
-    <stroke android:width="2dp" android:color="?android:attr/colorAccent" />
+    <stroke android:width="2dp" android:color="?attr/workspaceAccentColor" />
 </shape>
\ No newline at end of file
diff --git a/res/drawable/drop_target_frame_hover.xml b/res/drawable/drop_target_frame_hover.xml
index 7d0e919..ddf3a4d 100644
--- a/res/drawable/drop_target_frame_hover.xml
+++ b/res/drawable/drop_target_frame_hover.xml
@@ -16,6 +16,6 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
-    <solid android:color="?android:attr/colorAccent" />
+    <solid android:color="?attr/workspaceAccentColor" />
     <corners android:radius="28dp" />
 </shape>
\ No newline at end of file
diff --git a/res/drawable/widget_resize_frame.xml b/res/drawable/widget_resize_frame.xml
index d157f5d..9426de4 100644
--- a/res/drawable/widget_resize_frame.xml
+++ b/res/drawable/widget_resize_frame.xml
@@ -18,5 +18,5 @@
     android:shape="rectangle">
     <solid android:color="@android:color/transparent" />
     <corners android:radius="@android:dimen/system_app_widget_background_radius" />
-    <stroke android:width="2dp" android:color="?android:attr/colorAccent" />
+    <stroke android:width="2dp" android:color="?attr/workspaceAccentColor" />
 </shape>
\ No newline at end of file
diff --git a/res/layout/app_widget_resize_frame.xml b/res/layout/app_widget_resize_frame.xml
index 249e42c..ff07a91 100644
--- a/res/layout/app_widget_resize_frame.xml
+++ b/res/layout/app_widget_resize_frame.xml
@@ -41,7 +41,7 @@
             android:layout_gravity="left|center_vertical"
             android:layout_marginLeft="@dimen/widget_handle_margin"
             android:src="@drawable/ic_widget_resize_handle"
-            android:tint="?android:attr/colorAccent" />
+            android:tint="?attr/workspaceAccentColor" />
 
         <!-- Top -->
         <ImageView
@@ -51,7 +51,7 @@
             android:layout_gravity="top|center_horizontal"
             android:layout_marginTop="@dimen/widget_handle_margin"
             android:src="@drawable/ic_widget_resize_handle"
-            android:tint="?android:attr/colorAccent" />
+            android:tint="?attr/workspaceAccentColor" />
 
         <!-- Right -->
         <ImageView
@@ -61,7 +61,7 @@
             android:layout_gravity="right|center_vertical"
             android:layout_marginRight="@dimen/widget_handle_margin"
             android:src="@drawable/ic_widget_resize_handle"
-            android:tint="?android:attr/colorAccent" />
+            android:tint="?attr/workspaceAccentColor" />
 
         <!-- Bottom -->
         <ImageView
@@ -71,7 +71,7 @@
             android:layout_gravity="bottom|center_horizontal"
             android:layout_marginBottom="@dimen/widget_handle_margin"
             android:src="@drawable/ic_widget_resize_handle"
-            android:tint="?android:attr/colorAccent" />
+            android:tint="?attr/workspaceAccentColor" />
 
         <ImageButton
             android:id="@+id/widget_reconfigure_button"
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index a540ea2..2407e93 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -52,4 +52,6 @@
     <color name="home_settings_track_on_color">@android:color/system_accent1_600</color>
     <color name="home_settings_track_off_color">@android:color/system_neutral2_600</color>
 
+    <color name="workspace_accent_color_light">@android:color/system_accent2_700</color>
+    <color name="workspace_accent_color_dark">@android:color/system_accent1_50</color>
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 1fadc88..ed1db30 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -46,7 +46,8 @@
     <attr name="folderHintColor" format="color" />
     <attr name="isFolderDarkText" format="boolean" />
     <attr name="workProfileOverlayTextColor" format="color" />
-    <attr name="gridColor" format="color" />
+    <attr name="workspaceAccentColor" format="color" />
+    <attr name="dropTargetHoverTextColor" format="color" />
 
     <!-- BubbleTextView specific attributes. -->
     <declare-styleable name="BubbleTextView">
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 01f5364..76e821d 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -73,4 +73,7 @@
 
     <color name="wallpaper_popup_scrim">?android:attr/colorAccent</color>
     <color name="wallpaper_scrim_color">#0D878787</color>
+
+    <color name="workspace_accent_color_light">#ff254e47</color>
+    <color name="workspace_accent_color_dark">#ff9cfff2</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 4e9a16e..9891ff5 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/res/values/styles.xml b/res/values/styles.xml
index 32df25a..1536ec1 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -57,7 +57,8 @@
         <item name="workProfileOverlayTextColor">#FF212121</item>
         <item name="eduHalfSheetBGColor">?android:attr/colorAccent</item>
         <item name="disabledIconAlpha">.54</item>
-        <item name="gridColor">?android:attr/colorAccent</item>
+        <item name="workspaceAccentColor">@color/workspace_accent_color_light</item>
+        <item name="dropTargetHoverTextColor">@color/workspace_text_color_light</item>
         <item name="overviewScrimColor">@color/overview_scrim</item>
 
         <item name="android:windowTranslucentStatus">false</item>
@@ -71,6 +72,8 @@
 
     <style name="LauncherTheme.DarkMainColor" parent="@style/LauncherTheme">
         <item name="disabledIconAlpha">.254</item>
+        <item name="workspaceAccentColor">@color/workspace_accent_color_dark</item>
+        <item name="dropTargetHoverTextColor">@color/workspace_text_color_dark</item>
 
     </style>
 
@@ -250,7 +253,7 @@
     <style name="DropTargetButtonBase" parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:drawablePadding">8dp</item>
         <item name="android:padding">16dp</item>
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textColor">@color/drop_target_text</item>
         <item name="android:textSize">@dimen/drop_target_text_size</item>
         <item name="android:singleLine">true</item>
         <item name="android:ellipsize">end</item>
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 13d5570..0a674b5 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -48,20 +48,6 @@
 public abstract class ButtonDropTarget extends TextView
         implements DropTarget, DragController.DragListener, OnClickListener {
 
-    private static final Property<ButtonDropTarget, Integer> TEXT_COLOR =
-            new Property<ButtonDropTarget, Integer>(Integer.TYPE, "textColor") {
-
-                @Override
-                public Integer get(ButtonDropTarget target) {
-                    return target.getTextColor();
-                }
-
-                @Override
-                public void set(ButtonDropTarget target, Integer value) {
-                    target.setTextColor(value);
-                }
-            };
-
     private static final int[] sTempCords = new int[2];
     private static final int DRAG_VIEW_DROP_DURATION = 285;
     private static final float DRAG_VIEW_HOVER_OVER_OPACITY = 0.65f;
@@ -84,15 +70,12 @@
     private final int mDrawableSize;
 
     protected CharSequence mText;
-    protected ColorStateList mOriginalTextColor;
     protected Drawable mDrawable;
     private boolean mTextVisible = true;
 
     private PopupWindow mToolTip;
     private int mToolTipLocation;
 
-    private AnimatorSet mCurrentColorAnim;
-
     public ButtonDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -110,7 +93,6 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mText = getText();
-        mOriginalTextColor = getTextColors();
         setContentDescription(mText);
     }
 
@@ -125,6 +107,7 @@
         // drawableLeft and drawableStart.
         mDrawable = getContext().getDrawable(resId).mutate();
         mDrawable.setBounds(0, 0, mDrawableSize, mDrawableSize);
+        mDrawable.setTintList(getTextColors());
         setCompoundDrawablesRelative(mDrawable, null, null, null);
     }
 
@@ -191,12 +174,6 @@
     @Override
     public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
         mActive = !options.isKeyboardDrag && supportsDrop(dragObject.dragInfo);
-        mDrawable.setColorFilter(null);
-        if (mCurrentColorAnim != null) {
-            mCurrentColorAnim.cancel();
-            mCurrentColorAnim = null;
-        }
-        setTextColor(mOriginalTextColor);
         setVisibility(mActive ? View.VISIBLE : View.GONE);
 
         mAccessibleDrag = options.isAccessibleDrag;
@@ -314,10 +291,6 @@
         mLauncher.getAccessibilityDelegate().handleAccessibleDrop(this, null, null);
     }
 
-    public int getTextColor() {
-        return getTextColors().getDefaultColor();
-    }
-
     public void setTextVisible(boolean isVisible) {
         CharSequence newText = isVisible ? mText : "";
         if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) {
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 3823437..00278e4 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -263,7 +263,7 @@
         mBackground.setCallback(this);
         mBackground.setAlpha(0);
 
-        mGridColor = Themes.getAttrColor(getContext(), R.attr.gridColor);
+        mGridColor = Themes.getAttrColor(getContext(), R.attr.workspaceAccentColor);
         mGridVisualizationPadding =
                 res.getDimensionPixelSize(R.dimen.grid_visualization_cell_spacing);
         mGridVisualizationRoundingRadius =
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 3c0cbb6..2eba4ed 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -162,6 +162,9 @@
     public final int hotseatBarSidePaddingEndPx;
     public final int hotseatQsbHeight;
 
+    public final float qsbBottomMarginOriginalPx;
+    public int qsbBottomMarginPx;
+
     // All apps
     public int allAppsOpenVerticalTranslate;
     public int allAppsCellHeightPx;
@@ -336,6 +339,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(
@@ -364,6 +371,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
@@ -810,8 +819,13 @@
         int freeSpace = isTaskbarPresent
                 ? workspacePadding.bottom
                 : hotseatBarSizePx - hotseatCellHeightPx - hotseatQsbHeight;
-        return (int) (freeSpace * QSB_CENTER_FACTOR)
+
+        if (isScalableGrid && qsbBottomMarginPx <= freeSpace) {
+            return qsbBottomMarginPx;
+        } else {
+            return (int) (freeSpace * QSB_CENTER_FACTOR)
                 + (isTaskbarPresent ? taskbarSize : getInsets().bottom);
+        }
     }
 
     /**
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