Merge "Add modal action for grid only overview." into tm-qpr-dev
diff --git a/OWNERS b/OWNERS
index 962d63a..dd9fbf9 100644
--- a/OWNERS
+++ b/OWNERS
@@ -36,4 +36,4 @@
 xuqiu@google.com
 
 per-file FeatureFlags.java, globs = set noparent
-per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com
+per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com, captaincole@google.com
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 347c492..9ff2cfc 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -112,7 +112,6 @@
 import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.model.data.ItemInfo;
@@ -557,11 +556,7 @@
 
             final boolean scrimEnabled = ENABLE_SCRIM_FOR_APP_LAUNCH.get();
             if (scrimEnabled) {
-                boolean useTaskbarColor = mDeviceProfile.isTaskbarPresentInApps
-                        && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
-                int scrimColor = useTaskbarColor
-                        ? mLauncher.getResources().getColor(R.color.taskbar_background)
-                        : Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor);
+                int scrimColor = Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor);
                 int scrimColorTrans = ColorUtils.setAlphaComponent(scrimColor, 0);
                 int[] colors = isAppOpening
                         ? new int[]{scrimColorTrans, scrimColor}
@@ -575,29 +570,6 @@
                     scrim.setDuration(CONTENT_SCRIM_DURATION);
                     scrim.setInterpolator(DEACCEL_1_5);
 
-                    if (useTaskbarColor) {
-                        // Hide the taskbar background color since it would duplicate the scrim.
-                        scrim.addListener(new AnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationStart(Animator animation) {
-                                LauncherTaskbarUIController taskbarUIController =
-                                        mLauncher.getTaskbarUIController();
-                                if (taskbarUIController != null) {
-                                    taskbarUIController.forceHideBackground(true);
-                                }
-                            }
-
-                            @Override
-                            public void onAnimationEnd(Animator animation) {
-                                LauncherTaskbarUIController taskbarUIController =
-                                        mLauncher.getTaskbarUIController();
-                                if (taskbarUIController != null) {
-                                    taskbarUIController.forceHideBackground(false);
-                                }
-                            }
-                        });
-                    }
-
                     launcherAnimator.play(scrim);
                 }
             }
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index ff7c138..c165750 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -82,7 +82,8 @@
     public Animator createAnimToRecentsState(RecentsState toState, long duration) {
         boolean useStashedLauncherState = toState.hasOverviewActions();
         boolean stashedLauncherState =
-                useStashedLauncherState && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
+                useStashedLauncherState && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()
+                        && toState == RecentsState.MODAL_TASK;
         TaskbarStashController stashController = mControllers.taskbarStashController;
         // Set both FLAG_IN_STASHED_LAUNCHER_STATE and FLAG_IN_APP to ensure the state is respected.
         // For all other states, just use the current stashed-in-app setting (e.g. if long clicked).
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 3046076..b00c4cb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -41,7 +41,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatedFloat;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.model.data.ItemInfo;
@@ -86,7 +85,6 @@
             };
 
     // Initialized in init.
-    private AnimatedFloat mTaskbarOverrideBackgroundAlpha;
     private TaskbarKeyguardController mKeyguardController;
     private final TaskbarLauncherStateController
             mTaskbarLauncherStateController = new TaskbarLauncherStateController();
@@ -100,8 +98,6 @@
         super.init(taskbarControllers);
 
         mTaskbarLauncherStateController.init(mControllers, mLauncher);
-        mTaskbarOverrideBackgroundAlpha = mControllers.taskbarDragLayerController
-                .getOverrideBackgroundAlpha();
 
         mLauncher.setTaskbarUIController(this);
         mKeyguardController = taskbarControllers.taskbarKeyguardController;
@@ -256,13 +252,6 @@
     }
 
     /**
-     * Sets whether the background behind the taskbar/nav bar should be hidden.
-     */
-    public void forceHideBackground(boolean forceHide) {
-        mTaskbarOverrideBackgroundAlpha.updateValue(forceHide ? 0 : 1);
-    }
-
-    /**
      * Starts a Taskbar EDU flow, if the user should see one upon launching an application.
      */
     public void showEduOnAppLaunch() {
@@ -318,21 +307,6 @@
                 instanceId);
     }
 
-    @Override
-    public void setSystemGestureInProgress(boolean inProgress) {
-        super.setSystemGestureInProgress(inProgress);
-        if (DisplayController.isTransientTaskbar(mLauncher)) {
-            forceHideBackground(false);
-            return;
-        }
-        if (!FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
-            // Launcher's ScrimView will draw the background throughout the gesture. But once the
-            // gesture ends, start drawing taskbar's background again since launcher might stop
-            // drawing.
-            forceHideBackground(inProgress);
-        }
-    }
-
     /**
      * Animates Taskbar elements during a transition to a Launcher state that should use in-app
      * layouts.
@@ -401,10 +375,6 @@
     public void dumpLogs(String prefix, PrintWriter pw) {
         super.dumpLogs(prefix, pw);
 
-        pw.println(String.format(
-                "%s\tmTaskbarOverrideBackgroundAlpha=%.2f",
-                prefix,
-                mTaskbarOverrideBackgroundAlpha.value));
         pw.println(String.format("%s\tTaskbar in-app display progress:", prefix));
         mTaskbarInAppDisplayProgressMultiProp.dump(
                 prefix + "\t",
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index ab52adb..dde7d94 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -88,6 +88,7 @@
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -105,7 +106,6 @@
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
 import java.io.PrintWriter;
-import java.util.function.Consumer;
 
 /**
  * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
@@ -877,8 +877,9 @@
      * (potentially breaking a split pair).
      */
     private void launchFromTaskbarPreservingSplitIfVisible(RecentsView recents, ItemInfo info) {
+        ComponentKey componentToBeLaunched = new ComponentKey(info.getTargetComponent(), info.user);
         recents.getSplitSelectController().findLastActiveTaskAndRunCallback(
-                info.getTargetComponent(),
+                componentToBeLaunched,
                 foundTask -> {
                     if (foundTask != null) {
                         TaskView foundTaskView =
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 267bee1..7c4071f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -127,10 +127,6 @@
         return mAssistantBgTaskbar;
     }
 
-    public AnimatedFloat getOverrideBackgroundAlpha() {
-        return mBgOverride;
-    }
-
     public AnimatedFloat getTaskbarBackgroundOffset() {
         return mBgOffset;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 4110822..8c91833 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -130,10 +130,10 @@
                         ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext)
                         : null;
                 int configDiff = mOldConfig.diff(newConfig);
+                int configDiffForRecreate = configDiff;
                 int configsRequiringRecreate = ActivityInfo.CONFIG_ASSETS_PATHS
                         | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_UI_MODE
                         | ActivityInfo.CONFIG_SCREEN_SIZE;
-                boolean requiresRecreate = (configDiff & configsRequiringRecreate) != 0;
                 if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0
                         && mTaskbarActivityContext != null && dp != null
                         && !isPhoneMode(dp)) {
@@ -146,12 +146,19 @@
                     int oldWidth = isOrientationChange ? oldDp.heightPx : oldDp.widthPx;
                     int oldHeight = isOrientationChange ? oldDp.widthPx : oldDp.heightPx;
                     if (dp.widthPx == oldWidth && dp.heightPx == oldHeight) {
-                        configDiff &= ~ActivityInfo.CONFIG_SCREEN_SIZE;
-                        requiresRecreate = (configDiff & configsRequiringRecreate) != 0;
+                        configDiffForRecreate &= ~ActivityInfo.CONFIG_SCREEN_SIZE;
+                    }
+                }
+                if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0) {
+                    // Only recreate for theme changes, not other UI mode changes such as docking.
+                    int oldUiNightMode = (mOldConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK);
+                    int newUiNightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK);
+                    if (oldUiNightMode == newUiNightMode) {
+                        configDiffForRecreate &= ~ActivityInfo.CONFIG_UI_MODE;
                     }
                 }
 
