Merge "Load Delegate Items in correct order when loading Launcher Data." into udc-dev
diff --git a/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml b/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
index 43439c6..2887518 100644
--- a/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
@@ -205,15 +205,16 @@
             android:id="@+id/checkmark_animation"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_marginBottom="44dp"
             android:gravity="center"
             android:scaleType="centerCrop"
             app:lottie_loop="false"
             android:visibility="gone"
 
-            app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_subtitle" />
+            app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_subtitle"
+            app:layout_constraintBottom_toBottomOf="parent" />
 
         <Button
             android:id="@+id/gesture_tutorial_fragment_action_button"
@@ -224,6 +225,7 @@
             android:stateListAnimator="@null"
             android:text="@string/gesture_tutorial_action_button_label"
             android:visibility="invisible"
+            android:layout_marginBottom="60dp"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintBottom_toBottomOf="parent"
diff --git a/quickstep/res/layout/taskbar_edu.xml b/quickstep/res/layout/taskbar_edu.xml
index f3856ba..d7daea3 100644
--- a/quickstep/res/layout/taskbar_edu.xml
+++ b/quickstep/res/layout/taskbar_edu.xml
@@ -55,7 +55,7 @@
             style="@style/TaskbarEdu.Button.Close"
             android:textColor="?android:attr/textColorPrimary"/>
 
-        <com.android.launcher3.pageindicators.LauncherDotsPageIndicator
+        <com.android.launcher3.pageindicators.PageIndicatorDots
             android:id="@+id/content_page_indicator"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
@@ -63,7 +63,6 @@
             app:layout_constraintBottom_toBottomOf="@id/edu_start_button"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
-            app:indicatorDotColor="@color/folder_pagination_color"
             android:elevation="1dp" />
 
         <Button
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index d8fd51a..003c2fc 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -47,11 +47,7 @@
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.RemoteActionShortcut;
 import com.android.launcher3.popup.SystemShortcut;
@@ -73,21 +69,14 @@
     private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
     private static final boolean DEBUG = false;
 
-    private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0;
-    private static final int IN_MINIMAL_DEVICE = 2;
-
     // Welbeing contract
     private static final String PATH_ACTIONS = "actions";
-    private static final String PATH_MINIMAL_DEVICE = "minimal_device";
-    private static final String METHOD_GET_MINIMAL_DEVICE_CONFIG = "get_minimal_device_config";
     private static final String METHOD_GET_ACTIONS = "get_actions";
     private static final String EXTRA_ACTIONS = "actions";
     private static final String EXTRA_ACTION = "action";
     private static final String EXTRA_MAX_NUM_ACTIONS_SHOWN = "max_num_actions_shown";
     private static final String EXTRA_PACKAGES = "packages";
     private static final String EXTRA_SUCCESS = "success";
-    private static final String EXTRA_MINIMAL_DEVICE_STATE = "minimal_device_state";
-    private static final String DB_NAME_MINIMAL_DEVICE = "minimal.db";
 
     public static final MainThreadInitializedObject<WellbeingModel> INSTANCE =
             new MainThreadInitializedObject<>(WellbeingModel::new);
@@ -137,36 +126,7 @@
     @WorkerThread
     private void onWellbeingUriChanged(Uri uri) {
         Preconditions.assertNonUiThread();
-        if (DEBUG || mIsInTest) {
-            Log.d(TAG, "ContentObserver.onChange() called with: uri = [" + uri + "]");
-        }
-        if (uri.getPath().contains(PATH_ACTIONS)) {
-            // Wellbeing reports that app actions have changed.
-            updateAllPackages();
-        } else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
-            // Wellbeing reports that minimal device state or config is changed.
-            if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
-                return;
-            }
-
-            // Temporary bug fix for b/169771796. Wellbeing provides the layout configuration when
-            // minimal device is enabled. We always want to reload the configuration from Wellbeing
-            // since the layout configuration might have changed.
-            mContext.deleteDatabase(DB_NAME_MINIMAL_DEVICE);
-
-            final Bundle extras = new Bundle();
-            String dbFile;
-            if (isInMinimalDeviceMode()) {
-                dbFile = DB_NAME_MINIMAL_DEVICE;
-                extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
-                        mWellbeingProviderPkg + ".api");
-            } else {
-                dbFile = InvariantDeviceProfile.INSTANCE.get(mContext).dbFile;
-            }
-            LauncherSettings.Settings.call(mContext.getContentResolver(),
-                    LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
-                    dbFile, extras);
-        }
+        updateAllPackages();
     }
 
     public void setInTest(boolean inTest) {
@@ -178,12 +138,9 @@
         final ContentResolver resolver = mContext.getContentResolver();
         resolver.unregisterContentObserver(mContentObserver);
         Uri actionsUri = apiBuilder().path(PATH_ACTIONS).build();
-        Uri minimalDeviceUri = apiBuilder().path(PATH_MINIMAL_DEVICE).build();
         try {
             resolver.registerContentObserver(
                     actionsUri, true /* notifyForDescendants */, mContentObserver);
-            resolver.registerContentObserver(
-                    minimalDeviceUri, true /* notifyForDescendants */, mContentObserver);
         } catch (Exception e) {
             Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
             if (mIsInTest) throw new RuntimeException(e);
@@ -228,32 +185,6 @@
     }
 
     @WorkerThread
-    private boolean isInMinimalDeviceMode() {
-        if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
-            return false;
-        }
-        if (DEBUG || mIsInTest) {
-            Log.d(TAG, "isInMinimalDeviceMode() called");
-        }
-        Preconditions.assertNonUiThread();
-
-        final Uri contentUri = apiBuilder().build();
-        try (ContentProviderClient client = mContext.getContentResolver()
-                .acquireUnstableContentProviderClient(contentUri)) {
-            final Bundle remoteBundle = client == null ? null : client.call(
-                    METHOD_GET_MINIMAL_DEVICE_CONFIG, null /* args */, null /* extras */);
-            return remoteBundle != null
-                    && remoteBundle.getInt(EXTRA_MINIMAL_DEVICE_STATE,
-                    UNKNOWN_MINIMAL_DEVICE_STATE) == IN_MINIMAL_DEVICE;
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to retrieve data from " + contentUri + ": " + e);
-            if (mIsInTest) throw new RuntimeException(e);
-        }
-        if (DEBUG || mIsInTest) Log.i(TAG, "isInMinimalDeviceMode(): finished");
-        return false;
-    }
-
-    @WorkerThread
     private boolean updateActions(String[] packageNames) {
         if (packageNames.length == 0) {
             return true;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 66a903b..41093bd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -55,7 +55,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragDriver;
 import com.android.launcher3.dragndrop.DragOptions;
@@ -188,12 +187,10 @@
 
         DragOptions dragOptions = new DragOptions();
         dragOptions.preDragCondition = null;
-        if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()) {
-            PopupContainerWithArrow<BaseTaskbarContext> popupContainer =
-                    mControllers.taskbarPopupController.showForIcon(btv);
-            if (popupContainer != null) {
-                dragOptions.preDragCondition = popupContainer.createPreDragCondition(false);
-            }
+        PopupContainerWithArrow<BaseTaskbarContext> popupContainer =
+                mControllers.taskbarPopupController.showForIcon(btv);
+        if (popupContainer != null) {
+            dragOptions.preDragCondition = popupContainer.createPreDragCondition(false);
         }
         if (dragOptions.preDragCondition == null) {
             dragOptions.preDragCondition = new DragOptions.PreDragCondition() {
@@ -208,8 +205,7 @@
                 public void onPreDragStart(DropTarget.DragObject dragObject) {
                     mDragView = dragObject.dragView;
 
-                    if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()
-                            && !shouldStartDrag(0)) {
+                    if (!shouldStartDrag(0)) {
                         mDragView.setOnAnimationEndCallback(() -> {
                             // Drag might be cancelled during the DragView animation, so check
                             // mIsPreDrag again.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
index d0e6386..6cd6512 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
@@ -25,12 +25,12 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
-import com.android.launcher3.pageindicators.LauncherDotsPageIndicator;
+import com.android.launcher3.pageindicators.PageIndicatorDots;
 import com.android.launcher3.taskbar.TaskbarEduController.TaskbarEduCallbacks;
 import com.android.launcher3.views.ActivityContext;
 
 /** Horizontal carousel of tutorial screens for Taskbar Edu. */
-public class TaskbarEduPagedView extends PagedView<LauncherDotsPageIndicator> {
+public class TaskbarEduPagedView extends PagedView<PageIndicatorDots> {
 
     private TaskbarEduView mTaskbarEduView;
     private TaskbarEduCallbacks mControllerCallbacks;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 5fedb3d..fcb2042 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -98,7 +98,8 @@
             windowLayoutParams.providedInsets =
                     arrayOf(
                             InsetsFrameProvider(insetsOwner, 0, navigationBars()),
-                            InsetsFrameProvider(insetsOwner, 0, tappableElement())
+                            InsetsFrameProvider(insetsOwner, 0, tappableElement()),
+                            InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures())
                     )
         }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 5bb958a..610efeb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -27,6 +27,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_IME_SWITCHER_BUTTON_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 
@@ -265,6 +266,7 @@
     }
 
     private void navigateHome() {
+        TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
         mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME);
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
index c10b57a..054689b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
@@ -32,7 +32,6 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.accessibility.BaseAccessibilityDelegate;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -75,7 +74,7 @@
 
     @Override
     protected void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out) {
-        if (ShortcutUtil.supportsShortcuts(item) && FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()) {
+        if (ShortcutUtil.supportsShortcuts(item)) {
             out.add(mActions.get(NotificationListener.getInstanceIfConnected() != null
                     ? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS));
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 5f11740..69ea9fd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -36,6 +36,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -43,6 +44,7 @@
 import android.app.RemoteAction;
 import android.content.SharedPreferences;
 import android.graphics.drawable.Icon;
+import android.os.SystemClock;
 import android.util.Log;
 import android.view.InsetsController;
 import android.view.View;
@@ -187,6 +189,10 @@
     // Auto stashes when user has not interacted with the Taskbar after X ms.
     private static final long NO_TOUCH_TIMEOUT_TO_STASH_MS = 5000;
 
+    // Duration for which an unlock event is considered "current", as other events are received
+    // asynchronously.
+    private static final long UNLOCK_TRANSITION_MEMOIZATION_MS = 200;
+
     /**
      * The default stash animation, morphing the taskbar into the navbar.
      */
@@ -743,56 +749,77 @@
 
     private void createTransientAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration,
             @StashAnimation int animationType) {
-        Interpolator skipInterpolator = null;
+        // Target values of the properties this is going to set
+        final float backgroundOffsetTarget = isStashed ? 1 : 0;
+        final float iconAlphaTarget = isStashed ? 0 : 1;
+        final float stashedHandleAlphaTarget = isStashed ? 1 : 0;
+
+        // Timing for the alpha values depend on the animation played
+        long iconAlphaStartDelay = 0, iconAlphaDuration = 0, stashedHandleAlphaDelay = 0,
+                stashedHandleAlphaDuration = 0;
+        if (duration > 0) {
+            if (animationType == TRANSITION_HANDLE_FADE) {
+                // When fading, the handle fades in/out at the beginning of the transition with
+                // TASKBAR_STASH_ALPHA_DURATION.
+                stashedHandleAlphaDuration = TASKBAR_STASH_ALPHA_DURATION;
+                // The iconAlphaDuration must be set to duration for the skippable interpolators
+                // below to work.
+                iconAlphaDuration = duration;
+            } else {
+                iconAlphaStartDelay = TASKBAR_STASH_ALPHA_START_DELAY;
+                iconAlphaDuration = TASKBAR_STASH_ALPHA_DURATION;
+                stashedHandleAlphaDuration = TASKBAR_STASH_ALPHA_DURATION;
+
+                if (isStashed) {
+                    if (animationType == TRANSITION_HOME_TO_APP) {
+                        iconAlphaStartDelay = TASKBAR_STASH_ICON_ALPHA_HOME_TO_APP_START_DELAY;
+                    }
+                    stashedHandleAlphaDelay = iconAlphaStartDelay;
+                    stashedHandleAlphaDuration = Math.max(0, duration - iconAlphaStartDelay);
+                }
+
+            }
+        }
+
+        play(as, mTaskbarStashedHandleAlpha.animateToValue(stashedHandleAlphaTarget),
+                stashedHandleAlphaDelay,
+                stashedHandleAlphaDuration, LINEAR);
+
+        // The rest of the animations might be "skipped" in TRANSITION_HANDLE_FADE transitions.
+        AnimatorSet skippable = as;
+        if (animationType == TRANSITION_HANDLE_FADE) {
+            skippable = new AnimatorSet();
+            as.play(skippable);
+            skippable.setInterpolator(isStashed ? INSTANT : FINAL_FRAME);
+        }
+
+        final boolean animateBg = animationType != TRANSITION_UNSTASH_SUW_MANUAL;
+        if (animateBg) {
+            play(skippable, mTaskbarBackgroundOffset.animateToValue(backgroundOffsetTarget), 0,
+                    duration, EMPHASIZED);
+        } else {
+            skippable.addListener(AnimatorListeners.forEndCallback(
+                    () -> mTaskbarBackgroundOffset.updateValue(backgroundOffsetTarget)));
+        }
+
+        play(skippable, mIconAlphaForStash.animateToValue(iconAlphaTarget), iconAlphaStartDelay,
+                iconAlphaDuration,
+                LINEAR);
 
         if (isStashed) {
-            play(as, mTaskbarBackgroundOffset.animateToValue(1), 0, duration, EMPHASIZED);
-
-            long alphaStartDelay = duration == 0 ? 0 : animationType == TRANSITION_HOME_TO_APP
-                    ? TASKBAR_STASH_ICON_ALPHA_HOME_TO_APP_START_DELAY
-                    : TASKBAR_STASH_ALPHA_START_DELAY;
-            long alphaDuration = duration == 0 ? 0 : TASKBAR_STASH_ALPHA_DURATION;
-            play(as, mIconAlphaForStash.animateToValue(0), alphaStartDelay, alphaDuration, LINEAR);
-            play(as, mTaskbarStashedHandleAlpha.animateToValue(1), alphaStartDelay,
-                    Math.max(0, duration - alphaStartDelay), LINEAR);
-
-            play(as, mControllers.taskbarSpringOnStashController.createSpringToStash(), 0, duration,
-                    LINEAR);
-
-            if (animationType == TRANSITION_HANDLE_FADE) {
-                skipInterpolator = INSTANT;
-            }
-        } else  {
-            final boolean animateBg = animationType != TRANSITION_UNSTASH_SUW_MANUAL;
-            if (animateBg) {
-                play(as, mTaskbarBackgroundOffset.animateToValue(0), 0, duration, EMPHASIZED);
-            } else {
-                as.addListener(AnimatorListeners.forEndCallback(
-                        () -> mTaskbarBackgroundOffset.updateValue(0)));
-            }
-
-            long alphaStartDelay = duration == 0 ? 0 : TASKBAR_STASH_ALPHA_START_DELAY;
-            long alphaDuration = duration == 0 ? 0 : TASKBAR_STASH_ALPHA_DURATION;
-            play(as, mIconAlphaForStash.animateToValue(1), alphaStartDelay, alphaDuration, LINEAR);
-            play(as, mTaskbarStashedHandleAlpha.animateToValue(0), 0, alphaDuration, LINEAR);
-
-            if (animationType == TRANSITION_HANDLE_FADE) {
-                skipInterpolator = FINAL_FRAME;
-            }
+            play(skippable, mControllers.taskbarSpringOnStashController.createSpringToStash(),
+                    0, duration, LINEAR);
         }
-        mControllers.taskbarViewController.addRevealAnimToIsStashed(as, isStashed, duration,
+
+        mControllers.taskbarViewController.addRevealAnimToIsStashed(skippable, isStashed, duration,
                 EMPHASIZED);
 
-        if (skipInterpolator != null) {
-            as.setInterpolator(skipInterpolator);
-        }
-
-        play(as, mControllers.stashedHandleViewController
+        play(skippable, mControllers.stashedHandleViewController
                 .createRevealAnimToIsStashed(isStashed), 0, duration, EMPHASIZED);
 
         // Return the stashed handle to its default scale in case it was changed as part of the
         // feedforward hint. Note that the reveal animation above also visually scales it.
-        as.play(mTaskbarStashedHandleHintScale.animateToValue(1f)
+        skippable.play(mTaskbarStashedHandleHintScale.animateToValue(1f)
                 .setDuration(isStashed ? duration / 2 : duration));
     }
 
@@ -951,7 +978,8 @@
         updateStateForFlag(FLAG_STASHED_SYSUI,
                 hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING));
 
-        boolean isLocked = hasAnyFlag(systemUiStateFlags, MASK_ANY_SYSUI_LOCKED);
+        boolean isLocked = hasAnyFlag(systemUiStateFlags, MASK_ANY_SYSUI_LOCKED)
+                && !hasAnyFlag(systemUiStateFlags, SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY);
         updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, isLocked);
 
         // Only update FLAG_STASHED_IN_APP_IME when system gesture is not in progress.
@@ -1169,6 +1197,8 @@
         private @StashAnimation int mLastStartedTransitionType = TRANSITION_DEFAULT;
         private int mPrevFlags;
 
+        private long mLastUnlockTransitionTimeout = 0;
+
         StatePropertyHolder(IntPredicate stashCondition) {
             mStashCondition = stashCondition;
         }
@@ -1186,7 +1216,7 @@
 
             if (DEBUG) {
                 String stateString = formatFlagChange(flags, mPrevFlags,
-                            TaskbarStashController::getStateString);
+                        TaskbarStashController::getStateString);
                 Log.d(TAG, "createSetStateAnimator: flags: " + stateString
                         + ", duration: " + duration
                         + ", isStashed: " + isStashed
@@ -1199,6 +1229,17 @@
                 mPrevFlags = flags;
             }
 
+            boolean isUnlockTransition = hasAnyFlag(changedFlags, FLAG_STASHED_DEVICE_LOCKED)
+                    && !hasAnyFlag(FLAG_STASHED_DEVICE_LOCKED);
+            if (isUnlockTransition) {
+                // the launcher might not be resumed at the time the device is considered
+                // unlocked (when the keyguard goes away), but possibly shortly afterwards.
+                // To play the unlock transition at the time the unstash animation actually happens,
+                // this memoizes the state transition for UNLOCK_TRANSITION_MEMOIZATION_MS.
+                mLastUnlockTransitionTimeout =
+                        SystemClock.elapsedRealtime() + UNLOCK_TRANSITION_MEMOIZATION_MS;
+            }
+
             @StashAnimation int animationType = computeTransitionType(changedFlags);
 
             // Allow re-starting animation if upgrading from default animation type, otherwise
@@ -1229,6 +1270,7 @@
         }
 
         private @StashAnimation int computeTransitionType(int changedFlags) {
+
             boolean hotseatHiddenDuringAppLaunch =
                     !mControllers.uiController.isHotseatIconOnTopWhenAligned()
                             && hasAnyFlag(changedFlags, FLAG_IN_APP);
@@ -1240,8 +1282,8 @@
                 return TRANSITION_HANDLE_FADE;
             }
 
-            boolean isUnlockTransition = hasAnyFlag(changedFlags, FLAG_STASHED_DEVICE_LOCKED)
-                    && !hasAnyFlag(FLAG_STASHED_DEVICE_LOCKED);
+            boolean isUnlockTransition =
+                    SystemClock.elapsedRealtime() < mLastUnlockTransitionTimeout;
             if (isUnlockTransition) {
                 // When transitioning to unlocked device, the  hotseat will already be visible on
                 // the homescreen, thus do not play an un-stash animation.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index b9242b2..1435cb0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -244,7 +244,8 @@
                                     taskAttributes.getIconView().getDrawable(),
                                     taskAttributes.getThumbnailView(),
                                     taskAttributes.getThumbnailView().getThumbnail(),
-                                    null /* intent */);
+                                    null /* intent */,
+                                    null /* user */);
                             return;
                         }
                     }
@@ -256,7 +257,8 @@
                             new BitmapDrawable(info.bitmap.icon),
                             startingView,
                             null /* thumbnail */,
-                            intent);
+                            intent,
+                            info.user);
                 }
         );
     }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 88a27b8..edd8823 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -135,6 +135,7 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.wm.shell.common.TransactionPool;
@@ -1345,7 +1346,10 @@
         }
         StatsLogger logger = StatsLogManager.newInstance(mContext).logger()
                 .withSrcState(LAUNCHER_STATE_BACKGROUND)
-                .withDstState(endTarget.containerType);
+                .withDstState(endTarget.containerType)
+                .withInputType(mGestureState.isTrackpadGesture()
+                        ? SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__TRACKPAD
+                        : SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__TOUCH);
         if (targetTask != null) {
             logger.withItemInfo(targetTask.getItemInfo());
         }
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index a68bea2..df42efc 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -86,9 +86,7 @@
             Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding "
                     + convertToFlagNames(stateFlag) + " to " + convertToFlagNames(mState));
         }
-        if (FeatureFlags.ENABLE_GESTURE_ERROR_DETECTION.get()) {
-            trackGestureEvents(stateFlag);
-        }
+        trackGestureEvents(stateFlag);
         final int oldState = mState;
         mState = mState | stateFlag;
 
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index fad4563..93363a0 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -18,6 +18,7 @@
 import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
@@ -29,6 +30,7 @@
 import static com.android.quickstep.GestureState.DEFAULT_STATE;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_MOVE;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
@@ -674,20 +676,24 @@
 
         if (mUncheckedConsumer != InputConsumer.NO_OP) {
             switch (event.getActionMasked()) {
-                case ACTION_DOWN:
-                case ACTION_UP:
+                case ACTION_DOWN, ACTION_UP ->
                     ActiveGestureLog.INSTANCE.addLog(
                             /* event= */ "onMotionEvent(" + (int) event.getRawX() + ", "
                                     + (int) event.getRawY() + "): "
-                                    + MotionEvent.actionToString(event.getActionMasked()),
+                                    + MotionEvent.actionToString(event.getActionMasked()) + ", "
+                                    + MotionEvent.classificationToString(event.getClassification()),
                             /* gestureEvent= */ event.getActionMasked() == ACTION_DOWN
                                     ? MOTION_DOWN
                                     : MOTION_UP);
-                    break;
-                default:
+                case ACTION_MOVE ->
                     ActiveGestureLog.INSTANCE.addLog("onMotionEvent: "
-                            + MotionEvent.actionToString(event.getActionMasked()));
-                    break;
+                            + MotionEvent.actionToString(event.getActionMasked()) + ","
+                            + MotionEvent.classificationToString(event.getClassification())
+                            + ", pointerCount: " + event.getPointerCount(), MOTION_MOVE);
+                default ->
+                    ActiveGestureLog.INSTANCE.addLog("onMotionEvent: "
+                            + MotionEvent.actionToString(event.getActionMasked()) + ","
+                            + MotionEvent.classificationToString(event.getClassification()));
             }
         }
 