-                if (requiresRecreate) {
+                if ((configDiffForRecreate & configsRequiringRecreate) != 0) {
                     recreateTaskbar();
                 } else {
                     // Config change might be handled without re-creating the taskbar
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index bbf861b..115db25 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -723,7 +723,7 @@
             }
         }
         play(as, mControllers.taskbarViewController
-                .createRevealAnimToIsStashed(isStashed, isInApp()), 0, duration, EMPHASIZED);
+                .createRevealAnimToIsStashed(isStashed), 0, duration, EMPHASIZED);
 
         if (skipInterpolator != null) {
             as.setInterpolator(skipInterpolator);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 3479255..b552e9b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -30,16 +30,15 @@
 
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
-import com.android.systemui.shared.recents.model.Task;
 
 import java.io.PrintWriter;
-import java.util.function.Consumer;
 
 /**
  * Base class for providing different taskbar UI
@@ -189,8 +188,12 @@
         if (recentsView == null) {
             return;
         }
+
+        ComponentKey componentToBeStaged = new ComponentKey(
+                splitSelectSource.itemInfo.getTargetComponent(),
+                splitSelectSource.itemInfo.user);
         recentsView.getSplitSelectController().findLastActiveTaskAndRunCallback(
-                splitSelectSource.intent.getComponent(),
+                componentToBeStaged,
                 foundTask -> {
                     splitSelectSource.alreadyRunningTaskId = foundTask == null
                             ? INVALID_TASK_ID
@@ -206,8 +209,9 @@
      */
     public void triggerSecondAppForSplit(ItemInfoWithIcon info, Intent intent, View startingView) {
         RecentsView recents = getRecentsView();
+        ComponentKey secondAppComponent = new ComponentKey(info.getTargetComponent(), info.user);
         recents.getSplitSelectController().findLastActiveTaskAndRunCallback(
-                info.getTargetComponent(),
+                secondAppComponent,
                 foundTask -> {
                     if (foundTask != null) {
                         TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index c708838..ac92374 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.anim.AnimatedFloat.VALUE;
 import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
@@ -303,7 +304,7 @@
      * @param isStashed When true, the icon crops vertically to the size of the stashed handle.
      *                  When false, the reverse happens.
      */
-    public AnimatorSet createRevealAnimToIsStashed(boolean isStashed, boolean isInApp) {
+    public AnimatorSet createRevealAnimToIsStashed(boolean isStashed) {
         AnimatorSet as = new AnimatorSet();
 
         Rect stashedBounds = new Rect();
@@ -317,6 +318,10 @@
         for (int i = numChildren - 1; i >= 0; i--) {
             View child = mTaskbarView.getChildAt(i);
 
+            if (child == mTaskbarView.getQsb()) {
+                continue;
+            }
+
             // Crop the icons to/from the nav handle shape.
             as.play(createRevealAnimForView(child, isStashed));
 
@@ -437,7 +442,7 @@
                 float scale = ((float) taskbarDp.iconSizePx) / launcherDp.hotseatQsbVisualHeight;
                 setter.addFloat(child, SCALE_PROPERTY, scale, 1f, interpolator);
 
-                setter.setFloat(child, ICON_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
+                setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
 
                 if (mIsHotseatIconOnTopWhenAligned) {
                     setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 951a9b6..93baf5b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -136,6 +136,7 @@
 import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
 import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.NavigationMode;
@@ -574,10 +575,13 @@
     @Override
     public void startSplitSelection(SplitSelectSource splitSelectSource) {
         RecentsView recentsView = getOverviewPanel();
+        ComponentKey componentToBeStaged = new ComponentKey(
+                splitSelectSource.itemInfo.getTargetComponent(),
+                splitSelectSource.itemInfo.user);
         // Check if there is already an instance of this app running, if so, initiate the split
         // using that.
         mSplitSelectStateController.findLastActiveTaskAndRunCallback(
-                splitSelectSource.intent.getComponent(),
+                componentToBeStaged,
                 foundTask -> {
                     splitSelectSource.alreadyRunningTaskId = foundTask == null
                             ? INVALID_TASK_ID
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
index 29ef2b4..84b873d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
@@ -18,28 +18,53 @@
 
 import static android.app.ActivityThread.currentApplication;
 
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+import android.util.Log;
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags.BooleanFlag;
+import com.android.launcher3.config.FeatureFlags.IntFlag;
+import com.android.launcher3.util.ScreenOnTracker;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Helper class to create various flags for system build
  */
 public class FlagsFactory {
 
+    private static final String TAG = "FlagsFactory";
+
+    private static final FlagsFactory INSTANCE = new FlagsFactory();
+    private static final boolean FLAG_AUTO_APPLY_ENABLED = false;
+
     public static final String FLAGS_PREF_NAME = "featureFlags";
     public static final String NAMESPACE_LAUNCHER = "launcher";
 
     private static final List<DebugFlag> sDebugFlags = new ArrayList<>();
 
+
+    private final Set<String> mKeySet = new HashSet<>();
+    private boolean mRestartRequested = false;
+
+    private FlagsFactory() {
+        if (!FLAG_AUTO_APPLY_ENABLED) {
+            return;
+        }
+        DeviceConfig.addOnPropertiesChangedListener(
+                NAMESPACE_LAUNCHER, UI_HELPER_EXECUTOR, this::onPropertiesChanged);
+    }
+
     /**
      * Creates a new debug flag
      */
@@ -63,6 +88,7 @@
      */
     public static BooleanFlag getReleaseFlag(
             int bugId, String key, boolean defaultValueInCode, String description) {
+        INSTANCE.mKeySet.add(key);
         boolean defaultValue = DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, defaultValueInCode);
         if (Utilities.IS_DEBUG_DEVICE) {
             SharedPreferences prefs = currentApplication()
@@ -78,6 +104,15 @@
         }
     }
 
+    /**
+     * Creates a new integer flag. Integer flags are always release flags
+     */
+    public static IntFlag getIntFlag(
+            int bugId, String key, int defaultValueInCode, String description) {
+        INSTANCE.mKeySet.add(key);
+        return new IntFlag(DeviceConfig.getInt(NAMESPACE_LAUNCHER, key, defaultValueInCode));
+    }
+
     static List<DebugFlag> getDebugFlags() {
         if (!Utilities.IS_DEBUG_DEVICE) {
             return Collections.emptyList();
@@ -121,4 +156,28 @@
             }
         }
     }
+
+    private void onPropertiesChanged(Properties properties) {
+        if (!Collections.disjoint(properties.getKeyset(), mKeySet)) {
+            // Schedule a restart
+            if (mRestartRequested) {
+                return;
+            }
+            Log.e(TAG, "Flag changed, scheduling restart");
+            mRestartRequested = true;
+            ScreenOnTracker sot = ScreenOnTracker.INSTANCE.get(currentApplication());
+            if (sot.isScreenOn()) {
+                sot.addListener(this::onScreenOnChanged);
+            } else {
+                onScreenOnChanged(false);
+            }
+        }
+    }
+
+    private void onScreenOnChanged(boolean isOn) {
+        if (mRestartRequested && !isOn) {
+            Log.e(TAG, "Restart requested, killing process");
+            System.exit(0);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 8125fc8..e578720 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -24,9 +24,7 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.DesktopTaskView;
 import com.android.quickstep.views.RecentsView;
@@ -103,10 +101,6 @@
 
     @Override
     public int getWorkspaceScrimColor(Launcher launcher) {
-        DeviceProfile dp = launcher.getDeviceProfile();
-        if (dp.isTaskbarPresentInApps && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
-            return launcher.getColor(R.color.taskbar_background);
-        }
         return Color.TRANSPARENT;
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 233ed46..d2f9294 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -26,7 +26,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Themes;
 import com.android.quickstep.util.LayoutUtils;
@@ -104,13 +103,8 @@
     }
 
     @Override
-    public boolean isTaskbarStashed(Launcher launcher) {
-        return !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
-    }
-
-    @Override
     public boolean isTaskbarAlignedWithHotseat(Launcher launcher) {
-        return !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
+        return false;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
index 8babd34..3ae221b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -53,4 +53,9 @@
             return SplitAnimationTimings.ABORT_DURATION;
         }
     }
+
+    @Override
+    public boolean shouldPreserveDataStateOnReapply() {
+        return true;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/BootAwarePreloader.kt b/quickstep/src/com/android/quickstep/BootAwarePreloader.kt
new file mode 100644
index 0000000..35404a9
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/BootAwarePreloader.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.quickstep
+
+import android.content.Context
+import android.util.Log
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.isBootAwareStartupDataEnabled
+import com.android.launcher3.util.LockedUserState
+
+/**
+ * Loads expensive objects in memory before the user is unlocked. This decreases experienced latency
+ * when starting the launcher for the first time after a reboot.
+ */
+object BootAwarePreloader {
+    private const val TAG = "BootAwarePreloader"
+
+    @JvmStatic
+    fun start(context: Context) {
+        val lp = LauncherPrefs.get(context)
+        when {
+            LockedUserState.get(context).isUserUnlocked || !isBootAwareStartupDataEnabled -> {
+                /* No-Op */
+            }
+            lp.isStartupDataMigrated -> {
+                Log.d(TAG, "preloading start up data")
+                LauncherAppState.INSTANCE.get(context)
+            }
+            else -> {
+                Log.d(TAG, "queuing start up data migration to boot aware prefs")
+                LockedUserState.get(context).runOnUserUnlocked {
+                    lp.migrateStartupDataToDeviceProtectedStorage()
+                }
+            }
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index ff81e08..accab38 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -410,6 +410,7 @@
         mDeviceState = new RecentsAnimationDeviceState(this, true);
         mTaskbarManager = new TaskbarManager(this);
         mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
+        BootAwarePreloader.start(this);
 
         // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
         mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index d1100c7..5214f7c 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -29,7 +29,6 @@
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -58,6 +57,7 @@
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
 import com.android.quickstep.RecentsModel;
@@ -162,7 +162,7 @@
      * Used in various task-switching or splitscreen operations when we need to check if there is a
      * currently running Task of a certain type and use the most recent one.
      */
-    public void findLastActiveTaskAndRunCallback(ComponentName componentName,
+    public void findLastActiveTaskAndRunCallback(ComponentKey componentKey,
             Consumer<Task> callback) {
         mRecentTasksModel.getTasks(taskGroups -> {
             Task lastActiveTask = null;
@@ -170,12 +170,12 @@
             for (int i = taskGroups.size() - 1; i >= 0; i--) {
                 GroupTask groupTask = taskGroups.get(i);
                 Task task1 = groupTask.task1;
-                if (isInstanceOfComponent(task1, componentName)) {
+                if (isInstanceOfComponent(task1, componentKey)) {
                     lastActiveTask = task1;
                     break;
                 }
                 Task task2 = groupTask.task2;
-                if (isInstanceOfComponent(task2, componentName)) {
+                if (isInstanceOfComponent(task2, componentKey)) {
                     lastActiveTask = task2;
                     break;
                 }
@@ -189,13 +189,14 @@
      * Checks if a given Task is the most recently-active Task of type componentName. Used for
      * selecting already-running Tasks for splitscreen.
      */
-    public boolean isInstanceOfComponent(@Nullable Task task, ComponentName componentName) {
+    public boolean isInstanceOfComponent(@Nullable Task task, ComponentKey componentKey) {
         // Exclude the task that is already staged
         if (task == null || task.key.id == mInitialTaskId) {
             return false;
         }
 
-        return task.key.baseIntent.getComponent().equals(componentName);
+        return task.key.baseIntent.getComponent().equals(componentKey.componentName)
+                && task.key.userId == componentKey.user.getIdentifier();
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 1058e99..409504b 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -261,26 +261,8 @@
      * Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar.
      */
     private void updatePadding() {
-        if (mDp == null) {
-            return;
-        }
-        boolean largeScreenLandscape = mDp.isTablet && !mDp.isTwoPanels && mDp.isLandscape;
-        // If in 3-button mode, shift action buttons to accommodate 3-button layout.
-        // (Special exception for landscape tablets, where there is enough room and we don't need to
-        // shift the action buttons.)
-        if (mDp.areNavButtonsInline && !largeScreenLandscape
-                // If taskbar is in overview, overview action has dedicated space above nav buttons
-                && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
-            // Add extra horizontal spacing
-            int additionalPadding = mDp.hotseatBarEndOffset;
-            if (isLayoutRtl()) {
-                setPadding(mInsets.left + additionalPadding, 0, mInsets.right, 0);
-            } else {
-                setPadding(mInsets.left, 0, mInsets.right + additionalPadding, 0);
-            }
-        } else {
-            setPadding(mInsets.left, 0, mInsets.right, 0);
-        }
+        // If taskbar is in overview, overview action has dedicated space above nav buttons
+        setPadding(mInsets.left, 0, mInsets.right, 0);
     }
 
     /** Updates vertical margins for different navigation mode or configuration changes. */
@@ -300,11 +282,6 @@
             return 0;
         }
 
-        if (!mDp.isGestureMode && mDp.isTaskbarPresent
-                && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
-            return mDp.getOverviewActionsClaimedSpaceBelow();
-        }
-
         if (mDp.isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()) {
             return mDp.stashedTaskbarSize;
         }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 1122e80..843b2fb 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -44,7 +44,6 @@
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCH_FROM_STAGED_APP;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_ACTIONS_SPLIT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
@@ -3681,8 +3680,7 @@
                         removeViewInLayout(mClearAllButton);
                         if (isHomeTaskDismissed) {
                             updateEmptyMessage();
-                        } else if (!(ENABLE_TASKBAR_IN_OVERVIEW.get() &&
-                                mSplitSelectStateController.isSplitSelectActive())) {
+                        } else if (!mSplitSelectStateController.isSplitSelectActive()) {
                             startHome();
                         }
                     } else {
@@ -3785,8 +3783,7 @@
         mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SELECT_ACTIVE, isSplitSelectionActive());
         mActionsView.updateSplitButtonHiddenFlags(FLAG_IS_NOT_TABLET,
                 !mActivity.getDeviceProfile().isTablet);
-        mActionsView.updateSplitButtonDisabledFlags(FLAG_SINGLE_TASK,
-                !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get() && getTaskViewCount() <= 1);
+        mActionsView.updateSplitButtonDisabledFlags(FLAG_SINGLE_TASK, /*enable=*/ false);
         if (DESKTOP_MODE_SUPPORTED) {
             boolean isCurrentDesktop = getCurrentPageTaskView() instanceof DesktopTaskView;
             mActionsView.updateHiddenFlags(HIDDEN_DESKTOP, isCurrentDesktop);
@@ -4581,7 +4578,6 @@
         return true;
     }
 
-    /** TODO(b/181707736) More gracefully handle exiting split selection state */
     @SuppressLint("WrongCall")
     protected void resetFromSplitSelectionState() {
         if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1) {
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index b0b111d..0d9e412 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -16,8 +16,6 @@
 
 package com.android.quickstep.views;
 
-import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
-
 import android.content.Context;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -27,11 +25,8 @@
 import androidx.annotation.Nullable;
 import androidx.appcompat.widget.AppCompatTextView;
 
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.util.DisplayController;
 
 /**
  * A rounded rectangular component containing a single TextView.
@@ -103,36 +98,10 @@
                         this,
                         mLauncher.getDeviceProfile(),
                         getMeasuredHeight(),
-                        getMeasuredWidth(),
-                        getThreeButtonNavShift()
+                        getMeasuredWidth()
                 );
     }
 
-    // In some cases, when user is using 3-button nav, there isn't enough room for both the
-    // 3-button nav and a centered SplitInstructionsView. This function will return an int that will
-    // be used to shift the SplitInstructionsView over a bit so that everything looks well-spaced.
-    // In many cases, this will return 0, since we don't need to shift it away from the center.
-    int getThreeButtonNavShift() {
-        DeviceProfile dp = mLauncher.getDeviceProfile();
-        if ((DisplayController.getNavigationMode(getContext()) == THREE_BUTTONS)
-                && ((dp.isTwoPanels) || (dp.isTablet && !dp.isLandscape))
-                // If taskbar is in overview, overview action has dedicated space above nav buttons
-                && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
-            int navButtonWidth = getResources().getDimensionPixelSize(
-                    R.dimen.taskbar_nav_buttons_size);
-            int extraMargin = getResources().getDimensionPixelSize(
-                    R.dimen.taskbar_split_instructions_margin);
-            // Explanation: The 3-button nav for non-phones sits on one side of the screen, taking
-            // up 3 buttons + a side margin worth of space. Our splitInstructionsView starts in the
-            // center of the screen and we want to center it in the remaining space, therefore we
-            // want to shift it over by half the 3-button layout's width.
-            // If the user is using an RtL layout, we shift it the opposite way.
-            return -((3 * navButtonWidth + extraMargin) / 2) * (isLayoutRtl() ? -1 : 1);
-        } else {
-            return 0;
-        }
-    }
-
     public AppCompatTextView getTextView() {
         return mTextView;
     }
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
index 5abdc93..512df8e 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -23,12 +23,14 @@
 import android.content.Intent
 import android.graphics.Rect
 import android.os.Handler
+import android.os.UserHandle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.launcher3.LauncherState
 import com.android.launcher3.logging.StatsLogManager
 import com.android.launcher3.model.data.ItemInfo
 import com.android.launcher3.statehandlers.DepthController
 import com.android.launcher3.statemanager.StateManager
+import com.android.launcher3.util.ComponentKey
 import com.android.launcher3.util.SplitConfigurationOptions
 import com.android.launcher3.util.withArgCaptor
 import com.android.quickstep.RecentsModel
@@ -60,6 +62,9 @@
 
     lateinit var splitSelectStateController: SplitSelectStateController
 
+    private val primaryUserHandle = UserHandle(ActivityManager.RunningTaskInfo().userId)
+    private val nonPrimaryUserHandle = UserHandle(ActivityManager.RunningTaskInfo().userId + 10)
+
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
@@ -77,6 +82,7 @@
 
     @Test
     fun activeTasks_noMatchingTasks() {
+        val nonMatchingComponent = ComponentKey(ComponentName("no", "match"), primaryUserHandle)
         val groupTask1 =
             generateGroupTask(
                 ComponentName("pomegranate", "juice"),
@@ -100,7 +106,7 @@
         val consumer =
             withArgCaptor<Consumer<ArrayList<GroupTask>>> {
                 splitSelectStateController.findLastActiveTaskAndRunCallback(
-                    ComponentName("no", "match"),
+                    nonMatchingComponent,
                     taskConsumer
                 )
                 verify(recentsModel).getTasks(capture())
@@ -114,6 +120,8 @@
     fun activeTasks_singleMatchingTask() {
         val matchingPackage = "hotdog"
         val matchingClass = "juice"
+        val matchingComponent =
+            ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
         val groupTask1 =
             generateGroupTask(
                 ComponentName(matchingPackage, matchingClass),
@@ -149,7 +157,100 @@
         val consumer =
             withArgCaptor<Consumer<ArrayList<GroupTask>>> {
                 splitSelectStateController.findLastActiveTaskAndRunCallback(
-                    ComponentName(matchingPackage, matchingClass),
+                    matchingComponent,
+                    taskConsumer
+                )
+                verify(recentsModel).getTasks(capture())
+            }
+
+        // Send our mocked tasks
+        consumer.accept(tasks)
+    }
+
+    @Test
+    fun activeTasks_skipTaskWithDifferentUser() {
+        val matchingPackage = "hotdog"
+        val matchingClass = "juice"
+        val nonPrimaryUserComponent =
+            ComponentKey(ComponentName(matchingPackage, matchingClass), nonPrimaryUserHandle)
+        val groupTask1 =
+            generateGroupTask(
+                ComponentName(matchingPackage, matchingClass),
+                ComponentName("pomegranate", "juice")
+            )
+        val groupTask2 =
+            generateGroupTask(
+                ComponentName("pumpkin", "pie"),
+                ComponentName("personal", "computer")
+            )
+        val tasks: ArrayList<GroupTask> = ArrayList()
+        tasks.add(groupTask1)
+        tasks.add(groupTask2)
+
+        // Assertions happen in the callback we get from what we pass into
+        // #findLastActiveTaskAndRunCallback
+        val taskConsumer =
+            Consumer<Task> { assertNull("No tasks should have matched", it /*task*/) }
+
+        // Capture callback from recentsModel#getTasks()
+        val consumer =
+            withArgCaptor<Consumer<ArrayList<GroupTask>>> {
+                splitSelectStateController.findLastActiveTaskAndRunCallback(
+                    nonPrimaryUserComponent,
+                    taskConsumer
+                )
+                verify(recentsModel).getTasks(capture())
+            }
+
+        // Send our mocked tasks
+        consumer.accept(tasks)
+    }
+
+    @Test
+    fun activeTasks_findTaskAsNonPrimaryUser() {
+        val matchingPackage = "hotdog"
+        val matchingClass = "juice"
+        val nonPrimaryUserComponent =
+            ComponentKey(ComponentName(matchingPackage, matchingClass), nonPrimaryUserHandle)
+        val groupTask1 =
+            generateGroupTask(
+                ComponentName(matchingPackage, matchingClass),
+                nonPrimaryUserHandle,
+                ComponentName("pomegranate", "juice"),
+                nonPrimaryUserHandle
+            )
+        val groupTask2 =
+            generateGroupTask(
+                ComponentName("pumpkin", "pie"),
+                ComponentName("personal", "computer")
+            )
+        val tasks: ArrayList<GroupTask> = ArrayList()
+        tasks.add(groupTask1)
+        tasks.add(groupTask2)
+
+        // Assertions happen in the callback we get from what we pass into
+        // #findLastActiveTaskAndRunCallback
+        val taskConsumer =
+            Consumer<Task> {
+                assertEquals(
+                    "ComponentName package mismatched",
+                    it.key.baseIntent.component.packageName,
+                    matchingPackage
+                )
+                assertEquals(
+                    "ComponentName class mismatched",
+                    it.key.baseIntent.component.className,
+                    matchingClass
+                )
+                assertEquals("userId mismatched", it.key.userId, nonPrimaryUserHandle.identifier)
+                assertEquals(it, groupTask1.task1)
+            }
+
+        // Capture callback from recentsModel#getTasks()
+        val consumer =
+            withArgCaptor<Consumer<ArrayList<GroupTask>>> {
+                splitSelectStateController.findLastActiveTaskAndRunCallback(
+                    nonPrimaryUserComponent,
                     taskConsumer
                 )
                 verify(recentsModel).getTasks(capture())
@@ -163,6 +264,8 @@
     fun activeTasks_multipleMatchMostRecentTask() {
         val matchingPackage = "hotdog"
         val matchingClass = "juice"
+        val matchingComponent =
+            ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
         val groupTask1 =
             generateGroupTask(
                 ComponentName(matchingPackage, matchingClass),
@@ -198,7 +301,7 @@
         val consumer =
             withArgCaptor<Consumer<ArrayList<GroupTask>>> {
                 splitSelectStateController.findLastActiveTaskAndRunCallback(
-                    ComponentName(matchingPackage, matchingClass),
+                    matchingComponent,
                     taskConsumer
                 )
                 verify(recentsModel).getTasks(capture())
@@ -245,6 +348,7 @@
         assertFalse(splitSelectStateController.isSplitSelectActive)
     }
 
+    // Generate GroupTask with default userId.
     private fun generateGroupTask(
         task1ComponentName: ComponentName,
         task2ComponentName: ComponentName
@@ -268,4 +372,34 @@
             SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1)
         )
     }
+
+    // Generate GroupTask with custom user handles.
+    private fun generateGroupTask(
+        task1ComponentName: ComponentName,
+        userHandle1: UserHandle,
+        task2ComponentName: ComponentName,
+        userHandle2: UserHandle
+    ): GroupTask {
+        val task1 = Task()
+        var taskInfo = ActivityManager.RunningTaskInfo()
+        // Apply custom userHandle1
+        taskInfo.userId = userHandle1.identifier
+        var intent = Intent()
+        intent.component = task1ComponentName
+        taskInfo.baseIntent = intent
+        task1.key = Task.TaskKey(taskInfo)
+        val task2 = Task()
+        taskInfo = ActivityManager.RunningTaskInfo()
+        // Apply custom userHandle2
+        taskInfo.userId = userHandle2.identifier
+        intent = Intent()
+        intent.component = task2ComponentName
+        taskInfo.baseIntent = intent
+        task2.key = Task.TaskKey(taskInfo)
+        return GroupTask(
+            task1,
+            task2,
+            SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1)
+        )
+    }
 }
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index ba6e547..82758bf 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -415,10 +415,6 @@
     <dimen name="split_instructions_elevation">1dp</dimen>
     <dimen name="split_instructions_horizontal_padding">24dp</dimen>
     <dimen name="split_instructions_vertical_padding">12dp</dimen>
-    <dimen name="split_instructions_bottom_margin_tablet_landscape">32dp</dimen>
-    <dimen name="split_instructions_bottom_margin_tablet_portrait">44dp</dimen>
-    <dimen name="split_instructions_bottom_margin_twopanels_landscape">33dp</dimen>
-    <dimen name="split_instructions_bottom_margin_twopanels_portrait">51dp</dimen>
     <dimen name="split_instructions_bottom_margin_phone_landscape">24dp</dimen>
     <dimen name="split_instructions_bottom_margin_phone_portrait">60dp</dimen>
     
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 8a90d0e..87ee4f6 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -1417,14 +1417,7 @@
      */
     public int getOverviewActionsClaimedSpaceBelow() {
         if (isTaskbarPresent) {
-            if (FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
-                return transientTaskbarSize + transientTaskbarMargin * 2;
-            }
-
-            return isGestureMode
-                    ? stashedTaskbarSize
-                    // Align vertically to where nav buttons are.
-                    : ((taskbarSize - overviewActionsHeight) / 2) + getTaskbarOffsetY();
+            return transientTaskbarSize + transientTaskbarMargin * 2;
         }
         return mInsets.bottom;
     }
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 4ac7f07..a498323 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -59,6 +59,7 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.LockedUserState;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Partner;
 import com.android.launcher3.util.Themes;
@@ -205,7 +206,9 @@
         if (!newGridName.equals(gridName)) {
             LauncherPrefs.get(context).put(GRID_NAME, newGridName);
         }
-        new DeviceGridState(this).writeToPrefs(context);
+        LockedUserState.get(context).runOnUserUnlocked(() -> {
+            new DeviceGridState(this).writeToPrefs(context);
+        });
 
         DisplayController.INSTANCE.get(context).setPriorityListener(
                 (displayContext, info, flags) -> {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 2b98d98..c81ad01 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.InstallSessionTracker;
 import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.LockedUserState;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.RunnableList;
@@ -106,25 +107,27 @@
         }
         mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
 
-        CustomWidgetManager.INSTANCE.get(mContext)
-                .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
-
         SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext)
                 .addUserChangeListener(mModel::forceReload);
         mOnTerminateCallback.add(userChangeListener::close);
 
-        IconObserver observer = new IconObserver();
-        SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener(
-                observer, MODEL_EXECUTOR.getHandler());
-        mOnTerminateCallback.add(iconChangeTracker::close);
-        MODEL_EXECUTOR.execute(observer::verifyIconChanged);
-        LauncherPrefs.get(context).addListener(observer, THEMED_ICONS);
-        mOnTerminateCallback.add(
-                () -> LauncherPrefs.get(mContext).removeListener(observer, THEMED_ICONS));
+        LockedUserState.get(context).runOnUserUnlocked(() -> {
+            CustomWidgetManager.INSTANCE.get(mContext)
+                    .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
 
-        InstallSessionTracker installSessionTracker =
-                InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);
-        mOnTerminateCallback.add(installSessionTracker::unregister);
+            IconObserver observer = new IconObserver();
+            SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener(
+                    observer, MODEL_EXECUTOR.getHandler());
+            mOnTerminateCallback.add(iconChangeTracker::close);
+            MODEL_EXECUTOR.execute(observer::verifyIconChanged);
+            LauncherPrefs.get(context).addListener(observer, THEMED_ICONS);
+            mOnTerminateCallback.add(
+                    () -> LauncherPrefs.get(mContext).removeListener(observer, THEMED_ICONS));
+
+            InstallSessionTracker installSessionTracker =
+                    InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);
+            mOnTerminateCallback.add(installSessionTracker::unregister);
+        });
 
         // Register an observer to rebind the notification listener when dots are re-enabled.
         SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext);
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 5680c18..e675add 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -1,8 +1,25 @@
+/*
+ * 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
 
 import android.content.Context
+import android.content.Context.MODE_PRIVATE
 import android.content.SharedPreferences
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener
+import android.util.Log
 import androidx.annotation.VisibleForTesting
 import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
 import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY
@@ -20,10 +37,34 @@
  *
  * TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
  */
-class LauncherPrefs(private val context: Context) {
+class LauncherPrefs(private val encryptedContext: Context) {
+    private val deviceProtectedStorageContext =
+        encryptedContext.createDeviceProtectedStorageContext()
+
+    private val bootAwarePrefs
+        get() =
+            deviceProtectedStorageContext.getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
+
+    private val Item.encryptedPrefs
+        get() = encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
+
+    // This call to `SharedPreferences` needs to be explicit rather than using `get` since doing so
+    // would result in a circular dependency between `isStartupDataMigrated` and `choosePreferences`
+    val isStartupDataMigrated: Boolean
+        get() =
+            bootAwarePrefs.getBoolean(
+                IS_STARTUP_DATA_MIGRATED.sharedPrefKey,
+                IS_STARTUP_DATA_MIGRATED.defaultValue
+            )
+
+    private fun chooseSharedPreferences(item: Item): SharedPreferences =
+        if (isBootAwareStartupDataEnabled && item.isBootAware && isStartupDataMigrated)
+            bootAwarePrefs
+        else item.encryptedPrefs
 
     /** Wrapper around `getInner` for a `ContextualItem` */
-    fun <T> get(item: ContextualItem<T>): T = getInner(item, item.defaultValueFromContext(context))
+    fun <T> get(item: ContextualItem<T>): T =
+        getInner(item, item.defaultValueFromContext(encryptedContext))
 
     /** Wrapper around `getInner` for an `Item` */
     fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
@@ -35,7 +76,7 @@
      */
     @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
     private fun <T> getInner(item: Item, default: T): T {
-        val sp = context.getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE)
+        val sp = chooseSharedPreferences(item)
 
         return when (item.type) {
             String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
@@ -68,16 +109,8 @@
     fun put(vararg itemsToValues: Pair<Item, Any>): Unit =
         prepareToPutValues(itemsToValues).forEach { it.apply() }
 
-    /**
-     * Stores the value provided in `SharedPreferences` according to the item configuration provided
-     * It is asynchronous, so the caller can't assume that the value put is immediately available.
-     */
-    fun <T : Any> put(item: Item, value: T): Unit =
-        context
-            .getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE)
-            .edit()
-            .putValue(item, value)
-            .apply()
+    /** See referenced `put` method above. */
+    fun <T : Any> put(item: Item, value: T): Unit = put(item.to(value))
 
     /**
      * Synchronously stores all the values provided according to their associated Item
@@ -87,27 +120,35 @@
         prepareToPutValues(itemsToValues).forEach { it.commit() }
 
     /**
-     * Update each shared preference file with the item - value pairs provided. This method is
-     * optimized to avoid retrieving the same shared preference file multiple times.
+     * Updates the values stored in `SharedPreferences` for each corresponding Item-value pair. If
+     * the item is boot aware, this method updates both the boot aware and the encrypted files. This
+     * is done because: 1) It allows for easy roll-back if the data is already in encrypted prefs
+     * and we need to turn off the boot aware data feature & 2) It simplifies Backup/Restore, which
+     * already points to encrypted storage.
      *
-     * @return `List<SharedPreferences.Editor>` 1 for each distinct shared preference file among the
-     *   items given as part of the itemsToValues parameter
+     * Returns a list of editors with all transactions added so that the caller can determine to use
+     * .apply() or .commit()
      */
     private fun prepareToPutValues(
-        itemsToValues: Array<out Pair<Item, Any>>
-    ): List<SharedPreferences.Editor> =
-        itemsToValues
-            .groupBy { it.first.sharedPrefFile }
-            .map { fileToItemValueList ->
-                context
-                    .getSharedPreferences(fileToItemValueList.key, Context.MODE_PRIVATE)
-                    .edit()
-                    .apply {
-                        fileToItemValueList.value.forEach { itemToValue ->
-                            putValue(itemToValue.first, itemToValue.second)
-                        }
-                    }
+        updates: Array<out Pair<Item, Any>>
+    ): List<SharedPreferences.Editor> {
+        val updatesPerPrefFile = updates.groupBy { it.first.encryptedPrefs }.toMutableMap()
+
+        if (isBootAwareStartupDataEnabled) {
+            val bootAwareUpdates = updates.filter { it.first.isBootAware }
+            if (bootAwareUpdates.isNotEmpty()) {
+                updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
             }
+        }
+
+        return updatesPerPrefFile.map { prefToItemValueList ->
+            prefToItemValueList.key.edit().apply {
+                prefToItemValueList.value.forEach { itemToValue: Pair<Item, Any> ->
+                    putValue(itemToValue.first, itemToValue.second)
+                }
+            }
+        }
+    }
 
     /**
      * Handles adding values to `SharedPreferences` regardless of type. This method is especially
@@ -117,10 +158,10 @@
     @Suppress("UNCHECKED_CAST")
     private fun SharedPreferences.Editor.putValue(
         item: Item,
-        value: Any
+        value: Any?
     ): SharedPreferences.Editor =
-        when (value::class.java) {
-            String::class.java -> putString(item.sharedPrefKey, value as String)
+        when (item.type) {
+            String::class.java -> putString(item.sharedPrefKey, value as? String)
             Boolean::class.java,
             java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
             Int::class.java,
@@ -129,10 +170,10 @@
             java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
             Long::class.java,
             java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
-            Set::class.java -> putStringSet(item.sharedPrefKey, value as Set<String>)
+            Set::class.java -> putStringSet(item.sharedPrefKey, value as? Set<String>)
             else ->
                 throw IllegalArgumentException(
-                    "item type: ${value::class} is not compatible with sharedPref methods"
+                    "item type: ${item.type} is not compatible with sharedPref methods"
                 )
         }
 
@@ -143,13 +184,9 @@
      */
     fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
         items
-            .map { it.sharedPrefFile }
+            .map { chooseSharedPreferences(it) }
             .distinct()
-            .forEach {
-                context
-                    .getSharedPreferences(it, Context.MODE_PRIVATE)
-                    .registerOnSharedPreferenceChangeListener(listener)
-            }
+            .forEach { it.registerOnSharedPreferenceChangeListener(listener) }
     }
 
     /**
@@ -159,13 +196,9 @@
     fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item) {
         // If a listener is not registered to a SharedPreference, unregistering it does nothing
         items
-            .map { it.sharedPrefFile }
+            .map { chooseSharedPreferences(it) }
             .distinct()
-            .forEach {
-                context
-                    .getSharedPreferences(it, Context.MODE_PRIVATE)
-                    .unregisterOnSharedPreferenceChangeListener(listener)
-            }
+            .forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
     }
 
     /**
@@ -174,10 +207,8 @@
      */
     fun has(vararg items: Item): Boolean {
         items
-            .groupBy { it.sharedPrefFile }
-            .forEach { (file, itemsSublist) ->
-                val prefs: SharedPreferences =
-                    context.getSharedPreferences(file, Context.MODE_PRIVATE)
+            .groupBy { chooseSharedPreferences(it) }
+            .forEach { (prefs, itemsSublist) ->
                 if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
             }
         return true
@@ -192,62 +223,128 @@
     fun removeSync(vararg items: Item) = prepareToRemove(items).forEach { it.commit() }
 
     /**
-     * Creates `SharedPreferences.Editor` transactions for removing all the provided [Item] values
-     * from their respective `SharedPreferences` files. These returned `Editors` can then be
-     * committed or applied for synchronous or async behavior.
+     * Removes the key value pairs stored in `SharedPreferences` for each corresponding Item. If the
+     * item is boot aware, this method removes the data from both the boot aware and encrypted
+     * files.
+     *
+     * @return a list of editors with all transactions added so that the caller can determine to use
+     * .apply() or .commit()
      */
-    private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> =
-        items
-            .groupBy { it.sharedPrefFile }
-            .map { (file, items) ->
-                context.getSharedPreferences(file, Context.MODE_PRIVATE).edit().also { editor ->
-                    items.forEach { item -> editor.remove(item.sharedPrefKey) }
-                }
+    private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
+        val itemsPerFile = items.groupBy { it.encryptedPrefs }.toMutableMap()
+
+        if (isBootAwareStartupDataEnabled) {
+            val bootAwareUpdates = items.filter { it.isBootAware }
+            if (bootAwareUpdates.isNotEmpty()) {
+                itemsPerFile[bootAwarePrefs] = bootAwareUpdates
             }
+        }
+
+        return itemsPerFile.map { (prefs, items) ->
+            prefs.edit().also { editor ->
+                items.forEach { item -> editor.remove(item.sharedPrefKey) }
+            }
+        }
+    }
+
+    fun migrateStartupDataToDeviceProtectedStorage() {
+        if (!isBootAwareStartupDataEnabled) return
+
+        Log.d(
+            TAG,
+            "Migrating data to unencrypted shared preferences to enable preloading " +
+                "while the user is locked the next time the device reboots."
+        )
+
+        with(bootAwarePrefs.edit()) {
+            BOOT_AWARE_ITEMS.forEach { putValue(it, get(it)) }
+            putBoolean(IS_STARTUP_DATA_MIGRATED.sharedPrefKey, true)
+            apply()
+        }
+    }
 
     companion object {
+        private const val TAG = "LauncherPrefs"
+        @VisibleForTesting const val BOOT_AWARE_PREFS_KEY = "boot_aware_prefs"
+
         @JvmField var INSTANCE = MainThreadInitializedObject { LauncherPrefs(it) }
 
         @JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
 
-        @JvmField val ICON_STATE = nonRestorableItem(LauncherAppState.KEY_ICON_STATE, "")
-        @JvmField val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false)
+        @JvmField val ICON_STATE = nonRestorableItem(LauncherAppState.KEY_ICON_STATE, "", true)
+        @JvmField val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false, true)
         @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
         @JvmField val WORK_EDU_STEP = backedUpItem(WorkProfileManager.KEY_WORK_EDU_STEP, 0)
-        @JvmField val WORKSPACE_SIZE = backedUpItem(DeviceGridState.KEY_WORKSPACE_SIZE, "")
-        @JvmField val HOTSEAT_COUNT = backedUpItem(DeviceGridState.KEY_HOTSEAT_COUNT, -1)
+        @JvmField val WORKSPACE_SIZE = backedUpItem(DeviceGridState.KEY_WORKSPACE_SIZE, "", true)
+        @JvmField val HOTSEAT_COUNT = backedUpItem(DeviceGridState.KEY_HOTSEAT_COUNT, -1, true)
         @JvmField
         val DEVICE_TYPE =
-            backedUpItem(DeviceGridState.KEY_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE)
-        @JvmField val DB_FILE = backedUpItem(DeviceGridState.KEY_DB_FILE, "")
+            backedUpItem(DeviceGridState.KEY_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE, true)
+        @JvmField val DB_FILE = backedUpItem(DeviceGridState.KEY_DB_FILE, "", true)
         @JvmField
         val RESTORE_DEVICE =
-            backedUpItem(RestoreDbTask.RESTORED_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE)
+            backedUpItem(
+                RestoreDbTask.RESTORED_DEVICE_TYPE,
+                InvariantDeviceProfile.TYPE_PHONE,
+                true
+            )
         @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
         @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
-        @JvmField val GRID_NAME = ConstantItem("idp_grid_name", true, null, String::class.java)
+        @JvmField
+        val GRID_NAME =
+            ConstantItem(
+                "idp_grid_name",
+                isBackedUp = true,
+                defaultValue = null,
+                isBootAware = true,
+                type = String::class.java
+            )
         @JvmField
         val ALLOW_ROTATION =
             backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) {
                 RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
             }
+        @JvmField
+        val IS_STARTUP_DATA_MIGRATED =
+            ConstantItem(
+                "is_startup_data_boot_aware",
+                isBackedUp = false,
+                defaultValue = false,
+                isBootAware = true
+            )
 
         @VisibleForTesting
         @JvmStatic
-        fun <T> backedUpItem(sharedPrefKey: String, defaultValue: T): ConstantItem<T> =
-            ConstantItem(sharedPrefKey, true, defaultValue)
+        fun <T> backedUpItem(
+            sharedPrefKey: String,
+            defaultValue: T,
+            isBootAware: Boolean = false
+        ): ConstantItem<T> =
+            ConstantItem(sharedPrefKey, isBackedUp = true, defaultValue, isBootAware)
 
         @JvmStatic
         fun <T> backedUpItem(
             sharedPrefKey: String,
             type: Class<out T>,
+            isBootAware: Boolean = false,
             defaultValueFromContext: (c: Context) -> T
-        ): ContextualItem<T> = ContextualItem(sharedPrefKey, true, defaultValueFromContext, type)
+        ): ContextualItem<T> =
+            ContextualItem(
+                sharedPrefKey,
+                isBackedUp = true,
+                defaultValueFromContext,
+                isBootAware,
+                type
+            )
 
         @VisibleForTesting
         @JvmStatic
-        fun <T> nonRestorableItem(sharedPrefKey: String, defaultValue: T): ConstantItem<T> =
-            ConstantItem(sharedPrefKey, false, defaultValue)
+        fun <T> nonRestorableItem(
+            sharedPrefKey: String,
+            defaultValue: T,
+            isBootAware: Boolean = false
+        ): ConstantItem<T> =
+            ConstantItem(sharedPrefKey, isBackedUp = false, defaultValue, isBootAware)
 
         @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
         @JvmStatic
@@ -255,7 +352,7 @@
             // Use application context for shared preferences, so we use single cached instance
             return context.applicationContext.getSharedPreferences(
                 SHARED_PREFERENCES_KEY,
-                Context.MODE_PRIVATE
+                MODE_PRIVATE
             )
         }
 
@@ -265,16 +362,23 @@
             // Use application context for shared preferences, so we use a single cached instance
             return context.applicationContext.getSharedPreferences(
                 DEVICE_PREFERENCES_KEY,
-                Context.MODE_PRIVATE
+                MODE_PRIVATE
             )
         }
     }
 }
 
+// This is hard-coded to false for now until it is time to release this optimization. It is only
+// a var because the unit tests are setting this to true so they can run.
+@VisibleForTesting var isBootAwareStartupDataEnabled: Boolean = false
+
+private val BOOT_AWARE_ITEMS: MutableSet<ConstantItem<*>> = mutableSetOf()
+
 abstract class Item {
     abstract val sharedPrefKey: String
     abstract val isBackedUp: Boolean
     abstract val type: Class<*>
+    abstract val isBootAware: Boolean
     val sharedPrefFile: String
         get() = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY
 
@@ -285,14 +389,22 @@
     override val sharedPrefKey: String,
     override val isBackedUp: Boolean,
     val defaultValue: T,
+    override val isBootAware: Boolean,
     // The default value can be null. If so, the type needs to be explicitly stated, or else NPE
     override val type: Class<out T> = defaultValue!!::class.java
-) : Item()
+) : Item() {
+    init {
+        if (isBootAware && isBootAwareStartupDataEnabled) {
+            BOOT_AWARE_ITEMS.add(this)
+        }
+    }
+}
 
 data class ContextualItem<T>(
     override val sharedPrefKey: String,
     override val isBackedUp: Boolean,
     private val defaultSupplier: (c: Context) -> T,
+    override val isBootAware: Boolean,
     override val type: Class<out T>
 ) : Item() {
     private var default: T? = null
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 4430a94..54bf6a8 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -406,6 +406,8 @@
         Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
         setter.setFloat(getAppsViewProgressAlpha(), MultiPropertyFactory.MULTI_PROPERTY_VALUE,
                 hasAllAppsContent ? 1 : 0, allAppsFade);
+        setter.setFloat(getAppsViewPullbackAlpha(), MultiPropertyFactory.MULTI_PROPERTY_VALUE,
+                hasAllAppsContent ? 1 : 0, allAppsFade);
 
         boolean shouldProtectHeader =
                 ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS;
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index a7183fb..c98b60f 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -216,10 +216,6 @@
             "ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR", false,
             "Enables One Search box in Taskbar All Apps.");
 
-    public static final BooleanFlag ENABLE_TASKBAR_IN_OVERVIEW = getDebugFlag(270393449,
-            "ENABLE_TASKBAR_IN_OVERVIEW", true,
-            "Enables accessing the system Taskbar in overview.");
-
     public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE = getDebugFlag(270393906,
             "ENABLE_SPLIT_FROM_WORKSPACE", true,
             "Enable initiating split screen from workspace.");
@@ -251,10 +247,6 @@
             270394392, "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", false,
             "Enable option to launch search results using the new view container transitions");
 
-    public static final BooleanFlag TWO_PREDICTED_ROWS_ALL_APPS_SEARCH = getReleaseFlag(270394225,
-            "TWO_PREDICTED_ROWS_ALL_APPS_SEARCH", false,
-            "Use 2 rows of app predictions in All Apps search zero-state");
-
     public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = getReleaseFlag(
             270394468, "ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", true,
             "Enable option to show keyboard when going to all-apps");
@@ -358,10 +350,6 @@
             "Enable the ability to generate monochromatic icons, if it is not provided by the app"
     );
 
-    public static final BooleanFlag ENABLE_DREAM_TRANSITION = getDebugFlag(270396364,
-            "ENABLE_DREAM_TRANSITION", true,
-            "Enable the launcher transition when the device enters a dream");
-
     public static final BooleanFlag ENABLE_TASKBAR_EDU_TOOLTIP = getDebugFlag(270396268,
             "ENABLE_TASKBAR_EDU_TOOLTIP", true,
             "Enable the tooltip version of the Taskbar education flow.");
@@ -388,10 +376,6 @@
             "ENABLE_KEYBOARD_QUICK_SWITCH", false,
             "Enables keyboard quick switching");
 
-    public static final BooleanFlag ENABLE_SMARTSPACE_DEFAULT_DATE_REMOVED = getDebugFlag(269761613,
-            "ENABLE_SMARTSPACE_DEFAULT_DATE_REMOVED", false,
-            "Enables remove smartspace default date");
-
     public static class BooleanFlag {
 
         private final boolean mCurrentValue;
@@ -404,4 +388,20 @@
             return mCurrentValue;
         }
     }
+
+    /**
+     * Class representing an integer flag
+     */
+    public static class IntFlag {
+
+        private final int mCurrentValue;
+
+        public IntFlag(int currentValue) {
+            mCurrentValue = currentValue;
+        }
+
+        public int get() {
+            return mCurrentValue;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/statemanager/BaseState.java b/src/com/android/launcher3/statemanager/BaseState.java
index 2390425..a01d402 100644
--- a/src/com/android/launcher3/statemanager/BaseState.java
+++ b/src/com/android/launcher3/statemanager/BaseState.java
@@ -70,4 +70,12 @@
     default boolean showTaskThumbnailSplash() {
         return false;
     }
+
+    /**
+     * For this state, whether member variables and other forms of data state should be preserved
+     * or wiped when the state is reapplied. (See {@link StateManager#reapplyState()})
+     */
+    default boolean shouldPreserveDataStateOnReapply() {
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 34ac8c2..89d89d6 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -184,6 +184,13 @@
     public void reapplyState(boolean cancelCurrentAnimation) {
         boolean wasInAnimation = mConfig.currentAnimation != null;
         if (cancelCurrentAnimation) {
+            // Animation canceling can trigger a cleanup routine, causing problems when we are in a
+            // launcher state that relies on member variable data. So if we are in one of those
+            // states, accelerate the current animation to its end point rather than canceling it
+            // outright.
+            if (mState.shouldPreserveDataStateOnReapply() && mConfig.currentAnimation != null) {
+                mConfig.currentAnimation.end();
+            }
             mAtomicAnimationFactory.cancelAllStateElementAnimation();
             cancelAnimation();
         }
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index cf470f4..c356da9 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -448,7 +448,7 @@
 
     @Override
     public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
-            int splitInstructionsWidth, int threeButtonNavShift) {
+            int splitInstructionsWidth) {
         out.setPivotX(0);
         out.setPivotY(splitInstructionsHeight);
         out.setRotation(getDegreesRotated());
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 6234462..39ef129 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -142,7 +142,7 @@
      * @param splitInstructionsWidth  The SplitInstructionView's width.
      */
     void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
-            int splitInstructionsWidth, int threeButtonNavShift);
+            int splitInstructionsWidth);
 
     /**
      * @param splitDividerSize height of split screen drag handle in portrait, width in landscape
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 75378f6..628aa9a 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -27,7 +27,6 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
-import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
@@ -51,8 +50,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
@@ -517,57 +514,29 @@
 
     @Override
     public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
-            int splitInstructionsWidth, int threeButtonNavShift) {
+            int splitInstructionsWidth) {
         out.setPivotX(0);
         out.setPivotY(splitInstructionsHeight);
         out.setRotation(getDegreesRotated());
         int distanceToEdge;
-        if ((DisplayController.getNavigationMode(out.getContext()) == THREE_BUTTONS)
-                && (dp.isTwoPanels || dp.isTablet)
-                // If taskbar is in overview, overview action has dedicated space above nav buttons
-                && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
-            // If 3-button nav is active, align the splitInstructionsView with it.
-            distanceToEdge = dp.getTaskbarOffsetY()
-                    + ((dp.taskbarSize - splitInstructionsHeight) / 2);
-        } else {
-            // If 3-button nav is not active, set bottom margin according to spec.
-            if (dp.isPhone) {
-                if (dp.isLandscape) {
-                    distanceToEdge = out.getResources().getDimensionPixelSize(
-                            R.dimen.split_instructions_bottom_margin_phone_landscape);
-                } else {
-                    distanceToEdge = out.getResources().getDimensionPixelSize(
-                            R.dimen.split_instructions_bottom_margin_phone_portrait);
-                }
-            } else if (dp.isTwoPanels) {
-                if (dp.isLandscape) {
-                    distanceToEdge = out.getResources().getDimensionPixelSize(
-                            R.dimen.split_instructions_bottom_margin_twopanels_landscape);
-                } else {
-                    distanceToEdge = out.getResources().getDimensionPixelSize(
-                            R.dimen.split_instructions_bottom_margin_twopanels_portrait);
-                }
+        if (dp.isPhone) {
+            if (dp.isLandscape) {
+                distanceToEdge = out.getResources().getDimensionPixelSize(
+                        R.dimen.split_instructions_bottom_margin_phone_landscape);
             } else {
-                if (dp.isLandscape) {
-                    distanceToEdge = out.getResources().getDimensionPixelSize(
-                            R.dimen.split_instructions_bottom_margin_tablet_landscape);
-                } else {
-                    distanceToEdge = out.getResources().getDimensionPixelSize(
-                            R.dimen.split_instructions_bottom_margin_tablet_portrait);
-                }
+                distanceToEdge = out.getResources().getDimensionPixelSize(
+                        R.dimen.split_instructions_bottom_margin_phone_portrait);
             }
+        } else {
+            distanceToEdge = dp.getOverviewActionsClaimedSpaceBelow();
         }
 
         // Center the view in case of unbalanced insets on left or right of screen
         int insetCorrectionX = (dp.getInsets().right - dp.getInsets().left) / 2;
         // Adjust for any insets on the bottom edge
         int insetCorrectionY = dp.getInsets().bottom;
-        // Adjust for taskbar in overview
-        int taskbarCorrectionY =
-                dp.isTaskbarPresent && FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()
-                        ? dp.taskbarSize : 0;
-        out.setTranslationX(insetCorrectionX + threeButtonNavShift);
-        out.setTranslationY(-distanceToEdge + insetCorrectionY - taskbarCorrectionY);
+        out.setTranslationX(insetCorrectionX);
+        out.setTranslationY(-distanceToEdge + insetCorrectionY);
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
         lp.gravity = CENTER_HORIZONTAL | BOTTOM;
         out.setLayoutParams(lp);
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 3363443..ec01231 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -184,7 +184,7 @@
 
     @Override
     public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
-            int splitInstructionsWidth, int threeButtonNavShift) {
+            int splitInstructionsWidth) {
         out.setPivotX(0);
         out.setPivotY(splitInstructionsHeight);
         out.setRotation(getDegreesRotated());
diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt
index 7b49583..f5e13d2 100644
--- a/src/com/android/launcher3/util/LockedUserState.kt
+++ b/src/com/android/launcher3/util/LockedUserState.kt
@@ -50,7 +50,9 @@
     }
 
     companion object {
-        @VisibleForTesting val INSTANCE = MainThreadInitializedObject { LockedUserState(it) }
+        @VisibleForTesting
+        @JvmField
+        val INSTANCE = MainThreadInitializedObject { LockedUserState(it) }
 
         @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context)
     }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/flags/FlagsFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/flags/FlagsFactory.java
index 599969b..4463adc 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/flags/FlagsFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/flags/FlagsFactory.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.uioverrides.flags;
 
 import com.android.launcher3.config.FeatureFlags.BooleanFlag;
+import com.android.launcher3.config.FeatureFlags.IntFlag;
 
 import java.io.PrintWriter;
 
@@ -43,6 +44,14 @@
     }
 
     /**
+     * Creates a new integer flag. Integer flags are always release flags
+     */
+    public static IntFlag getIntFlag(
+            int bugId, String key, int defaultValueInCode, String description) {
+        return new IntFlag(defaultValueInCode);
+    }
+
+    /**
      * Dumps the current flags state to the print writer
      */
     public static void dump(PrintWriter pw) { }
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index cc6fa33..c69ec2c 100644
--- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -155,6 +155,7 @@
     public static final String TASKBAR_IN_APP_STATE = "b/227657604";
     public static final String NPE_TRANSIENT_TASKBAR = "b/257549303";
     public static final String FLAKY_BINDING = "b/270216650";
+    public static final String VIEW_AND_ACTIVITY_LEAKS = "b/260260325";
 
     public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
     public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display";
diff --git a/tests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/src/com/android/launcher3/LauncherPrefsTest.kt
index e8372b1..41ef3de 100644
--- a/tests/src/com/android/launcher3/LauncherPrefsTest.kt
+++ b/tests/src/com/android/launcher3/LauncherPrefsTest.kt
@@ -1,9 +1,27 @@
+/*
+ * 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
 
+import android.content.Context
+import android.content.SharedPreferences
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.LauncherPrefs.Companion.BOOT_AWARE_PREFS_KEY
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
@@ -13,19 +31,22 @@
 private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false)
 private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)")
 private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1)
-private val TEST_CONTEXTUAL_ITEM = ContextualItem("4", true, { true }, Boolean::class.java)
+private val TEST_CONTEXTUAL_ITEM = ContextualItem("4", true, { true }, false, Boolean::class.java)
 
 private const val TEST_DEFAULT_VALUE = "default"
 private const val TEST_PREF_KEY = "test_pref_key"
 
+private const val WAIT_TIME_IN_SECONDS = 3L
+
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class LauncherPrefsTest {
 
-    private val launcherPrefs by lazy {
-        LauncherPrefs.get(InstrumentationRegistry.getInstrumentation().targetContext).apply {
-            remove(TEST_BOOLEAN_ITEM, TEST_STRING_ITEM, TEST_INT_ITEM)
-        }
+    private val context by lazy { InstrumentationRegistry.getInstrumentation().targetContext }
+    private val launcherPrefs by lazy { LauncherPrefs.get(context) }
+
+    init {
+        isBootAwareStartupDataEnabled = true
     }
 
     @Test
@@ -52,7 +73,7 @@
             addListener(listener, TEST_STRING_ITEM)
             putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"))
 
-            assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
+            assertThat(latch.await(WAIT_TIME_IN_SECONDS, TimeUnit.SECONDS)).isTrue()
             remove(TEST_STRING_ITEM)
         }
     }
@@ -68,7 +89,7 @@
             putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "hello."))
 
             // latch will be still be 1 (and await will return false) if the listener was not called
-            assertThat(latch.await(2, TimeUnit.SECONDS)).isFalse()
+            assertThat(latch.await(WAIT_TIME_IN_SECONDS, TimeUnit.SECONDS)).isFalse()
             remove(TEST_STRING_ITEM)
         }
     }
@@ -85,7 +106,7 @@
                 TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"),
                 TEST_BOOLEAN_ITEM.to(!TEST_BOOLEAN_ITEM.defaultValue)
             )
-            assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
+            assertThat(latch.await(WAIT_TIME_IN_SECONDS, TimeUnit.SECONDS)).isTrue()
 
             removeListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
             latch = CountDownLatch(1)
@@ -96,7 +117,7 @@
             )
             remove(TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
 
-            assertThat(latch.await(2, TimeUnit.SECONDS)).isFalse()
+            assertThat(latch.await(WAIT_TIME_IN_SECONDS, TimeUnit.SECONDS)).isFalse()
         }
     }
 
@@ -166,4 +187,133 @@
         val backedUpItem = LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE)
         assertThat(backedUpItem.sharedPrefFile).isEqualTo(LauncherFiles.SHARED_PREFERENCES_KEY)
     }
+
+    @Test
+    fun put_bootAwareItem_updatesDeviceProtectedStorage() {
+        val bootAwareItem =
+            LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
+
+        val bootAwarePrefs: SharedPreferences =
+            context
+                .createDeviceProtectedStorageContext()
+                .getSharedPreferences(BOOT_AWARE_PREFS_KEY, Context.MODE_PRIVATE)
+        bootAwarePrefs.edit().remove(bootAwareItem.sharedPrefKey).commit()
+
+        launcherPrefs.putSync(bootAwareItem.to(bootAwareItem.defaultValue))
+        assertThat(bootAwarePrefs.contains(bootAwareItem.sharedPrefKey)).isTrue()
+
+        launcherPrefs.removeSync(bootAwareItem)
+    }
+
+    @Test
+    fun put_bootAwareItem_updatesEncryptedStorage() {
+        val bootAwareItem =
+            LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
+
+        val encryptedPrefs: SharedPreferences =
+            context.getSharedPreferences(bootAwareItem.sharedPrefFile, Context.MODE_PRIVATE)
+        encryptedPrefs.edit().remove(bootAwareItem.sharedPrefKey).commit()
+
+        launcherPrefs.putSync(bootAwareItem.to(TEST_STRING_ITEM.defaultValue))
+        assertThat(encryptedPrefs.contains(bootAwareItem.sharedPrefKey)).isTrue()
+
+        launcherPrefs.removeSync(bootAwareItem)
+    }
+
+    @Test
+    fun remove_bootAwareItem_removesFromDeviceProtectedStorage() {
+        val bootAwareItem =
+            LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
+
+        val bootAwarePrefs: SharedPreferences =
+            context
+                .createDeviceProtectedStorageContext()
+                .getSharedPreferences(BOOT_AWARE_PREFS_KEY, Context.MODE_PRIVATE)
+
+        bootAwarePrefs
+            .edit()
+            .putString(bootAwareItem.sharedPrefKey, bootAwareItem.defaultValue)
+            .commit()
+
+        launcherPrefs.removeSync(bootAwareItem)
+        assertThat(bootAwarePrefs.contains(bootAwareItem.sharedPrefKey)).isFalse()
+    }
+
+    @Test
+    fun remove_bootAwareItem_removesFromEncryptedStorage() {
+        val bootAwareItem =
+            LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
+
+        val encryptedPrefs: SharedPreferences =
+            context.getSharedPreferences(bootAwareItem.sharedPrefFile, Context.MODE_PRIVATE)
+
+        encryptedPrefs
+            .edit()
+            .putString(bootAwareItem.sharedPrefKey, bootAwareItem.defaultValue)
+            .commit()
+
+        launcherPrefs.removeSync(bootAwareItem)
+        assertThat(encryptedPrefs.contains(bootAwareItem.sharedPrefKey)).isFalse()
+    }
+
+    @Test
+    fun migrate_bootAwareItemsToDeviceProtectedStorage_worksAsIntended() {
+        val bootAwareItem =
+            LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
+        launcherPrefs.removeSync(bootAwareItem)
+
+        val bootAwarePrefs: SharedPreferences =
+            context
+                .createDeviceProtectedStorageContext()
+                .getSharedPreferences(BOOT_AWARE_PREFS_KEY, Context.MODE_PRIVATE)
+
+        if (bootAwarePrefs.contains(bootAwareItem.sharedPrefKey)) {
+            bootAwarePrefs.edit().remove(bootAwareItem.sharedPrefKey).commit()
+        }
+
+        val encryptedPrefs: SharedPreferences =
+            context.getSharedPreferences(bootAwareItem.sharedPrefFile, Context.MODE_PRIVATE)
+
+        encryptedPrefs
+            .edit()
+            .putString(bootAwareItem.sharedPrefKey, bootAwareItem.defaultValue)
+            .commit()
+
+        launcherPrefs.migrateStartupDataToDeviceProtectedStorage()
+        assertThat(bootAwarePrefs.contains(bootAwareItem.sharedPrefKey)).isTrue()
+
+        launcherPrefs.removeSync(bootAwareItem)
+    }
+
+    @Test
+    fun migrate_onlyEncryptedItemsToDeviceProtectedStorage_doesNotHappen() {
+        val onlyEncryptedItem =
+            LauncherPrefs.backedUpItem(
+                TEST_PREF_KEY + "_",
+                TEST_DEFAULT_VALUE + "_",
+                isBootAware = false
+            )
+
+        val bootAwarePrefs: SharedPreferences =
+            context
+                .createDeviceProtectedStorageContext()
+                .getSharedPreferences(BOOT_AWARE_PREFS_KEY, Context.MODE_PRIVATE)
+
+        if (bootAwarePrefs.contains(onlyEncryptedItem.sharedPrefKey)) {
+            bootAwarePrefs.edit().remove(onlyEncryptedItem.sharedPrefKey).commit()
+        }
+
+        val encryptedPrefs: SharedPreferences =
+            context.getSharedPreferences(onlyEncryptedItem.sharedPrefFile, Context.MODE_PRIVATE)
+
+        encryptedPrefs
+            .edit()
+            .putString(onlyEncryptedItem.sharedPrefKey, onlyEncryptedItem.defaultValue)
+            .commit()
+
+        launcherPrefs.migrateStartupDataToDeviceProtectedStorage()
+        assertThat(bootAwarePrefs.contains(onlyEncryptedItem.sharedPrefKey)).isFalse()
+
+        encryptedPrefs.edit().remove(onlyEncryptedItem.sharedPrefKey).commit()
+    }
 }
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index fa4c519..545b645 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -510,7 +510,7 @@
                     UserCache.INSTANCE, InstallSessionHelper.INSTANCE, LauncherPrefs.INSTANCE,
                     LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
                     DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
-                    SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE,
+                    SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE, LockedUserState.INSTANCE,
                     ItemInstallQueue.INSTANCE, WindowManagerProxy.INSTANCE);
             mPm = spy(getBaseContext().getPackageManager());
             mDbDir = new File(getCacheDir(), UUID.randomUUID().toString());