@@ -910,10 +916,6 @@
 
     private void logInputConsumerSelectionReason(
             InputConsumer consumer, CompoundString reasonString) {
-        if (!FeatureFlags.ENABLE_INPUT_CONSUMER_REASON_LOGGING.get()) {
-            ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + consumer.getName());
-            return;
-        }
         ActiveGestureLog.INSTANCE.addLog(new CompoundString("setInputConsumer: ")
                 .append(consumer.getName())
                 .append(". reason(s):")
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 4690d94..298d49a 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -227,6 +227,7 @@
         private SliceItem mSliceItem;
         private LauncherAtom.Slice mSlice;
         private Optional<Integer> mCardinality = Optional.empty();
+        private int mInputType = SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__UNKNOWN;
 
         StatsCompatLogger(Context context, ActivityContext activityContext) {
             mContext = context;
@@ -320,6 +321,12 @@
         }
 
         @Override
+        public StatsLogger withInputType(int inputType) {
+            this.mInputType = inputType;
+            return this;
+        }
+
+        @Override
         public void log(EventEnum event) {
             if (!Utilities.ATLEAST_R) {
                 return;
@@ -413,6 +420,7 @@
             InstanceId instanceId = mInstanceId;
             int srcState = mSrcState;
             int dstState = mDstState;
+            int inputType = mInputType;
             if (IS_VERBOSE) {
                 String name = (event instanceof Enum) ? ((Enum) event).name() :
                         event.getId() + "";
@@ -470,7 +478,8 @@
                     cardinality /* cardinality */,
                     getFeatures(atomInfo) /* features */,
                     getSearchAttributes(atomInfo) /* searchAttributes */,
-                    getAttributes(atomInfo) /* attributes */
+                    getAttributes(atomInfo) /* attributes */,
+                    inputType /* input_type */
             );
         }
     }
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
index 2964868..6eadd2b 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -32,11 +32,11 @@
      * Enums associated to gesture navigation events.
      */
     public enum GestureEvent {
-        MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, SET_END_TARGET_NEW_TASK,
-        ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION,
-        CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION,
-        CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED,
-        FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED,
+        MOTION_DOWN, MOTION_UP, MOTION_MOVE, SET_END_TARGET, SET_END_TARGET_HOME,
+        SET_END_TARGET_NEW_TASK, ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION,
+        FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK,
+        CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED,
+        EXPECTING_TASK_APPEARED, FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED,
 
         /**
          * These GestureEvents are specifically associated to state flags that get set in
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
index e05d85c..409bf9c 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
@@ -155,15 +155,13 @@
     }
 
     public void dump(String prefix, PrintWriter writer) {
-        if (FeatureFlags.ENABLE_GESTURE_ERROR_DETECTION.get()) {
-            writer.println(prefix + "ActiveGestureErrorDetector:");
-            for (int i = 0; i < logs.length; i++) {
-                EventLog eventLog = logs[(nextIndex + i) % logs.length];
-                if (eventLog == null) {
-                    continue;
-                }
-                ActiveGestureErrorDetector.analyseAndDump(prefix + '\t', writer, eventLog);
+        writer.println(prefix + "ActiveGestureErrorDetector:");
+        for (int i = 0; i < logs.length; i++) {
+            EventLog eventLog = logs[(nextIndex + i) % logs.length];
+            if (eventLog == null) {
+                continue;
             }
+            ActiveGestureErrorDetector.analyseAndDump(prefix + '\t', writer, eventLog);
         }
 
         writer.println(prefix + "ActiveGestureLog history:");
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index c537ef8..11e1fbd 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -89,11 +89,17 @@
     private final StateManager mStateManager;
     @Nullable
     private DepthController mDepthController;
-    private @StagePosition int mStagePosition;
+    private @StagePosition int mInitialStagePosition;
     private ItemInfo mItemInfo;
+    /** {@link #mInitialTaskIntent} and {@link #mInitialUser} (the user of the Intent) are set
+     * together when split is initiated from an Intent. */
     private Intent mInitialTaskIntent;
+    private UserHandle mInitialUser;
     private int mInitialTaskId = INVALID_TASK_ID;
+    /** {@link #mSecondTaskIntent} and {@link #mSecondUser} (the user of the Intent) are set
+     * together when split is confirmed with an Intent. */
     private Intent mSecondTaskIntent;
+    private UserHandle mSecondUser;
     private int mSecondTaskId = INVALID_TASK_ID;
     private boolean mRecentsAnimationRunning;
     /** If {@code true}, animates the existing task view split placeholder view */
@@ -103,8 +109,6 @@
      * split pair task view without wanting to animate current task dismissal overall
      */
     private boolean mDismissingFromSplitPair;
-    @Nullable
-    private UserHandle mUser;
     /** If not null, this is the TaskView we want to launch from */
     @Nullable
     private GroupedTaskView mLaunchingTaskView;
@@ -138,7 +142,7 @@
             mInitialTaskId = alreadyRunningTask;
         } else {
             mInitialTaskIntent = intent;
-            mUser = itemInfo.user;
+            mInitialUser = itemInfo.user;
         }
 
         setInitialData(stagePosition, splitEvent, itemInfo);
@@ -158,7 +162,7 @@
     private void setInitialData(@StagePosition int stagePosition,
             StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
         mItemInfo = itemInfo;
-        mStagePosition = stagePosition;
+        mInitialStagePosition = stagePosition;
         mSplitEvent = splitEvent;
     }
 
@@ -215,7 +219,7 @@
         Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
                 LogUtils.getShellShareableInstanceId();
         launchTasks(mInitialTaskId, mInitialTaskIntent, mSecondTaskId, mSecondTaskIntent,
-                mStagePosition, callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO,
+                mInitialStagePosition, callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO,
                 instanceIds.first);
 
         mStatsLogManager.logger()
@@ -232,8 +236,14 @@
         mSecondTaskId = task.key.id;
     }
 
-    public void setSecondTask(Intent intent) {
+    /**
+     * To be called as soon as user selects the second app (even if animations aren't complete)
+     * @param intent The second intent that will be launched.
+     * @param user The user of that intent.
+     */
+    public void setSecondTask(Intent intent, UserHandle user) {
         mSecondTaskIntent = intent;
+        mSecondUser = user;
     }
 
     /**
@@ -291,16 +301,17 @@
                         null /* options2 */, stagePosition, splitRatio, remoteTransition,
                         shellInstanceId);
             } else if (intent2 == null) {
-                launchIntentOrShortcut(intent1, options1, taskId2, stagePosition, splitRatio,
-                        remoteTransition, shellInstanceId);
+                launchIntentOrShortcut(intent1, mInitialUser, options1, taskId2, stagePosition,
+                        splitRatio, remoteTransition, shellInstanceId);
             } else if (intent1 == null) {
-                launchIntentOrShortcut(intent2, options1, taskId1,
+                launchIntentOrShortcut(intent2, mSecondUser, options1, taskId1,
                         getOppositeStagePosition(stagePosition), splitRatio, remoteTransition,
                         shellInstanceId);
             } else {
-                mSystemUiProxy.startIntents(getPendingIntent(intent1), options1.toBundle(),
-                        getPendingIntent(intent2), null /* options2 */, stagePosition,
-                        splitRatio, remoteTransition, shellInstanceId);
+                mSystemUiProxy.startIntents(getPendingIntent(intent1, mInitialUser),
+                        options1.toBundle(), getPendingIntent(intent2, mSecondUser),
+                        null /* options2 */, stagePosition, splitRatio, remoteTransition,
+                        shellInstanceId);
             }
         } else {
             final RemoteSplitLaunchAnimationRunner animationRunner =
@@ -314,61 +325,64 @@
                         taskId2, null /* options2 */, stagePosition, splitRatio, adapter,
                         shellInstanceId);
             } else if (intent2 == null) {
-                launchIntentOrShortcutLegacy(intent1, options1, taskId2, stagePosition, splitRatio,
-                        adapter, shellInstanceId);
+                launchIntentOrShortcutLegacy(intent1, mInitialUser, options1, taskId2,
+                        stagePosition, splitRatio, adapter, shellInstanceId);
             } else if (intent1 == null) {
-                launchIntentOrShortcutLegacy(intent2, options1, taskId1,
+                launchIntentOrShortcutLegacy(intent2, mSecondUser, options1, taskId1,
                         getOppositeStagePosition(stagePosition), splitRatio, adapter,
                         shellInstanceId);
             } else {
                 mSystemUiProxy.startIntentsWithLegacyTransition(
-                        getPendingIntent(intent1), getShortcutInfo(intent1), options1.toBundle(),
-                        getPendingIntent(intent2), getShortcutInfo(intent2), null /* options2 */,
-                        stagePosition, splitRatio, adapter, shellInstanceId);
+                        getPendingIntent(intent1, mInitialUser),
+                        getShortcutInfo(intent1, mInitialUser), options1.toBundle(),
+                        getPendingIntent(intent2, mSecondUser),
+                        getShortcutInfo(intent2, mSecondUser), null /* options2 */, stagePosition,
+                        splitRatio, adapter, shellInstanceId);
             }
         }
     }
 
-    private void launchIntentOrShortcut(Intent intent, ActivityOptions options1, int taskId,
-            @StagePosition int stagePosition, float splitRatio, RemoteTransition remoteTransition,
-            @Nullable InstanceId shellInstanceId) {
-        final ShortcutInfo shortcutInfo = getShortcutInfo(intent);
+    private void launchIntentOrShortcut(Intent intent, UserHandle user, ActivityOptions options1,
+            int taskId, @StagePosition int stagePosition, float splitRatio,
+            RemoteTransition remoteTransition, @Nullable InstanceId shellInstanceId) {
+        final ShortcutInfo shortcutInfo = getShortcutInfo(intent, user);
         if (shortcutInfo != null) {
             mSystemUiProxy.startShortcutAndTask(shortcutInfo,
                     options1.toBundle(), taskId, null /* options2 */, stagePosition,
                     splitRatio, remoteTransition, shellInstanceId);
         } else {
-            mSystemUiProxy.startIntentAndTask(getPendingIntent(intent), options1.toBundle(), taskId,
-                    null /* options2 */, stagePosition, splitRatio, remoteTransition,
-                    shellInstanceId);
+            mSystemUiProxy.startIntentAndTask(getPendingIntent(intent, user),
+                    options1.toBundle(), taskId, null /* options2 */, stagePosition, splitRatio,
+                    remoteTransition, shellInstanceId);
         }
     }
 
-    private void launchIntentOrShortcutLegacy(Intent intent, ActivityOptions options1, int taskId,
-            @StagePosition int stagePosition, float splitRatio, RemoteAnimationAdapter adapter,
+    private void launchIntentOrShortcutLegacy(Intent intent, UserHandle user,
+            ActivityOptions options1, int taskId, @StagePosition int stagePosition,
+            float splitRatio, RemoteAnimationAdapter adapter,
             @Nullable InstanceId shellInstanceId) {
-        final ShortcutInfo shortcutInfo = getShortcutInfo(intent);
+        final ShortcutInfo shortcutInfo = getShortcutInfo(intent, user);
         if (shortcutInfo != null) {
             mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
                     options1.toBundle(), taskId, null /* options2 */, stagePosition,
                     splitRatio, adapter, shellInstanceId);
         } else {
-            mSystemUiProxy.startIntentAndTaskWithLegacyTransition(getPendingIntent(intent),
-                    options1.toBundle(), taskId, null /* options2 */, stagePosition, splitRatio,
-                    adapter, shellInstanceId);
+            mSystemUiProxy.startIntentAndTaskWithLegacyTransition(
+                    getPendingIntent(intent, user), options1.toBundle(), taskId,
+                    null /* options2 */, stagePosition, splitRatio, adapter, shellInstanceId);
         }
     }
 
-    private PendingIntent getPendingIntent(Intent intent) {
-        return intent == null ? null : (mUser != null
+    private PendingIntent getPendingIntent(Intent intent, UserHandle user) {
+        return intent == null ? null : (user != null
                 ? PendingIntent.getActivityAsUser(mContext, 0, intent,
-                FLAG_MUTABLE | FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT, null /* options */, mUser)
+                FLAG_MUTABLE | FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT, null /* options */, user)
                 : PendingIntent.getActivity(mContext, 0, intent,
                         FLAG_MUTABLE | FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT));
     }
 
     public @StagePosition int getActiveSplitStagePosition() {
-        return mStagePosition;
+        return mInitialStagePosition;
     }
 
     public StatsLogManager.EventEnum getSplitEvent() {
@@ -380,7 +394,7 @@
     }
 
     @Nullable
-    private ShortcutInfo getShortcutInfo(Intent intent) {
+    private ShortcutInfo getShortcutInfo(Intent intent, UserHandle user) {
         if (intent == null || intent.getPackage() == null) {
             return null;
         }
@@ -392,7 +406,7 @@
 
         try {
             final Context context = mContext.createPackageContextAsUser(
-                    intent.getPackage(), 0 /* flags */, mUser);
+                    intent.getPackage(), 0 /* flags */, user);
             return new ShortcutInfo.Builder(context, shortcutId).build();
         } catch (PackageManager.NameNotFoundException e) {
             Log.w(TAG, "Failed to create a ShortcutInfo for " + intent.getPackage());
@@ -522,7 +536,9 @@
         mInitialTaskIntent = null;
         mSecondTaskId = INVALID_TASK_ID;
         mSecondTaskIntent = null;
-        mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
+        mInitialUser = null;
+        mSecondUser = null;
+        mInitialStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
         mRecentsAnimationRunning = false;
         mLaunchingTaskView = null;
         mItemInfo = null;
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
index e5c74dc..dd10c2d 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.os.UserHandle;
 import android.view.View;
 
 import com.android.launcher3.DeviceProfile;
@@ -66,21 +67,24 @@
         }
         Object tag = view.getTag();
         Intent intent;
+        UserHandle user;
         BitmapInfo bitmapInfo;
         if (tag instanceof WorkspaceItemInfo) {
             final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) tag;
             intent = workspaceItemInfo.intent;
+            user = workspaceItemInfo.user;
             bitmapInfo = workspaceItemInfo.bitmap;
         } else if (tag instanceof com.android.launcher3.model.data.AppInfo) {
             final com.android.launcher3.model.data.AppInfo appInfo =
                     (com.android.launcher3.model.data.AppInfo) tag;
             intent = appInfo.intent;
+            user = appInfo.user;
             bitmapInfo = appInfo.bitmap;
         } else {
             return false;
         }
 
-        mController.setSecondTask(intent);
+        mController.setSecondTask(intent, user);
 
         boolean isTablet = mLauncher.getDeviceProfile().isTablet;
         SplitAnimationTimings timings = AnimUtils.getDeviceSplitToConfirmTimings(isTablet);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 7989bb0..cf6ee2d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -4589,11 +4589,13 @@
      *                   is (either the ThumbnailView or the tapped icon).
      * @param intent If we are launching a fresh instance of the app, this is the Intent for it. If
      *               the second app is already running in Recents, this will be null.
+     * @param user If we are launching a fresh instance of the app, this is the UserHandle for it.
+     *             If the second app is already running in Recents, this will be null.
      * @return true if waiting for confirmation of second app or if split animations are running,
      *          false otherwise
      */
     public boolean confirmSplitSelect(TaskView containerTaskView, Task task, Drawable drawable,
-            View secondView, @Nullable Bitmap thumbnail, Intent intent) {
+            View secondView, @Nullable Bitmap thumbnail, Intent intent, UserHandle user) {
         if (canLaunchFullscreenTask()) {
             return false;
         }
@@ -4609,7 +4611,7 @@
             }
             mSplitSelectStateController.setSecondTask(task);
         } else {
-            mSplitSelectStateController.setSecondTask(intent);
+            mSplitSelectStateController.setSecondTask(intent, user);
         }
 
         RectF secondTaskStartingBounds = new RectF();
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index df90583..42589ce 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -761,7 +761,8 @@
         if (container != null) {
             return getRecentsView().confirmSplitSelect(this, container.getTask(),
                     container.getIconView().getDrawable(), container.getThumbnailView(),
-                    container.getThumbnailView().getThumbnail(), /* intent */ null);
+                    container.getThumbnailView().getThumbnail(), /* intent */ null,
+                    /* user */ null);
         }
         return false;
     }
diff --git a/res/drawable/page_indicator.xml b/res/drawable/page_indicator.xml
new file mode 100644
index 0000000..c0ccc49
--- /dev/null
+++ b/res/drawable/page_indicator.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="?attr/folderPaginationColor"/>
+    <size android:width="@dimen/page_indicator_size" android:height="@dimen/page_indicator_size"/>
+</shape>
\ No newline at end of file
diff --git a/res/layout/page_indicator_dots.xml b/res/layout/page_indicator_dots.xml
index 41844b7..d5fe51e 100644
--- a/res/layout/page_indicator_dots.xml
+++ b/res/layout/page_indicator_dots.xml
@@ -14,13 +14,9 @@
      limitations under the License.
 -->
 
-<com.android.launcher3.pageindicators.LauncherDotsPageIndicator
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+<com.android.launcher3.pageindicators.PageIndicatorDots xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/page_indicator"
     android:layout_width="match_parent"
     android:layout_height="@dimen/workspace_page_indicator_height"
     android:layout_gravity="bottom | center_horizontal"
-    android:theme="@style/HomeScreenElementTheme"
-    launcher:indicatorDotColor="?attr/workspaceTextColor"
-    />
\ No newline at end of file
+    android:theme="@style/HomeScreenElementTheme" />
\ No newline at end of file
diff --git a/res/layout/system_shortcut_content.xml b/res/layout/system_shortcut_content.xml
index ddcef09..3008339 100644
--- a/res/layout/system_shortcut_content.xml
+++ b/res/layout/system_shortcut_content.xml
@@ -22,15 +22,12 @@
         android:id="@+id/bubble_text"
         android:background="?android:attr/selectableItemBackground"
         android:gravity="start|center_vertical"
-        android:paddingTop="@dimen/bg_popup_item_vertical_padding"
-        android:paddingBottom="@dimen/bg_popup_item_vertical_padding"
         android:minHeight="@dimen/bg_popup_item_height"
         android:textAlignment="viewStart"
         android:paddingStart="@dimen/deep_shortcuts_text_padding_start"
         android:paddingEnd="@dimen/popup_padding_end"
         android:textSize="14sp"
-        android:minLines="1"
-        android:maxLines="2"
+        android:lines="1"
         android:ellipsize="end"
         android:hyphenationFrequency="full"
         android:textColor="@color/system_shortcut_text"
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 35ccc5a..5518dc8 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -53,13 +53,12 @@
             android:textColorHighlight="?android:attr/colorControlHighlight"
             android:textColorHint="?attr/folderHintColor"/>
 
-        <com.android.launcher3.pageindicators.LauncherDotsPageIndicator
+        <com.android.launcher3.pageindicators.PageIndicatorDots
             android:id="@+id/folder_page_indicator"
             android:layout_gravity="center_vertical"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:elevation="1dp"
-            launcher:indicatorDotColor="@color/folder_pagination_color"
             />
 
     </LinearLayout>
diff --git a/res/layout/widgets_two_pane_sheet_paged_view.xml b/res/layout/widgets_two_pane_sheet_paged_view.xml
index 5cc2406..d3a8584 100644
--- a/res/layout/widgets_two_pane_sheet_paged_view.xml
+++ b/res/layout/widgets_two_pane_sheet_paged_view.xml
@@ -74,7 +74,9 @@
                 android:id="@+id/suggestions_header"
                 android:layout_marginTop="8dp"
                 android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
-                android:orientation="horizontal">
+                android:orientation="horizontal"
+                android:background="?android:attr/colorBackground"
+                launcher:layout_sticky="true">
             </LinearLayout>
 
             <com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
diff --git a/res/layout/widgets_two_pane_sheet_recyclerview.xml b/res/layout/widgets_two_pane_sheet_recyclerview.xml
index 09cef88..8f2a25e 100644
--- a/res/layout/widgets_two_pane_sheet_recyclerview.xml
+++ b/res/layout/widgets_two_pane_sheet_recyclerview.xml
@@ -59,7 +59,10 @@
                 android:id="@+id/suggestions_header"
                 android:layout_marginTop="8dp"
                 android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
-                android:orientation="horizontal">
+                android:paddingBottom="16dp"
+                android:orientation="horizontal"
+                android:background="?android:attr/colorBackground"
+                launcher:layout_sticky="true">
             </LinearLayout>
         </com.android.launcher3.views.StickyHeaderLayout>
     </FrameLayout>
diff --git a/res/values-night-v31/colors.xml b/res/values-night-v31/colors.xml
index 3b85b88..f331361 100644
--- a/res/values-night-v31/colors.xml
+++ b/res/values-night-v31/colors.xml
@@ -26,6 +26,4 @@
     <color name="home_settings_track_off_color">@android:color/system_neutral1_700</color>
 
     <color name="all_apps_button_color">@android:color/system_neutral2_200</color>
-
-    <color name="folder_pagination_color">@android:color/system_accent2_100</color>
 </resources>
\ No newline at end of file
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index ee27d99..17fe419 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -18,6 +18,4 @@
 
 <resources>
     <color name="all_apps_button_color">#BFC8CC</color>
-    <color name="folder_pagination_color">#ffbfebe3</color>
-
 </resources>
\ No newline at end of file
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 0c87ff4..054fe47 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -41,7 +41,8 @@
     <color name="wallpaper_popup_scrim">@android:color/system_neutral1_900</color>
 
     <color name="folder_dot_color">@android:color/system_accent3_100</color>
-    <color name="folder_pagination_color">@android:color/system_accent1_600</color>
+    <color name="folder_pagination_color_light">@android:color/system_accent1_600</color>
+    <color name="folder_pagination_color_dark">@android:color/system_accent2_100</color>
 
     <color name="home_settings_header_accent">@android:color/system_accent1_600</color>
     <color name="home_settings_header_collapsed">@android:color/system_neutral1_100</color>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 82bab56..417ae61 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -42,6 +42,7 @@
     <attr name="popupNotificationDotColor" format="color" />
 
     <attr name="folderDotColor" format="color" />
+    <attr name="folderPaginationColor" format="color" />
     <attr name="folderPreviewColor" format="color" />
     <attr name="folderBackgroundColor" format="color" />
     <attr name="folderIconRadius" format="float" />
@@ -500,10 +501,4 @@
         <!-- The icon drawable of a widget category. -->
         <attr name="sectionDrawable" format="reference" />
     </declare-styleable>
-
-    <!-- Attributes for PagedIndicator -->
-    <declare-styleable name="PagedIndicator">
-        <!-- Color used to draw dots -->
-        <attr name="indicatorDotColor" format="color" />
-    </declare-styleable>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 1ef918d..8788557 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -67,7 +67,8 @@
     <color name="folder_preview_dark">#464746</color>
 
     <color name="folder_dot_color">?attr/colorPrimary</color>
-    <color name="folder_pagination_color">#ff006c5f</color>
+    <color name="folder_pagination_color_light">#ff006c5f</color>
+    <color name="folder_pagination_color_dark">#ffbfebe3</color>
 
     <color name="text_color_primary_dark">#FFFFFFFF</color>
     <color name="text_color_secondary_dark">#FFFFFFFF</color>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 517bb87..5dc4f0a 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -50,6 +50,7 @@
         <item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme</item>
         <item name="folderDotColor">@color/folder_dot_color</item>
+        <item name="folderPaginationColor">@color/folder_pagination_color_light</item>
         <item name="folderPreviewColor">@color/folder_preview_light</item>
         <item name="folderBackgroundColor">@color/folder_background_light</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
@@ -103,6 +104,7 @@
         <item name="popupShadeThird">@color/popup_shade_third_dark</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
         <item name="folderDotColor">@color/folder_dot_color</item>
+        <item name="folderPaginationColor">@color/folder_pagination_color_dark</item>
         <item name="folderPreviewColor">@color/folder_preview_dark</item>
         <item name="folderBackgroundColor">@color/folder_background_dark</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 2083726..5367d80 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -38,6 +38,7 @@
 import android.util.Xml;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.LauncherProvider.SqlArguments;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -531,6 +532,7 @@
     protected class SearchWidgetParser extends PendingWidgetParser {
         @Override
         @Nullable
+        @WorkerThread
         public ComponentName getComponentName(XmlPullParser parser) {
             return QsbContainerView.getSearchComponentName(mContext);
         }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 53d9281..961c254 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -872,7 +872,9 @@
             if (mIcon instanceof PreloadIconDrawable) {
                 preloadIconDrawable = (PreloadIconDrawable) mIcon;
                 preloadIconDrawable.setLevel(progressLevel);
-                preloadIconDrawable.setIsDisabled(!info.isAppStartable());
+                preloadIconDrawable.setIsDisabled(ENABLE_DOWNLOAD_APP_UX_V2.get()
+                        ? info.getProgressLevel() == 0
+                        : !info.isAppStartable());
             } else {
                 preloadIconDrawable = makePreloadIcon();
                 setIcon(preloadIconDrawable);
@@ -897,8 +899,9 @@
         final PreloadIconDrawable preloadDrawable = newPendingIcon(getContext(), info);
 
         preloadDrawable.setLevel(progressLevel);
-        preloadDrawable.setIsDisabled(!info.isAppStartable());
-
+        preloadDrawable.setIsDisabled(ENABLE_DOWNLOAD_APP_UX_V2.get()
+                ? info.getProgressLevel() == 0
+                : !info.isAppStartable());
         return preloadDrawable;
     }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8fcc08d..4f7380a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -85,6 +85,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
@@ -1355,6 +1356,10 @@
 
         if (SHOW_DOT_PAGINATION.get()) {
             mWorkspace.getPageIndicator().setShouldAutoHide(true);
+            mWorkspace.getPageIndicator().setPaintColor(
+                    Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)
+                            ? Color.BLACK
+                            : Color.WHITE);
         }
     }
 
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index e688709..d1fa764 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -22,7 +22,6 @@
 import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
 
 import android.annotation.TargetApi;
-import android.app.backup.BackupManager;
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -85,7 +84,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Locale;
-import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
@@ -94,7 +92,6 @@
     private static final boolean LOGD = false;
 
     private static final String DOWNGRADE_SCHEMA_FILE = "downgrade_schema.json";
-    private static final long RESTORE_BACKUP_TABLE_DELAY = TimeUnit.SECONDS.toMillis(30);
 
     /**
      * Represents the schema of the database. Changes in scheme need not be backwards compatible.
@@ -114,8 +111,6 @@
     protected DatabaseHelper mOpenHelper;
     protected String mProviderAuthority;
 
-    private long mLastRestoreTimestamp = 0L;
-
     private int mDefaultWorkspaceLayoutOverride = 0;
 
     /**
@@ -374,13 +369,6 @@
                 clearFlagEmptyDbCreated();
                 return null;
             }
-            case LauncherSettings.Settings.METHOD_WAS_EMPTY_DB_CREATED : {
-                Bundle result = new Bundle();
-                result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
-                        LauncherPrefs.getPrefs(getContext()).getBoolean(
-                                mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false));
-                return result;
-            }
             case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: {
                 Bundle result = new Bundle();
                 result.putIntArray(LauncherSettings.Settings.EXTRA_VALUE, deleteEmptyFolders()
@@ -438,25 +426,11 @@
                         new SQLiteTransaction(mOpenHelper.getWritableDatabase()));
                 return result;
             }
-            case LauncherSettings.Settings.METHOD_REFRESH_BACKUP_TABLE: {
-                mOpenHelper.mBackupTableExists = tableExists(mOpenHelper.getReadableDatabase(),
-                        Favorites.BACKUP_TABLE_NAME);
-                return null;
-            }
             case LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE: {
                 mOpenHelper.mHotseatRestoreTableExists = tableExists(
                         mOpenHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
                 return null;
             }
-            case LauncherSettings.Settings.METHOD_RESTORE_BACKUP_TABLE: {
-                final long ts = System.currentTimeMillis();
-                if (ts - mLastRestoreTimestamp > RESTORE_BACKUP_TABLE_DELAY) {
-                    mLastRestoreTimestamp = ts;
-                    RestoreDbTask.restoreIfPossible(
-                            getContext(), mOpenHelper, new BackupManager(getContext()));
-                }
-                return null;
-            }
             case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: {
                 Bundle result = new Bundle();
                 result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
@@ -479,22 +453,6 @@
                                 () -> mOpenHelper));
                 return result;
             }
-            case LauncherSettings.Settings.METHOD_SWITCH_DATABASE: {
-                if (TextUtils.equals(arg, mOpenHelper.getDatabaseName())) return null;
-                final DatabaseHelper helper = mOpenHelper;
-                if (extras == null || !extras.containsKey(KEY_LAYOUT_PROVIDER_AUTHORITY)) {
-                    mProviderAuthority = null;
-                } else {
-                    mProviderAuthority = extras.getString(KEY_LAYOUT_PROVIDER_AUTHORITY);
-                }
-                mOpenHelper = DatabaseHelper.createDatabaseHelper(
-                        getContext(), arg, false /* forMigration */);
-                helper.close();
-                LauncherAppState app = LauncherAppState.getInstanceNoCreate();
-                if (app == null) return null;
-                app.getModel().forceReload();
-                return null;
-            }
         }
         return null;
     }
@@ -666,7 +624,6 @@
         private final Context mContext;
         private final boolean mForMigration;
         private int mMaxItemId = -1;
-        private boolean mBackupTableExists;
         private boolean mHotseatRestoreTableExists;
 
         static DatabaseHelper createDatabaseHelper(Context context, boolean forMigration) {
@@ -727,10 +684,6 @@
         }
 
         protected void onAddOrDeleteOp(SQLiteDatabase db) {
-            if (mBackupTableExists) {
-                dropTable(db, Favorites.BACKUP_TABLE_NAME);
-                mBackupTableExists = false;
-            }
             if (mHotseatRestoreTableExists) {
                 dropTable(db, Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
                 mHotseatRestoreTableExists = false;
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 6e3e96c..1cd2a30 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -380,7 +380,6 @@
                 LauncherProvider.AUTHORITY + "/settings");
 
         public static final String METHOD_CLEAR_EMPTY_DB_FLAG = "clear_empty_db_flag";
-        public static final String METHOD_WAS_EMPTY_DB_CREATED = "get_empty_db_flag";
 
         public static final String METHOD_DELETE_EMPTY_FOLDERS = "delete_empty_folders";
 
@@ -404,18 +403,12 @@
 
         public static final String METHOD_NEW_TRANSACTION = "new_db_transaction";
 
-        public static final String METHOD_REFRESH_BACKUP_TABLE = "refresh_backup_table";
-
         public static final String METHOD_REFRESH_HOTSEAT_RESTORE_TABLE = "restore_hotseat_table";
 
-        public static final String METHOD_RESTORE_BACKUP_TABLE = "restore_backup_table";
-
         public static final String METHOD_UPDATE_CURRENT_OPEN_HELPER = "update_current_open_helper";
 
         public static final String METHOD_PREP_FOR_PREVIEW = "prep_for_preview";
 
-        public static final String METHOD_SWITCH_DATABASE = "switch_database";
-
         public static final String EXTRA_VALUE = "value";
 
         public static final String EXTRA_DB_NAME = "db_name";
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 50ad2be..32c8968 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -71,7 +71,6 @@
         }
 
         InstallSessionHelper packageInstallerCompat = InstallSessionHelper.INSTANCE.get(context);
-        packageInstallerCompat.restoreDbIfApplicable(info);
         if (TextUtils.isEmpty(info.getAppPackageName())
                 || info.getInstallReason() != PackageManager.INSTALL_REASON_USER
                 || packageInstallerCompat.promiseIconAddedForId(info.getSessionId())) {
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 26ad4b5..62e7ef3 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -153,8 +153,8 @@
         Interpolator workspaceFadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
                 pageAlphaProvider.interpolator);
         float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0;
-        mLauncher.getWorkspace().getPageIndicator().setAlpha(
-                propertySetter, workspacePageIndicatorAlpha, workspaceFadeInterpolator);
+        propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
+                workspacePageIndicatorAlpha, workspaceFadeInterpolator);
         Interpolator hotseatFadeInterpolator = config.getInterpolator(ANIM_HOTSEAT_FADE,
                 workspaceFadeInterpolator);
         float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 66417d3..b485780 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -184,7 +184,7 @@
                 this, mActivityContext.getStatsLogManager());
         mAH = Arrays.asList(null, null, null);
         mNavBarScrimPaint = new Paint();
-        mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
+        mNavBarScrimPaint.setColor(Themes.getNavBarScrimColor(mActivityContext));
 
         AllAppsStore.OnUpdateListener onAppsUpdated = this::onAppsUpdated;
         if (TestProtocol.sDebugTracing) {
@@ -831,6 +831,12 @@
             }
         }
         updateBackground(dp);
+
+        int navBarScrimColor = Themes.getNavBarScrimColor(mActivityContext);
+        if (mNavBarScrimPaint.getColor() != navBarScrimColor) {
+            mNavBarScrimPaint.setColor(navBarScrimColor);
+            invalidate();
+        }
     }
 
     protected void updateBackground(DeviceProfile deviceProfile) {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index ead81d6..14cd964 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -69,21 +69,13 @@
 
     /**
      * Feature flag to handle define config changes dynamically instead of killing the process.
-     *
+     * <p>
      *
      * To add a new flag that can be toggled through the flags UI:
-     *
+     * <p>
      * Declare a new ToggleableFlag below. Give it a unique key (e.g. "QSB_ON_FIRST_SCREEN"),
      * and set a default value for the flag. This will be the default value on Debug builds.
      */
-    public static final BooleanFlag ENABLE_INPUT_CONSUMER_REASON_LOGGING = getDebugFlag(270390028,
-            "ENABLE_INPUT_CONSUMER_REASON_LOGGING", ENABLED,
-            "Log the reason why an Input Consumer was selected for a gesture.");
-
-    public static final BooleanFlag ENABLE_GESTURE_ERROR_DETECTION = getDebugFlag(270389990,
-            "ENABLE_GESTURE_ERROR_DETECTION", ENABLED,
-            "Analyze gesture events and log detected errors");
-
     // When enabled the promise icon is visible in all apps while installation an app.
     public static final BooleanFlag PROMISE_APPS_IN_ALL_APPS = getDebugFlag(270390012,
             "PROMISE_APPS_IN_ALL_APPS", DISABLED, "Add promise icon in all-apps");
@@ -139,21 +131,6 @@
             "ASSISTANT_GIVES_LAUNCHER_FOCUS", DISABLED,
             "Allow Launcher to handle nav bar gestures while Assistant is running over it");
 
-    public static final BooleanFlag ENABLE_BULK_WORKSPACE_ICON_LOADING = getDebugFlag(270392203,
-            "ENABLE_BULK_WORKSPACE_ICON_LOADING", ENABLED,
-            "Enable loading workspace icons in bulk.");
-
-    public static final BooleanFlag ENABLE_BULK_ALL_APPS_ICON_LOADING = getDebugFlag(270392465,
-            "ENABLE_BULK_ALL_APPS_ICON_LOADING", ENABLED, "Enable loading all apps icons in bulk.");
-
-    public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(270392706,
-            "ENABLE_DATABASE_RESTORE", DISABLED,
-            "Enable database restore when new restore session is created");
-
-    public static final BooleanFlag ENABLE_SMARTSPACE_DISMISS = getDebugFlag(270391664,
-            "ENABLE_SMARTSPACE_DISMISS", ENABLED,
-            "Adds a menu option to dismiss the current Enhanced Smartspace card.");
-
     public static final BooleanFlag ENABLE_OVERLAY_CONNECTION_OPTIM = getDebugFlag(270392629,
             "ENABLE_OVERLAY_CONNECTION_OPTIM", DISABLED,
             "Enable optimizing overlay service connection");
@@ -173,14 +150,6 @@
             "SEPARATE_RECENTS_ACTIVITY", DISABLED,
             "Uses a separate recents activity instead of using the integrated recents+Launcher UI");
 
-    public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(270392984,
-            "ENABLE_MINIMAL_DEVICE", DISABLED,
-            "Allow user to toggle minimal device mode in launcher.");
-
-    public static final BooleanFlag ENABLE_TASKBAR_POPUP_MENU = getDebugFlag(270392477,
-            "ENABLE_TASKBAR_POPUP_MENU", ENABLED,
-            "Enables long pressing taskbar icons to show the popup menu.");
-
     public static final BooleanFlag ENABLE_TWO_PANEL_HOME = getDebugFlag(270392643,
             "ENABLE_TWO_PANEL_HOME", ENABLED,
             "Uses two panel on home screen. Only applicable on large screen devices.");
@@ -228,7 +197,7 @@
             "Enable initiating split screen from workspace.");
 
     public static final BooleanFlag ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS =
-            getDebugFlag(270394122, "ENABLE_SPLIT_FROM_FULLSCREEN_SHORTCUT", ENABLED,
+            getDebugFlag(270394122, "ENABLE_SPLIT_FROM_FULLSCREEN_SHORTCUT", DISABLED,
             "Enable splitting from fullscreen app with keyboard shortcuts");
 
     public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE = getDebugFlag(
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 9fe3c0b..d43731b 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -45,7 +45,7 @@
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.pageindicators.LauncherDotsPageIndicator;
+import com.android.launcher3.pageindicators.PageIndicatorDots;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
 import com.android.launcher3.util.Thunk;
@@ -60,7 +60,7 @@
 import java.util.function.ToIntFunction;
 import java.util.stream.Collectors;
 
-public class FolderPagedView extends PagedView<LauncherDotsPageIndicator> implements ClipPathView {
+public class FolderPagedView extends PagedView<PageIndicatorDots> implements ClipPathView {
 
     private static final String TAG = "FolderPagedView";
 
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 5a50569..0fe79e7 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -27,16 +27,11 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.PathMeasure;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
-import android.os.SystemClock;
 import android.util.Property;
 
 import com.android.launcher3.R;
@@ -47,10 +42,6 @@
 import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.window.RefreshRateTracker;
-
-import java.util.WeakHashMap;
-import java.util.function.Function;
 
 /**
  * Extension of {@link FastBitmapDrawable} which shows a progress bar around the icon.
@@ -91,14 +82,6 @@
     private static final int PRELOAD_ACCENT_COLOR_INDEX = 0;
     private static final int PRELOAD_BACKGROUND_COLOR_INDEX = 1;
 
-    private static final int ALPHA_DURATION_MILLIS = 3000;
-    private static final int OVERLAY_ALPHA_RANGE = 191;
-    private static final long WAVE_MOTION_DELAY_FACTOR_MILLIS = 100;
-    private static final WeakHashMap<Integer, PorterDuffColorFilter> COLOR_FILTER_MAP =
-            new WeakHashMap<>();
-    public static final Function<Integer, PorterDuffColorFilter> FILTER_FACTORY =
-            currArgb -> new PorterDuffColorFilter(currArgb, PorterDuff.Mode.SRC_ATOP);
-
     private final Matrix mTmpMatrix = new Matrix();
     private final PathMeasure mPathMeasure = new PathMeasure();
 
@@ -119,7 +102,6 @@
     private float mTrackLength;
 
     private boolean mRanFinishAnimation;
-    private final int mRefreshRateMillis;
 
     // Progress of the internal state. [0, 1] indicates the fraction of completed progress,
     // [1, (1 + COMPLETE_ANIM_FRACTION)] indicates the progress of zoom animation.
@@ -138,7 +120,6 @@
                 IconPalette.getPreloadProgressColor(context, info.bitmap.color),
                 getPreloadColors(context),
                 Utilities.isDarkTheme(context),
-                getRefreshRateMillis(context),
                 GraphicsUtils.getShapePath(context, DEFAULT_PATH_SIZE));
     }
 
@@ -147,7 +128,6 @@
             int indicatorColor,
             int[] preloadColors,
             boolean isDarkMode,
-            int refreshRateMillis,
             Path shapePath) {
         super(info.bitmap);
         mItem = info;
@@ -162,13 +142,14 @@
         mSystemAccentColor = preloadColors[PRELOAD_ACCENT_COLOR_INDEX];
         mSystemBackgroundColor = preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX];
         mIsDarkMode = isDarkMode;
-        mRefreshRateMillis = refreshRateMillis;
 
         // If it's a pending app we will animate scale and alpha when it's no longer pending.
         mIconScaleMultiplier.updateValue(info.getProgressLevel() == 0 ? 0 : 1);
 
         setLevel(info.getProgressLevel());
-        setIsStartable(info.isAppStartable());
+        if (!ENABLE_DOWNLOAD_APP_UX_V2.get()) {
+            setIsStartable(info.isAppStartable());
+        }
     }
 
     @Override
@@ -223,20 +204,16 @@
                 : SMALL_SCALE;
         canvas.scale(scale, scale, bounds.exactCenterX(), bounds.exactCenterY());
 
-        ColorFilter filter = getOverlayFilter();
-        mPaint.setColorFilter(filter);
         super.drawInternal(canvas, bounds);
         canvas.restoreToCount(saveCount);
-
-        if (ENABLE_DOWNLOAD_APP_UX_V2.get() && filter != null) {
-            reschedule();
-        }
     }
 
     @Override
     protected void updateFilter() {
         if (!ENABLE_DOWNLOAD_APP_UX_V2.get()) {
             setAlpha(mIsDisabled ? DISABLED_ICON_ALPHA : MAX_PAINT_ALPHA);
+        } else {
+            super.updateFilter();
         }
     }
 
@@ -317,7 +294,7 @@
     /**
      * Sets the internal progress and updates the UI accordingly
      *   for progress <= 0:
-     *     - icon with pending motion
+     *     - icon is pending
      *     - progress track is not visible
      *     - progress bar is not visible
      *   for progress < 1:
@@ -367,11 +344,6 @@
 
         return preloadColors;
     }
-
-    private static int getRefreshRateMillis(Context context) {
-        return RefreshRateTracker.getSingleFrameMs(context);
-    }
-
     /**
      * Returns a FastBitmapDrawable with the icon.
      */
@@ -388,55 +360,9 @@
                 mIndicatorColor,
                 new int[] {mSystemAccentColor, mSystemBackgroundColor},
                 mIsDarkMode,
-                mRefreshRateMillis,
                 mShapePath);
     }
 
-    @Override
-    public boolean setVisible(boolean visible, boolean restart) {
-        if (!visible) {
-            unscheduleSelf(mInvalidateRunnable);
-        }
-        return super.setVisible(visible, restart);
-    }
-
-    private void reschedule() {
-        unscheduleSelf(mInvalidateRunnable);
-        if (!isVisible()) {
-            return;
-        }
-        final long upTime = SystemClock.uptimeMillis();
-        scheduleSelf(mInvalidateRunnable,
-                upTime - ((upTime % mRefreshRateMillis)) + mRefreshRateMillis);
-    }
-
-    /**
-     * Returns a color filter to be used as an overlay on the pending icon with cascading motion
-     * based on its position.
-     */
-    private ColorFilter getOverlayFilter() {
-        if (!ENABLE_DOWNLOAD_APP_UX_V2.get() || mInternalStateProgress > 0) {
-            // If the download has started, we do no need to animate
-            return null;
-        }
-        long waveMotionDelay = (mItem.cellX * WAVE_MOTION_DELAY_FACTOR_MILLIS)
-                + (mItem.cellY * WAVE_MOTION_DELAY_FACTOR_MILLIS);
-        long time = SystemClock.uptimeMillis();
-        int alpha = (int) Utilities.mapBoundToRange(
-                (int) ((time + waveMotionDelay) % ALPHA_DURATION_MILLIS),
-                0,
-                ALPHA_DURATION_MILLIS,
-                0,
-                OVERLAY_ALPHA_RANGE * 2,
-                LINEAR);
-        if (alpha > OVERLAY_ALPHA_RANGE) {
-            alpha = (OVERLAY_ALPHA_RANGE - (alpha % OVERLAY_ALPHA_RANGE));
-        }
-        int overlayColor = mIsDarkMode ? 0 : 255;
-        int currArgb = Color.argb(alpha, overlayColor, overlayColor, overlayColor);
-        return COLOR_FILTER_MAP.computeIfAbsent(currArgb, FILTER_FACTORY);
-    }
-
     protected static class PreloadIconConstantState extends FastBitmapConstantState {
 
         protected final ItemInfoWithIcon mInfo;
@@ -444,7 +370,6 @@
         protected final int[] mPreloadColors;
         protected final boolean mIsDarkMode;
         protected final int mLevel;
-        protected final int mRefreshRateMillis;
         private final Path mShapePath;
 
         public PreloadIconConstantState(
@@ -454,7 +379,6 @@
                 int indicatorColor,
                 int[] preloadColors,
                 boolean isDarkMode,
-                int refreshRateMillis,
                 Path shapePath) {
             super(bitmap, iconColor);
             mInfo = info;
@@ -462,7 +386,6 @@
             mPreloadColors = preloadColors;
             mIsDarkMode = isDarkMode;
             mLevel = info.getProgressLevel();
-            mRefreshRateMillis = refreshRateMillis;
             mShapePath = shapePath;
         }
 
@@ -473,7 +396,6 @@
                     mIndicatorColor,
                     mPreloadColors,
                     mIsDarkMode,
-                    mRefreshRateMillis,
                     mShapePath);
         }
     }
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index cf710da..7f46324 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -770,6 +770,13 @@
         }
 
         /**
+         * Sets the input type of the log message.
+         */
+        default StatsLogger withInputType(int inputType) {
+            return this;
+        }
+
+        /**
          * Builds the final message and logs it as {@link EventEnum}.
          */
         default void log(EventEnum event) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index f8376e8..481cc6e 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -442,7 +442,7 @@
                             tempPackageKey, widgetHelper, pmHelper,
                             iconRequestInfos, unlockedUsers, isSafeMode, allDeepShortcuts);
                 }
-                maybeLoadWorkspaceIconsInBulk(iconRequestInfos);
+                tryLoadWorkspaceIconsInBulk(iconRequestInfos);
             } finally {
                 IOUtils.closeSilently(c);
             }
@@ -622,8 +622,8 @@
                         // Already verified above that user is same as default user
                         info = c.getRestoredItemInfo(intent);
                     } else if (c.itemType == Favorites.ITEM_TYPE_APPLICATION) {
-                        info = c.getAppShortcutInfo(intent, allowMissingTarget, useLowResIcon,
-                                !FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get());
+                        info = c.getAppShortcutInfo(
+                                intent, allowMissingTarget, useLowResIcon, false);
                     } else if (c.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                         ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
                         if (unlockedUsers.get(c.serialNumber)) {
@@ -885,21 +885,19 @@
         }
     }
 
-    private void maybeLoadWorkspaceIconsInBulk(
+    private void tryLoadWorkspaceIconsInBulk(
             List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos) {
-        if (FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()) {
-            Trace.beginSection("LoadWorkspaceIconsInBulk");
-            try {
-                mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
-                for (IconRequestInfo<WorkspaceItemInfo> iconRequestInfo : iconRequestInfos) {
-                    WorkspaceItemInfo wai = iconRequestInfo.itemInfo;
-                    if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
-                        iconRequestInfo.loadWorkspaceIcon(mApp.getContext());
-                    }
+        Trace.beginSection("LoadWorkspaceIconsInBulk");
+        try {
+            mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
+            for (IconRequestInfo<WorkspaceItemInfo> iconRequestInfo : iconRequestInfos) {
+                WorkspaceItemInfo wai = iconRequestInfo.itemInfo;
+                if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
+                    iconRequestInfo.loadWorkspaceIcon(mApp.getContext());
                 }
-            } finally {
-                Trace.endSection();
             }
+        } finally {
+            Trace.endSection();
         }
     }
 
@@ -984,7 +982,7 @@
                 iconRequestInfos.add(new IconRequestInfo<>(
                         appInfo, app, /* useLowResIcon= */ false));
                 mBgAllAppsList.add(
-                        appInfo, app, !FeatureFlags.ENABLE_BULK_ALL_APPS_ICON_LOADING.get());
+                        appInfo, app, false);
             }
             allActivityList.addAll(apps);
         }
@@ -997,7 +995,7 @@
                 AppInfo promiseAppInfo = mBgAllAppsList.addPromiseApp(
                         mApp.getContext(),
                         PackageInstallInfo.fromInstallingState(info),
-                        !FeatureFlags.ENABLE_BULK_ALL_APPS_ICON_LOADING.get());
+                        false);
 
                 if (promiseAppInfo != null) {
                     iconRequestInfos.add(new IconRequestInfo<>(
@@ -1008,15 +1006,13 @@
             }
         }
 
-        if (FeatureFlags.ENABLE_BULK_ALL_APPS_ICON_LOADING.get()) {
-            Trace.beginSection("LoadAllAppsIconsInBulk");
-            try {
-                mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
-                iconRequestInfos.forEach(iconRequestInfo ->
-                        mBgAllAppsList.updateSectionName(iconRequestInfo.itemInfo));
-            } finally {
-                Trace.endSection();
-            }
+        Trace.beginSection("LoadAllAppsIconsInBulk");
+        try {
+            mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
+            iconRequestInfos.forEach(iconRequestInfo ->
+                    mBgAllAppsList.updateSectionName(iconRequestInfo.itemInfo));
+        } finally {
+            Trace.endSection();
         }
 
         mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED,
diff --git a/src/com/android/launcher3/pageindicators/LauncherDotsPageIndicator.java b/src/com/android/launcher3/pageindicators/LauncherDotsPageIndicator.java
deleted file mode 100644
index 8a21d13..0000000
--- a/src/com/android/launcher3/pageindicators/LauncherDotsPageIndicator.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.pageindicators;
-
-import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
-import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
-
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.ViewConfiguration;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.Alarm;
-import com.android.launcher3.Insettable;
-import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.util.MultiValueAlpha;
-
-/**
- * Extension of {@link PageIndicatorDots} with Launcher specific page-indicator functionality
- */
-public class LauncherDotsPageIndicator extends PageIndicatorDots
-        implements Insettable, PageIndicator {
-
-    private static final int PAGINATION_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
-    private static final int PAGINATION_FADE_IN_DURATION = 83;
-    private static final int PAGINATION_FADE_OUT_DURATION = 167;
-
-    private static final int INDEX_VIEW_ALPHA = 0;
-    private static final int INDEX_AUTO_HIDE = 1;
-    private static final int ALPHA_CHANNEL_COUNT = 2;
-
-    private final Alarm mAutoHideAlarm;
-    private final MultiValueAlpha mMultiValueAlpha;
-
-    private @Nullable ObjectAnimator mAlphaAnimator;
-    private boolean mShouldAutoHide;
-    private float mTargetAutoHideAlpha;
-
-    private boolean mIsSettled = true;
-    private int mTotalScroll;
-
-    public LauncherDotsPageIndicator(Context context) {
-        this(context, null);
-    }
-
-    public LauncherDotsPageIndicator(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public LauncherDotsPageIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
-        mMultiValueAlpha.setUpdateVisibility(true);
-
-        mTargetAutoHideAlpha = mMultiValueAlpha.get(INDEX_AUTO_HIDE).getValue();
-
-        mAutoHideAlarm = new Alarm();
-        mAutoHideAlarm.setOnAlarmListener(a -> animatePaginationToAlpha(0));
-    }
-
-    @Override
-    public void setScroll(int currentScroll, int totalScroll) {
-        mTotalScroll = totalScroll;
-        super.setScroll(currentScroll, totalScroll);
-    }
-
-    @Override
-    public void setShouldAutoHide(boolean shouldAutoHide) {
-        mShouldAutoHide = shouldAutoHide;
-        mAutoHideAlarm.cancelAlarm();
-        if (!mIsSettled || !mShouldAutoHide) {
-            animatePaginationToAlpha(1);
-        } else {
-            mAutoHideAlarm.setAlarm(PAGINATION_FADE_DELAY);
-        }
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (mShouldAutoHide && mTotalScroll == 0) {
-            return;
-        }
-        super.onDraw(canvas);
-    }
-
-    @Override
-    public void setActiveMarker(int activePage) {
-        super.setActiveMarker(activePage);
-    }
-
-    @Override
-    public void setMarkersCount(int numMarkers) {
-        super.setMarkersCount(numMarkers);
-    }
-
-    @Override
-    public void pauseAnimations() {
-        if (mAlphaAnimator != null) {
-            mAlphaAnimator.pause();
-        }
-    }
-
-    @Override
-    public void skipAnimationsToEnd() {
-        if (mAlphaAnimator != null) {
-            mAlphaAnimator.end();
-        }
-    }
-
-    @Override
-    protected void onAnimationStateChanged(boolean isSettled) {
-        mIsSettled = isSettled;
-        if (!mShouldAutoHide) {
-            return;
-        }
-        mAutoHideAlarm.cancelAlarm();
-        if (isSettled) {
-            mAutoHideAlarm.setAlarm(PAGINATION_FADE_DELAY);
-        } else {
-            animatePaginationToAlpha(1f);
-        }
-    }
-
-    private void animatePaginationToAlpha(float targetAlpha) {
-        if (mTargetAutoHideAlpha == targetAlpha) {
-            // Ignore the new animation if it is going to the same alpha as the current animation.
-            return;
-        }
-
-        if (mAlphaAnimator != null) {
-            mAlphaAnimator.cancel();
-        }
-        mAlphaAnimator = ObjectAnimator.ofFloat(mMultiValueAlpha.get(INDEX_AUTO_HIDE),
-                MULTI_PROPERTY_VALUE, targetAlpha);
-        // If we are animating to decrease the alpha, then it's a fade out animation
-        // whereas if we are animating to increase the alpha, it's a fade in animation.
-        mAlphaAnimator.setDuration(targetAlpha == 0
-                ? PAGINATION_FADE_OUT_DURATION
-                : PAGINATION_FADE_IN_DURATION);
-        mAlphaAnimator.addListener(forEndCallback(() -> mAlphaAnimator = null));
-        mAlphaAnimator.start();
-        mTargetAutoHideAlpha = targetAlpha;
-    }
-
-
-    @Override
-    public void stopAllAnimations() {
-        super.stopAllAnimations();
-    }
-
-    @Override
-    public void prepareEntryAnimation() {
-        super.prepareEntryAnimation();
-    }
-
-    @Override
-    public void playEntryAnimation() {
-        super.playEntryAnimation();
-    }
-
-    /**
-     * We need to override setInsets to prevent InsettableFrameLayout from applying different
-     * margins on the pagination.
-     */
-    @Override
-    public void setInsets(Rect insets) {
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    @Override
-    public void setAlpha(PropertySetter setter, float alpha, TimeInterpolator interpolator) {
-        setter.setFloat(mMultiValueAlpha.get(INDEX_VIEW_ALPHA),
-                MULTI_PROPERTY_VALUE, alpha, interpolator);
-    }
-}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index 193f50d..570d6ff 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -15,11 +15,6 @@
  */
 package com.android.launcher3.pageindicators;
 
-import android.animation.TimeInterpolator;
-import android.view.View;
-
-import com.android.launcher3.anim.PropertySetter;
-
 /**
  * Base class for a page indicator.
  */
@@ -53,9 +48,9 @@
     }
 
     /**
-     * Sets the provided alpha on the pageIndicator
+     * Sets the paint color.
      */
-    default void setAlpha(PropertySetter setter, float alpha, TimeInterpolator interpolator) {
-        setter.setViewAlpha((View) this, alpha, interpolator);
+    default void setPaintColor(int color) {
+        // No-op by default
     }
 }
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 95452b9..b2c64b3 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -25,30 +25,42 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.Paint.Style;
+import android.graphics.Rect;
 import android.graphics.RectF;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
+import android.util.IntProperty;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewOutlineProvider;
 import android.view.animation.Interpolator;
 import android.view.animation.OvershootInterpolator;
 
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.Themes;
 
 /**
  * {@link PageIndicator} which shows dots per page. The active page is shown with the current
  * accent color.
  */
-public class PageIndicatorDots extends View {
+public class PageIndicatorDots extends View implements Insettable, PageIndicator {
 
     private static final float SHIFT_PER_ANIMATION = 0.5f;
     private static final float SHIFT_THRESHOLD = 0.1f;
     private static final long ANIMATION_DURATION = 150;
+    private static final int PAGINATION_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
+    private static final int PAGINATION_FADE_IN_DURATION = 83;
+    private static final int PAGINATION_FADE_OUT_DURATION = 167;
 
     private static final int ENTER_ANIMATION_START_DELAY = 300;
     private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150;
@@ -58,6 +70,9 @@
     private static final int DOT_ALPHA = 128;
     private static final float DOT_ALPHA_FRACTION = 0.5f;
     private static final int DOT_GAP_FACTOR = SHOW_DOT_PAGINATION.get() ? 4 : 3;
+    private static final int VISIBLE_ALPHA = 255;
+    private static final int INVISIBLE_ALPHA = 0;
+    private Paint mPaginationPaint;
 
     // This value approximately overshoots to 1.5 times the original size.
     private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f;
@@ -79,14 +94,30 @@
                 }
     };
 
+    private static final IntProperty<PageIndicatorDots> PAGINATION_ALPHA =
+            new IntProperty<PageIndicatorDots>("pagination_alpha") {
+        @Override
+        public Integer get(PageIndicatorDots obj) {
+            return obj.mPaginationPaint.getAlpha();
+        }
+
+        @Override
+        public void setValue(PageIndicatorDots obj, int alpha) {
+            obj.mPaginationPaint.setAlpha(alpha);
+            obj.invalidate();
+        }
+    };
+
+    private final Handler mDelayedPaginationFadeHandler = new Handler(Looper.getMainLooper());
     private final float mDotRadius;
     private final float mCircleGap;
     private final boolean mIsRtl;
 
-    private final Paint mPaginationPaint;
-
     private int mNumPages;
     private int mActivePage;
+    private int mTotalScroll;
+    private boolean mShouldAutoHide;
+    private int mToAlpha;
 
     /**
      * The current position of the active dot including the animation progress.
@@ -100,9 +131,13 @@
     private float mCurrentPosition;
     private float mFinalPosition;
     private ObjectAnimator mAnimator;
+    private @Nullable ObjectAnimator mAlphaAnimator;
 
     private float[] mEntryAnimationRadiusFactors;
 
+    private final Runnable mHidePaginationRunnable =
+            () -> animatePaginationToAlpha(INVISIBLE_ALPHA);
+
     public PageIndicatorDots(Context context) {
         this(context, null);
     }
@@ -116,34 +151,37 @@
 
         mPaginationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mPaginationPaint.setStyle(Style.FILL);
-
-        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PagedIndicator);
-        mPaginationPaint.setColor(ta.getColor(R.styleable.PagedIndicator_indicatorDotColor, 0));
-        ta.recycle();
-
+        mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
         mDotRadius = (SHOW_DOT_PAGINATION.get()
                 ? getResources().getDimension(R.dimen.page_indicator_dot_size_v2)
                 : getResources().getDimension(R.dimen.page_indicator_dot_size))
                 / 2;
         mCircleGap = DOT_GAP_FACTOR * mDotRadius;
         setOutlineProvider(new MyOutlineProver());
-        mIsRtl = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        mIsRtl = Utilities.isRtl(getResources());
     }
 
+    @Override
     public void setScroll(int currentScroll, int totalScroll) {
         if (SHOW_DOT_PAGINATION.get() && mActivePage != 0 && currentScroll == 0) {
             CURRENT_POSITION.set(this, (float) mActivePage);
             return;
         }
 
-        if (mNumPages <= 1 || totalScroll == 0) {
+        if (mNumPages <= 1) {
             return;
         }
 
+        if (mShouldAutoHide) {
+            animatePaginationToAlpha(VISIBLE_ALPHA);
+        }
+
         if (mIsRtl) {
             currentScroll = totalScroll - currentScroll;
         }
 
+        mTotalScroll = totalScroll;
+
         int scrollPerPage = totalScroll / (mNumPages - 1);
         int pageToLeft = scrollPerPage == 0 ? 0 : currentScroll / scrollPerPage;
         int pageToLeftScroll = pageToLeft * scrollPerPage;
@@ -153,12 +191,87 @@
         if (currentScroll < pageToLeftScroll + scrollThreshold) {
             // scroll is within the left page's threshold
             animateToPosition(pageToLeft);
+            if (mShouldAutoHide) {
+                hideAfterDelay();
+            }
         } else if (currentScroll > pageToRightScroll - scrollThreshold) {
             // scroll is far enough from left page to go to the right page
             animateToPosition(pageToLeft + 1);
+            if (mShouldAutoHide) {
+                hideAfterDelay();
+            }
         } else {
             // scroll is between left and right page
             animateToPosition(pageToLeft + SHIFT_PER_ANIMATION);
+            if (mShouldAutoHide) {
+                mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
+            }
+        }
+    }
+
+    @Override
+    public void setShouldAutoHide(boolean shouldAutoHide) {
+        mShouldAutoHide = shouldAutoHide && SHOW_DOT_PAGINATION.get();
+        if (shouldAutoHide && mPaginationPaint.getAlpha() > INVISIBLE_ALPHA) {
+            hideAfterDelay();
+        } else if (!shouldAutoHide) {
+            mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
+        }
+    }
+
+    @Override
+    public void setPaintColor(int color) {
+        mPaginationPaint.setColor(color);
+    }
+
+    private void hideAfterDelay() {
+        mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
+        mDelayedPaginationFadeHandler.postDelayed(mHidePaginationRunnable, PAGINATION_FADE_DELAY);
+    }
+
+    private void animatePaginationToAlpha(int alpha) {
+        if (alpha == mToAlpha) {
+            // Ignore the new animation if it is going to the same alpha as the current animation.
+            return;
+        }
+
+        if (mAlphaAnimator != null) {
+            mAlphaAnimator.cancel();
+        }
+        mAlphaAnimator = ObjectAnimator.ofInt(this, PAGINATION_ALPHA,
+                alpha);
+        // If we are animating to decrease the alpha, then it's a fade out animation
+        // whereas if we are animating to increase the alpha, it's a fade in animation.
+        mAlphaAnimator.setDuration(alpha < mToAlpha
+                ? PAGINATION_FADE_OUT_DURATION
+                : PAGINATION_FADE_IN_DURATION);
+        mAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAlphaAnimator = null;
+            }
+        });
+        mAlphaAnimator.start();
+        mToAlpha = alpha;
+    }
+
+    /**
+     * Pauses all currently running animations.
+     */
+    @Override
+    public void pauseAnimations() {
+        if (mAlphaAnimator != null) {
+            mAlphaAnimator.pause();
+        }
+    }
+
+    /**
+     * Force-ends all currently running or paused animations.
+     */
+    @Override
+    public void skipAnimationsToEnd() {
+        if (mAlphaAnimator != null) {
+            mAlphaAnimator.end();
         }
     }
 
@@ -168,25 +281,15 @@
             mCurrentPosition = mFinalPosition;
         }
         if (mAnimator == null && Float.compare(mCurrentPosition, mFinalPosition) != 0) {
-            onAnimationStateChanged(false);
             float positionForThisAnim = mCurrentPosition > mFinalPosition ?
                     mCurrentPosition - SHIFT_PER_ANIMATION : mCurrentPosition + SHIFT_PER_ANIMATION;
             mAnimator = ObjectAnimator.ofFloat(this, CURRENT_POSITION, positionForThisAnim);
             mAnimator.addListener(new AnimationCycleListener());
             mAnimator.setDuration(ANIMATION_DURATION);
             mAnimator.start();
-        } else if (mAnimator == null) {
-            // The state is only settled if the indicator lands on a int value
-            onAnimationStateChanged(Float.compare(Math.round(mFinalPosition), mFinalPosition) == 0);
         }
     }
 
-    /**
-     * Called when the animation state of the page indicator changes.
-     * @param isSettled true if the page indicator has settled at its final position
-     */
-    protected void onAnimationStateChanged(boolean isSettled) { }
-
     public void stopAllAnimations() {
         if (mAnimator != null) {
             mAnimator.cancel();
@@ -242,10 +345,14 @@
         animSet.start();
     }
 
+    @Override
     public void setActiveMarker(int activePage) {
-        mActivePage = activePage;
+        if (mActivePage != activePage) {
+            mActivePage = activePage;
+        }
     }
 
+    @Override
     public void setMarkersCount(int numMarkers) {
         mNumPages = numMarkers;
         requestLayout();
@@ -267,6 +374,11 @@
             return;
         }
 
+        if (mShouldAutoHide && mTotalScroll == 0) {
+            mPaginationPaint.setAlpha(INVISIBLE_ALPHA);
+            return;
+        }
+
         // Draw all page indicators;
         float circleGap = mCircleGap;
         float startX = (getWidth() - (mNumPages * circleGap) + mDotRadius) / 2;
@@ -368,9 +480,20 @@
         @Override
         public void onAnimationEnd(Animator animation) {
             if (!mCancelled) {
+                if (mShouldAutoHide && SHOW_DOT_PAGINATION.get()) {
+                    hideAfterDelay();
+                }
                 mAnimator = null;
                 animateToPosition(mFinalPosition);
             }
         }
     }
+
+    /**
+     * We need to override setInsets to prevent InsettableFrameLayout from applying different
+     * margins on the pagination.
+     */
+    @Override
+    public void setInsets(Rect insets) {
+    }
 }
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 125b4ce..47bfe85 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -29,14 +29,11 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.LauncherPrefs;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.SessionCommitReceiver;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.testing.shared.TestProtocol;
@@ -204,26 +201,6 @@
         return list;
     }
 
-    /**
-     * Attempt to restore workspace layout if the session is triggered due to device restore.
-     */
-    public boolean restoreDbIfApplicable(@NonNull final SessionInfo info) {
-        if (!FeatureFlags.ENABLE_DATABASE_RESTORE.get()) {
-            return false;
-        }
-        if (isRestore(info)) {
-            LauncherSettings.Settings.call(mAppContext.getContentResolver(),
-                    LauncherSettings.Settings.METHOD_RESTORE_BACKUP_TABLE);
-            return true;
-        }
-        return false;
-    }
-
-    @RequiresApi(26)
-    private static boolean isRestore(@NonNull final SessionInfo info) {
-        return info.getInstallReason() == PackageManager.INSTALL_REASON_DEVICE_RESTORE;
-    }
-
     @WorkerThread
     public boolean promiseIconAddedForId(final int sessionId) {
         return getPromiseIconIds().contains(sessionId);
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 0edf292..5b493c2 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -109,7 +109,7 @@
     protected final int mArrowPointRadius;
     protected final View mArrow;
 
-    private final int mMargin;
+    protected final int mChildContainerMargin;
 
     protected boolean mIsLeftAligned;
     protected boolean mIsAboveIcon;
@@ -145,7 +145,7 @@
         // Initialize arrow view
         final Resources resources = getResources();
         mArrowColor = getColorStateList(getContext(), R.color.popup_shade_first).getDefaultColor();
-        mMargin = resources.getDimensionPixelSize(R.dimen.popup_margin);
+        mChildContainerMargin = resources.getDimensionPixelSize(R.dimen.popup_margin);
         mArrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
         mArrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
         mArrow = new View(context);
@@ -249,7 +249,7 @@
             if (view.getVisibility() == VISIBLE) {
                 if (lastView != null) {
                     MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
-                    mlp.bottomMargin = mMargin;
+                    mlp.bottomMargin = mChildContainerMargin;
                 }
                 lastView = view;
                 MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
@@ -441,7 +441,7 @@
                 numVisibleChildren++;
             }
         }
-        int childMargins = (numVisibleChildren - 1) * mMargin;
+        int childMargins = (numVisibleChildren - 1) * mChildContainerMargin;
         int height = getMeasuredHeight() + extraVerticalSpace + childMargins;
         int width = getMeasuredWidth() + getPaddingLeft() + getPaddingRight();
 
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 3f75ecc..9cca29a 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -95,6 +95,8 @@
 
     private static final int SHORTCUT_COLLAPSE_THRESHOLD = 6;
 
+    private final float mShortcutHeight;
+
     private BubbleTextView mOriginalIcon;
     private int mNumNotifications;
     private NotificationContainer mNotificationContainer;
@@ -112,6 +114,7 @@
         mStartDragThreshold = getResources().getDimensionPixelSize(
                 R.dimen.deep_shortcuts_start_drag_threshold);
         mContainerWidth = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_width);
+        mShortcutHeight = getResources().getDimension(R.dimen.system_shortcut_header_height);
     }
 
     public PopupContainerWithArrow(Context context, AttributeSet attrs) {
@@ -387,16 +390,18 @@
      */
     private void addAllShortcutsMaterialU(int deepShortcutCount,
             List<SystemShortcut> systemShortcuts) {
-
         if (deepShortcutCount + systemShortcuts.size() <= SHORTCUT_COLLAPSE_THRESHOLD) {
             // add all system shortcuts including widgets shortcut to same container
             addSystemShortcutsMaterialU(systemShortcuts,
                     R.layout.system_shortcut_rows_container_material_u,
                     R.layout.system_shortcut);
-            addDeepShortcutsMaterialU(deepShortcutCount);
+            float currentHeight = (mShortcutHeight * systemShortcuts.size())
+                    + mChildContainerMargin;
+            addDeepShortcutsMaterialU(deepShortcutCount, currentHeight);
             return;
         }
 
+        float currentHeight = mShortcutHeight + mChildContainerMargin;
         List<SystemShortcut> nonWidgetSystemShortcuts =
                 getNonWidgetSystemShortcuts(systemShortcuts);
         // If total shortcuts over threshold, collapse system shortcuts to single row
@@ -411,8 +416,9 @@
             mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container_material_u,
                     this);
             initializeWidgetShortcut(mWidgetContainer, widgetShortcutOpt.get());
+            currentHeight += mShortcutHeight + mChildContainerMargin;
         }
-        addDeepShortcutsMaterialU(deepShortcutCount);
+        addDeepShortcutsMaterialU(deepShortcutCount, currentHeight);
     }
 
     /**
@@ -497,10 +503,14 @@
     /**
      * Inflates and adds [deepShortcutCount] number of DeepShortcutView for the  to a new container
      * @param deepShortcutCount number of DeepShortcutView instances to add
+     * @param currentHeight height of popup before adding deep shortcuts
      */
-    private void addDeepShortcutsMaterialU(int deepShortcutCount) {
+    private void addDeepShortcutsMaterialU(int deepShortcutCount, float currentHeight) {
         mDeepShortcutContainer = inflateAndAdd(R.layout.deep_shortcut_container, this);
         for (int i = deepShortcutCount; i > 0; i--) {
+            currentHeight += mShortcutHeight;
+            // when there is limited vertical screen space, limit total popup rows to fit
+            if (currentHeight >= mActivityContext.getDeviceProfile().availableHeightPx) break;
             DeepShortcutView v = inflateAndAdd(R.layout.deep_shortcut_material_u,
                     mDeepShortcutContainer);
             v.getLayoutParams().width = mContainerWidth;
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index f295204..1e3be27 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -40,6 +40,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
@@ -65,6 +66,7 @@
      * @param context
      * @return String
      */
+    @WorkerThread
     @Nullable
     public static String getSearchWidgetPackageName(@NonNull Context context) {
         String providerPkg = Settings.Global.getString(context.getContentResolver(),
@@ -84,6 +86,7 @@
      * @param context
      * @return AppWidgetProviderInfo
      */
+    @WorkerThread
     @Nullable
     public static AppWidgetProviderInfo getSearchWidgetProviderInfo(@NonNull Context context) {
         String providerPkg = getSearchWidgetPackageName(context);
@@ -110,6 +113,7 @@
     /**
      * returns componentName for searchWidget if package name is known.
      */
+    @WorkerThread
     @Nullable
     public static ComponentName getSearchComponentName(@NonNull  Context context) {
         AppWidgetProviderInfo providerInfo =
@@ -317,6 +321,7 @@
          * If widgetCategory is not supported, or no such widget is found, returns the first widget
          * provided by the package.
          */
+        @WorkerThread
         protected AppWidgetProviderInfo getSearchWidgetProvider() {
             return getSearchWidgetProviderInfo(getContext());
         }
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 5526839..a5c663f 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -32,10 +32,13 @@
 import android.util.SparseArray;
 import android.util.TypedValue;
 
+import androidx.annotation.ColorInt;
+
 import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.icons.GraphicsUtils;
+import com.android.launcher3.views.ActivityContext;
 
 /**
  * Various utility methods associated with theming.
@@ -198,4 +201,12 @@
 
         return result;
     }
+
+    /** Returns the desired navigation bar scrim color depending on the {@code DeviceProfile}. */
+    @ColorInt
+    public static <T extends Context & ActivityContext> int getNavBarScrimColor(T context) {
+        return context.getDeviceProfile().isTaskbarPresent
+                ? context.getColor(R.color.taskbar_background)
+                : Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor);
+    }
 }
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 07c6ba4..c3f26fa 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -37,6 +37,7 @@
 import androidx.core.view.ViewCompat;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.Insettable;
@@ -60,7 +61,7 @@
  */
 public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
         implements OnClickListener, OnLongClickListener, DragSource,
-        PopupDataProvider.PopupDataChangeListener, Insettable {
+        PopupDataProvider.PopupDataChangeListener, Insettable, OnDeviceProfileChangeListener {
     /** The default number of cells that can fit horizontally in a widget sheet. */
     public static final int DEFAULT_MAX_HORIZONTAL_SPANS = 4;
 
@@ -84,7 +85,7 @@
         mWidgetCellHorizontalPadding = getResources().getDimensionPixelSize(
                 R.dimen.widget_cell_horizontal_padding);
         mNavBarScrimPaint = new Paint();
-        mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
+        mNavBarScrimPaint.setColor(Themes.getNavBarScrimColor(mActivityContext));
     }
 
     protected int getScrimColor(Context context) {
@@ -98,12 +99,23 @@
                 .normalizeWindowInsets(getContext(), getRootWindowInsets(), new Rect());
         mNavBarScrimHeight = getNavBarScrimHeight(windowInsets);
         mActivityContext.getPopupDataProvider().setChangeListener(this);
+        mActivityContext.addOnDeviceProfileChangeListener(this);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mActivityContext.getPopupDataProvider().setChangeListener(null);
+        mActivityContext.removeOnDeviceProfileChangeListener(this);
+    }
+
+    @Override
+    public void onDeviceProfileChanged(DeviceProfile dp) {
+        int navBarScrimColor = Themes.getNavBarScrimColor(mActivityContext);
+        if (mNavBarScrimPaint.getColor() != navBarScrimColor) {
+            mNavBarScrimPaint.setColor(navBarScrimColor);
+            invalidate();
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 9984991..80bc1a7 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.widget;
 
-import static android.view.View.MeasureSpec.getSize;
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -119,8 +118,6 @@
     private TextView mWidgetName;
     private TextView mWidgetDims;
     private TextView mWidgetDescription;
-    private Consumer<Bitmap> mCallback;
-    private @Nullable Bitmap mCachedPreview;
 
     protected WidgetItem mItem;
 
@@ -432,8 +429,6 @@
      */
     private void ensurePreviewWithCallback(Consumer<Bitmap> callback,
             @Nullable Bitmap cachedPreview) {
-        mCallback = callback;
-        mCachedPreview = cachedPreview;
         if (mAppWidgetHostViewPreview != null) {
             int containerWidth = (int) (mTargetPreviewWidth * mPreviewContainerScale);
             int containerHeight = (int) (mTargetPreviewHeight * mPreviewContainerScale);
@@ -474,7 +469,6 @@
                     INDEX_WIDGET_CENTERING,
                     -(params.width - (params.width * mPreviewContainerScale)) / 2.0f,
                     -(params.height - (params.height * mPreviewContainerScale)) / 2.0f);
-            mWidgetImageContainer.removeAllViews();
             mWidgetImageContainer.addView(mAppWidgetHostViewPreview, /* index= */ 0);
             mWidgetImage.setVisibility(View.GONE);
             applyPreview(null);
@@ -585,19 +579,4 @@
                 (mTargetPreviewHeight - verticalPadding) * mPreviewContainerScale
                         / appWidgetContentHeight);
     }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int padding = getPaddingLeft() + getPaddingRight();
-        int allowedWidth = getSize(widthMeasureSpec) - padding;
-
-        // Here we prevent having clipped widgets when they're too large as the preview width is
-        // larger than the max allowed width. We then re-do the preview with the new preview width
-        if (allowedWidth > 0 && mCachedPreview == null && allowedWidth < mTargetPreviewWidth) {
-            mTargetPreviewWidth = allowedWidth;
-            ensurePreviewWithCallback(mCallback, null);
-        }
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
 }
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 4f6cc64..cefba16 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -106,6 +106,8 @@
 
     private static boolean sDumpWasGenerated = false;
     private static boolean sActivityLeakReported = false;
+    private static boolean sSeenKeygard = false;
+
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
 
     protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
@@ -237,9 +239,13 @@
     @Before
     public void setUp() throws Exception {
         mLauncher.onTestStart();
-        Assert.assertTrue("Keyguard is visible, which is likely caused by a crash in SysUI",
-                TestHelpers.wait(
-                        Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000));
+
+        sSeenKeygard = sSeenKeygard
+                || !TestHelpers.wait(
+                Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000);
+
+        Assert.assertFalse("Keyguard is visible, which is likely caused by a crash in SysUI",
+                sSeenKeygard);
 
         final String launcherPackageName = mDevice.getLauncherPackageName();
         try {
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index f30538d..217bec3 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -51,7 +51,6 @@
 import com.android.launcher3.tapl.FolderIcon;
 import com.android.launcher3.tapl.HomeAllApps;
 import com.android.launcher3.tapl.HomeAppIcon;
-import com.android.launcher3.tapl.HomeAppIconMenu;
 import com.android.launcher3.tapl.HomeAppIconMenuItem;
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.tapl.Workspace;
@@ -391,23 +390,14 @@
                 .switchToAllApps();
         allApps.freeze();
         try {
-            final HomeAppIconMenu menu = allApps
+            final HomeAppIconMenuItem menuItem = allApps
                     .getAppIcon(APP_NAME)
-                    .openDeepShortcutMenu();
-            final HomeAppIconMenuItem menuItem0 = menu.getMenuItem(0);
-            final HomeAppIconMenuItem menuItem2 = menu.getMenuItem(2);
+                    .openDeepShortcutMenu()
+                    .getMenuItem(0);
+            final String actualShortcutName = menuItem.getText();
+            final String expectedShortcutName = "Shortcut 1";
 
-            final HomeAppIconMenuItem menuItem;
-
-            final String expectedShortcutName = "Shortcut 3";
-            if (menuItem0.getText().equals(expectedShortcutName)) {
-                menuItem = menuItem0;
-            } else {
-                final String shortcutName2 = menuItem2.getText();
-                assertEquals("Wrong menu item", expectedShortcutName, shortcutName2);
-                menuItem = menuItem2;
-            }
-
+            assertEquals(expectedShortcutName, actualShortcutName);
             menuItem.dragToWorkspace(false, false);
             mLauncher.getWorkspace().getWorkspaceAppIcon(expectedShortcutName)
                     .launch(getAppPackageName());