Merge "Add flag for smartspace removal toggle" into main
diff --git a/OWNERS b/OWNERS
index 7834396..353ac8e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -15,6 +15,7 @@
 alexchau@google.com
 patmanning@google.com
 tsuharesu@google.com
+awickham@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, captaincole@google.com
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index ce644dc..cca0fd4 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -56,6 +56,7 @@
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
 import static com.android.launcher3.util.DisplayController.isTransientTaskbar;
+import static com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR;
 import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
 import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
@@ -78,6 +79,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Point;
@@ -92,6 +94,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.util.Pair;
 import android.util.Size;
 import android.view.CrossWindowBlurListeners;
@@ -231,6 +234,16 @@
 
     private final StartingWindowListener mStartingWindowListener =
             new StartingWindowListener(this);
+    private ContentObserver mAnimationRemovalObserver = new ContentObserver(
+            ORDERED_BG_EXECUTOR.getHandler()) {
+        @Override
+        public void onChange(boolean selfChange) {
+            mAreAnimationsEnabled = Global.getFloat(mLauncher.getContentResolver(),
+                    Global.ANIMATOR_DURATION_SCALE, 1f) > 0
+                    || Global.getFloat(mLauncher.getContentResolver(),
+                    Global.TRANSITION_ANIMATION_SCALE, 1f) > 0;
+        }
+    };;
 
     private DeviceProfile mDeviceProfile;
 
@@ -260,6 +273,7 @@
     // Pairs of window starting type and starting window background color for starting tasks
     // Will never be larger than MAX_NUM_TASKS
     private LinkedHashMap<Integer, Pair<Integer, Integer>> mTaskStartParams;
+    private boolean mAreAnimationsEnabled = true;
 
     private final Interpolator mOpeningXInterpolator;
     private final Interpolator mOpeningInterpolator;
@@ -270,6 +284,7 @@
         mHandler = new Handler(Looper.getMainLooper());
         mDeviceProfile = mLauncher.getDeviceProfile();
         mBackAnimationController = new LauncherBackAnimationController(mLauncher, this);
+        checkAndMonitorIfAnimationsAreEnabled();
 
         Resources res = mLauncher.getResources();
         mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
@@ -1160,6 +1175,8 @@
         unregisterRemoteAnimations();
         unregisterRemoteTransitions();
         SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null);
+        ORDERED_BG_EXECUTOR.execute(() -> mLauncher.getContentResolver()
+                .unregisterContentObserver(mAnimationRemovalObserver));
     }
 
     private void unregisterRemoteAnimations() {
@@ -1197,6 +1214,17 @@
         }
     }
 
+    private void checkAndMonitorIfAnimationsAreEnabled() {
+        ORDERED_BG_EXECUTOR.execute(() -> {
+            mAnimationRemovalObserver.onChange(true);
+            mLauncher.getContentResolver().registerContentObserver(Global.getUriFor(
+                    Global.ANIMATOR_DURATION_SCALE), false, mAnimationRemovalObserver);
+            mLauncher.getContentResolver().registerContentObserver(Global.getUriFor(
+                    Global.TRANSITION_ANIMATION_SCALE), false, mAnimationRemovalObserver);
+
+        });
+    }
+
     private boolean launcherIsATargetWithMode(RemoteAnimationTarget[] targets, int mode) {
         for (RemoteAnimationTarget target : targets) {
             if (target.mode == mode && target.taskInfo != null
@@ -1375,7 +1403,7 @@
                     (LauncherAppWidgetHostView) launcherView, targetRect, windowSize,
                     mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher),
                     isTransluscent, fallbackBackgroundColor);
-        } else if (launcherView != null) {
+        } else if (launcherView != null && mAreAnimationsEnabled) {
             floatingIconView = getFloatingIconView(mLauncher, launcherView, null,
                     mLauncher.getTaskbarUIController() == null
                             ? null
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
index 9758d44..7529508 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -46,7 +46,7 @@
         protected val startContextualContainer: ViewGroup,
         protected val imeSwitcher: ImageView?,
         protected val rotationButton: RotationButton?,
-        protected val a11yButton: ImageView
+        protected val a11yButton: ImageView?
 ) : NavButtonLayoutter {
     protected val homeButton: ImageView? = navButtonContainer.findViewById(R.id.home)
     protected val recentsButton: ImageView? = navButtonContainer.findViewById(R.id.recent_apps)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
index f254ee8..e7847f3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
@@ -35,7 +35,7 @@
         startContextualContainer: ViewGroup,
         imeSwitcher: ImageView?,
         rotationButton: RotationButton?,
-        a11yButton: ImageView
+        a11yButton: ImageView?
 ) :
     AbstractNavButtonLayoutter(
             resources,
@@ -114,7 +114,9 @@
             startContextualContainer.addView(imeSwitcher)
             imeSwitcher.layoutParams = getParamsToCenterView()
         }
-        endContextualContainer.addView(a11yButton)
+        if (a11yButton != null) {
+            endContextualContainer.addView(a11yButton)
+        }
         if (rotationButton != null) {
             endContextualContainer.addView(rotationButton.currentView)
             rotationButton.currentView.layoutParams = getParamsToCenterView()
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
index 7db1a37..c502cdb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -58,7 +58,7 @@
                 navButtonsView: FrameLayout,
                 imeSwitcher: ImageView?,
                 rotationButton: RotationButton?,
-                a11yButton: ImageView,
+                a11yButton: ImageView?,
                 resources: Resources,
                 isKidsMode: Boolean,
                 isInSetup: Boolean,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
index c1dae40..d6ea7f0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
@@ -31,7 +31,7 @@
         startContextualContainer: ViewGroup,
         imeSwitcher: ImageView?,
         rotationButton: RotationButton?,
-        a11yButton: ImageView
+        a11yButton: ImageView?
 ) :
         AbstractNavButtonLayoutter(
                 resources,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
index efe9d4b..54edd21 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
@@ -36,7 +36,7 @@
         startContextualContainer: ViewGroup,
         imeSwitcher: ImageView?,
         rotationButton: RotationButton?,
-        a11yButton: ImageView,
+        a11yButton: ImageView?,
 ) :
     AbstractNavButtonLayoutter(
             resources,
@@ -113,7 +113,9 @@
             startContextualContainer.addView(imeSwitcher)
             imeSwitcher.layoutParams = getParamsToCenterView()
         }
-        startContextualContainer.addView(a11yButton)
+        if (a11yButton != null) {
+            startContextualContainer.addView(a11yButton)
+        }
         if (rotationButton != null) {
             startContextualContainer.addView(rotationButton.currentView)
             rotationButton.currentView.layoutParams = getParamsToCenterView()
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
index ad03e5b..34df282 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
@@ -35,7 +35,7 @@
         startContextualContainer: ViewGroup,
         imeSwitcher: ImageView?,
         rotationButton: RotationButton?,
-        a11yButton: ImageView,
+        a11yButton: ImageView?,
 ) :
     AbstractNavButtonLayoutter(
             resources,
@@ -111,7 +111,9 @@
             endContextualContainer.addView(imeSwitcher)
             imeSwitcher.layoutParams = getParamsToCenterView()
         }
-        endContextualContainer.addView(a11yButton)
+        if (a11yButton != null) {
+            endContextualContainer.addView(a11yButton)
+        }
         if (rotationButton != null) {
             endContextualContainer.addView(rotationButton.currentView)
             rotationButton.currentView.layoutParams = getParamsToCenterView()
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
index fd6e991..cfe1276 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
@@ -31,7 +31,7 @@
         startContextualContainer: ViewGroup,
         imeSwitcher: ImageView?,
         rotationButton: RotationButton?,
-        a11yButton: ImageView
+        a11yButton: ImageView?
 ) :
         PhoneLandscapeNavLayoutter(
                 resources,
@@ -63,7 +63,9 @@
             endContextualContainer.addView(imeSwitcher)
             imeSwitcher.layoutParams = getParamsToCenterView()
         }
-        endContextualContainer.addView(a11yButton)
+        if (a11yButton != null) {
+            endContextualContainer.addView(a11yButton)
+        }
         if (rotationButton != null) {
             endContextualContainer.addView(rotationButton.currentView)
             rotationButton.currentView.layoutParams = getParamsToCenterView()
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
index db245b8..36f5150 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -32,7 +32,7 @@
         startContextualContainer: ViewGroup,
         imeSwitcher: ImageView?,
         rotationButton: RotationButton?,
-        a11yButton: ImageView
+        a11yButton: ImageView?
 ) :
     AbstractNavButtonLayoutter(
             resources,
@@ -72,7 +72,9 @@
             startContextualContainer.addView(imeSwitcher)
             imeSwitcher.layoutParams = getParamsToCenterView()
         }
-        endContextualContainer.addView(a11yButton)
+        if (a11yButton != null) {
+            endContextualContainer.addView(a11yButton)
+        }
         if (rotationButton != null) {
             endContextualContainer.addView(rotationButton.currentView)
             rotationButton.currentView.layoutParams = getParamsToCenterView()
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
index 56e55bb..58dce69 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -26,7 +26,10 @@
 import com.android.launcher3.R
 import com.android.systemui.shared.rotation.RotationButton
 
-/** Layoutter for showing 3 button navigation on large screen */
+/**
+ * Layoutter for rendering task bar in large screen. {@param isContextualButtonShowing} is true in
+ * 3-button mode.
+ */
 class TaskbarNavLayoutter(
         resources: Resources,
         navBarContainer: LinearLayout,
@@ -34,7 +37,7 @@
         startContextualContainer: ViewGroup,
         imeSwitcher: ImageView?,
         rotationButton: RotationButton?,
-        a11yButton: ImageView
+        a11yButton: ImageView?
 ) :
     AbstractNavButtonLayoutter(
             resources,
@@ -89,24 +92,28 @@
         endContextualContainer.removeAllViews()
         startContextualContainer.removeAllViews()
 
-        val endContextualContainerParams = FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
-        endContextualContainerParams.gravity = Gravity.END or Gravity.CENTER_VERTICAL
-        endContextualContainer.layoutParams = endContextualContainerParams
+        if (isContextualButtonShowing) {
+            val endContextualContainerParams = FrameLayout.LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
+            endContextualContainerParams.gravity = Gravity.END or Gravity.CENTER_VERTICAL
+            endContextualContainer.layoutParams = endContextualContainerParams
 
-        val startContextualContainerParams = FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
-        startContextualContainerParams.gravity = Gravity.START or Gravity.CENTER_VERTICAL
-        startContextualContainer.layoutParams = startContextualContainerParams
+            val startContextualContainerParams = FrameLayout.LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
+            startContextualContainerParams.gravity = Gravity.START or Gravity.CENTER_VERTICAL
+            startContextualContainer.layoutParams = startContextualContainerParams
 
-        if (imeSwitcher != null) {
-            startContextualContainer.addView(imeSwitcher)
-            imeSwitcher.layoutParams = getParamsToCenterView()
-        }
-        endContextualContainer.addView(a11yButton)
-        if (rotationButton != null) {
-            endContextualContainer.addView(rotationButton.currentView)
-            rotationButton.currentView.layoutParams = getParamsToCenterView()
+            if (imeSwitcher != null) {
+                startContextualContainer.addView(imeSwitcher)
+                imeSwitcher.layoutParams = getParamsToCenterView()
+            }
+            if (a11yButton != null) {
+                endContextualContainer.addView(a11yButton)
+            }
+            if (rotationButton != null) {
+                endContextualContainer.addView(rotationButton.currentView)
+                rotationButton.currentView.layoutParams = getParamsToCenterView()
+            }
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 1b22c84..289041a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -186,6 +186,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
@@ -218,8 +219,6 @@
     private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController;
     private SplitToWorkspaceController mSplitToWorkspaceController;
 
-    private AsyncClockEventDelegate mAsyncClockEventDelegate;
-
     /**
      * If Launcher restarted while in the middle of an Overview split select, it needs this data to
      * recover. In all other cases this will remain null.
@@ -497,10 +496,6 @@
             mSplitSelectStateController.onDestroy();
         }
 
-        if (mAsyncClockEventDelegate != null) {
-            mAsyncClockEventDelegate.onDestroy();
-        }
-
         super.onDestroy();
         mHotseatPredictionController.destroy();
         mSplitWithKeyboardShortcutController.onDestroy();
@@ -675,6 +670,9 @@
         floatingTaskView.setAlpha(1);
         floatingTaskView.addStagingAnimation(anim, startingTaskRect, tempRect,
                 false /* fadeWithThumbnail */, true /* isStagedTask */);
+        floatingTaskView.setOnClickListener(view ->
+                mSplitSelectStateController.getSplitAnimationController().
+                        playAnimPlaceholderToFullscreen(this, view, Optional.empty()));
         mSplitSelectStateController.setFirstFloatingTaskView(floatingTaskView);
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -714,7 +712,8 @@
 
         if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
             // If Launcher pauses before both split apps are selected, exit split screen.
-            if (!mSplitSelectStateController.isBothSplitAppsConfirmed()) {
+            if (!mSplitSelectStateController.isBothSplitAppsConfirmed() &&
+                    !mSplitSelectStateController.isLaunchingFirstAppFullscreen()) {
                 mSplitSelectStateController.getSplitAnimationController()
                         .playPlaceholderDismissAnim(this);
             }
@@ -1346,18 +1345,12 @@
         switch (name) {
             case "TextClock", "android.widget.TextClock" -> {
                 TextClock tc = new TextClock(context, attrs);
-                if (mAsyncClockEventDelegate == null) {
-                    mAsyncClockEventDelegate = new AsyncClockEventDelegate(this);
-                }
-                tc.setClockEventDelegate(mAsyncClockEventDelegate);
+                tc.setClockEventDelegate(AsyncClockEventDelegate.INSTANCE.get(this));
                 return tc;
             }
             case "AnalogClock", "android.widget.AnalogClock" -> {
                 AnalogClock ac = new AnalogClock(context, attrs);
-                if (mAsyncClockEventDelegate == null) {
-                    mAsyncClockEventDelegate = new AsyncClockEventDelegate(this);
-                }
-                ac.setClockEventDelegate(mAsyncClockEventDelegate);
+                ac.setClockEventDelegate(AsyncClockEventDelegate.INSTANCE.get(this));
                 return ac;
             }
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
index a68e753..6279f63 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
@@ -116,8 +116,9 @@
         boolean defaultValue = DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, defaultValueInCode);
         if (IS_DEBUG_DEVICE) {
             boolean currentValue = getSharedPreferences().getBoolean(key, defaultValue);
-            DebugFlag flag = new DeviceFlag(key, description, flagState, currentValue,
-                    defaultValueInCode);
+            DebugFlag flag = new DeviceFlag(key, description,
+                    (defaultValue == defaultValueInCode) ? flagState
+                            : defaultValue ? ENABLED : DISABLED, currentValue, defaultValueInCode);
             sDebugFlags.add(flag);
             return flag;
         } else {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 3e7d45e..1d55da3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -21,9 +21,9 @@
 import android.graphics.Rect;
 
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.Flags;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.quickstep.views.RecentsView;
 
 /**
@@ -72,7 +72,7 @@
 
     @Override
     public boolean isTaskbarStashed(Launcher launcher) {
-        if (FeatureFlags.enableGridOnlyOverview()) {
+        if (Flags.enableGridOnlyOverview()) {
             return true;
         }
         return super.isTaskbarStashed(launcher);
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 8925bd6..25389c5 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -50,10 +50,10 @@
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.statemanager.BaseState;
@@ -242,7 +242,7 @@
     public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect,
             PagedOrientationHandler orientedState) {
         if (dp.isTablet) {
-            if (FeatureFlags.enableGridOnlyOverview()) {
+            if (Flags.enableGridOnlyOverview()) {
                 calculateGridTaskSize(context, dp, outRect, orientedState);
             } else {
                 calculateFocusTaskSize(context, dp, outRect);
@@ -339,7 +339,7 @@
             PagedOrientationHandler orientedState) {
         Resources res = context.getResources();
         Rect potentialTaskRect = new Rect();
-        if (FeatureFlags.enableGridOnlyOverview()) {
+        if (Flags.enableGridOnlyOverview()) {
             calculateGridSize(dp, potentialTaskRect);
         } else {
             calculateFocusTaskSize(context, dp, potentialTaskRect);
@@ -371,7 +371,7 @@
     public final void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect,
             PagedOrientationHandler orientedState) {
         calculateTaskSize(context, dp, outRect, orientedState);
-        boolean isGridOnlyOverview = dp.isTablet && FeatureFlags.enableGridOnlyOverview();
+        boolean isGridOnlyOverview = dp.isTablet && Flags.enableGridOnlyOverview();
         int claimedSpaceBelow = isGridOnlyOverview
                 ? dp.overviewActionsTopMarginPx + dp.overviewActionsHeight + dp.stashedTaskbarHeight
                 : (dp.heightPx - outRect.bottom - dp.getInsets().bottom);
diff --git a/quickstep/src/com/android/quickstep/BootAwarePreloader.kt b/quickstep/src/com/android/quickstep/BootAwarePreloader.kt
index 35404a9..2fc4d04 100644
--- a/quickstep/src/com/android/quickstep/BootAwarePreloader.kt
+++ b/quickstep/src/com/android/quickstep/BootAwarePreloader.kt
@@ -19,7 +19,7 @@
 import android.util.Log
 import com.android.launcher3.LauncherAppState
 import com.android.launcher3.LauncherPrefs
-import com.android.launcher3.isBootAwareStartupDataEnabled
+import com.android.launcher3.moveStartupDataToDeviceProtectedStorageIsEnabled
 import com.android.launcher3.util.LockedUserState
 
 /**
@@ -33,7 +33,8 @@
     fun start(context: Context) {
         val lp = LauncherPrefs.get(context)
         when {
-            LockedUserState.get(context).isUserUnlocked || !isBootAwareStartupDataEnabled -> {
+            LockedUserState.get(context).isUserUnlocked ||
+                !moveStartupDataToDeviceProtectedStorageIsEnabled -> {
                 /* No-Op */
             }
             lp.isStartupDataMigrated -> {
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 6ee2cfd..3bc77ff 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -16,6 +16,8 @@
 
 package com.android.quickstep;
 
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
 import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
@@ -269,6 +271,7 @@
 
         TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
 
+        int numVisibleTasks = 0;
         for (GroupedRecentTaskInfo rawTask : rawTasks) {
             if (DESKTOP_MODE_SUPPORTED && rawTask.getType() == TYPE_FREEFORM) {
                 GroupTask desktopTask = createDesktopTask(rawTask);
@@ -285,12 +288,27 @@
             task1.setLastSnapshotData(taskInfo1);
             Task task2 = null;
             if (taskInfo2 != null) {
+                // Is split task
                 Task.TaskKey task2Key = new Task.TaskKey(taskInfo2);
                 task2 = loadKeysOnly
                         ? new Task(task2Key)
                         : Task.from(task2Key, taskInfo2,
                                 tmpLockedUsers.get(task2Key.userId) /* isLocked */);
                 task2.setLastSnapshotData(taskInfo2);
+            } else {
+                // Is fullscreen task
+                if (numVisibleTasks > 0) {
+                    boolean isExcluded = (taskInfo1.baseIntent.getFlags()
+                            & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
+                    if (taskInfo1.isTopActivityTransparent && isExcluded) {
+                        // If there are already visible tasks, then ignore the excluded tasks and
+                        // don't add them to the returned list
+                        continue;
+                    }
+                }
+            }
+            if (taskInfo1.isVisible) {
+                numVisibleTasks++;
             }
             final SplitConfigurationOptions.SplitBounds launcherSplitBounds =
                     convertShellSplitBoundsToLauncher(rawTask.getSplitBounds());
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 36a6eb6..e358a8e 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -17,7 +17,7 @@
 
 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 
-import static com.android.launcher3.config.FeatureFlags.enableGridOnlyOverview;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
 
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index bd3ccb7..7d03d77 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -34,8 +34,8 @@
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.Flags;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
@@ -80,7 +80,7 @@
         boolean isInLandscape = orientedState.getTouchRotation() != ROTATION_0;
         boolean isTablet = activity.getDeviceProfile().isTablet;
 
-        boolean isGridOnlyOverview = isTablet && FeatureFlags.enableGridOnlyOverview();
+        boolean isGridOnlyOverview = isTablet && Flags.enableGridOnlyOverview();
         // Add overview actions to the menu when in in-place rotate landscape mode, or in
         // grid-only overview.
         if ((!canLauncherRotate && isInLandscape) || isGridOnlyOverview) {
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index 2ca9f99..e5fca4b 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -15,7 +15,7 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.config.FeatureFlags.enableGridOnlyOverview;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
 
 import android.content.Context;
 import android.content.res.Resources;
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index 01baed3..f1af2ed 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -16,7 +16,6 @@
 package com.android.quickstep;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -229,12 +228,21 @@
         }
 
         /**
-         * Returns true if the given task holds an Assistant activity that is excluded from recents
+         * If the given task holds an activity that is excluded from recents, and there
+         * is another running task that is not excluded from recents, returns that underlying task.
          */
-        public boolean isExcludedAssistant() {
-            return mTopTask != null && mTopTask.configuration.windowConfiguration
-                    .getActivityType() == ACTIVITY_TYPE_ASSISTANT
-                    && (mTopTask.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
+        public @Nullable CachedTaskInfo otherVisibleTaskThisIsExcludedOver() {
+            if (mTopTask == null
+                    || (mTopTask.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0) {
+                // Not an excluded task.
+                return null;
+            }
+            List<RunningTaskInfo> visibleNonExcludedTasks = mAllCachedTasks.stream()
+                    .filter(t -> t.isVisible
+                            && (t.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0)
+                    .toList();
+            return visibleNonExcludedTasks.isEmpty() ? null
+                    : new CachedTaskInfo(visibleNonExcludedTasks);
         }
 
         /**
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 02fcc68..f840707 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -1079,13 +1079,19 @@
         boolean forceOverviewInputConsumer = gestureState.getActivityInterface().isStarted()
                 && gestureState.getRunningTask() != null
                 && gestureState.getRunningTask().isRootChooseActivity();
-        if (gestureState.getRunningTask() != null
-                && gestureState.getRunningTask().isExcludedAssistant()) {
-            // In the case where we are in the excluded assistant state, ignore it and treat the
-            // running activity as the task behind the assistant
-            gestureState.updateRunningTask(TopTaskTracker.INSTANCE.get(this)
-                    .getCachedTopTask(true /* filterOnlyVisibleRecents */));
-            forceOverviewInputConsumer = gestureState.getRunningTask().isHomeTask();
+
+        // In the case where we are in an excluded, translucent overlay, ignore it and treat the
+        // running activity as the task behind the overlay.
+        TopTaskTracker.CachedTaskInfo otherVisibleTask = gestureState.getRunningTask() == null
+                ? null
+                : gestureState.getRunningTask().otherVisibleTaskThisIsExcludedOver();
+        if (otherVisibleTask != null) {
+            ActiveGestureLog.INSTANCE.addLog(new CompoundString("Changing active task to ")
+                    .append(otherVisibleTask.getPackageName())
+                    .append(" because the previous task running on top of this one (")
+                    .append(gestureState.getRunningTask().getPackageName())
+                    .append(") was excluded from recents"));
+            gestureState.updateRunningTask(otherVisibleTask);
         }
 
         boolean previousGestureAnimatedToLauncher =
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 3388642..7d3a860 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -21,7 +21,9 @@
 import android.media.session.MediaSessionManager;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.View;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.Utilities;
@@ -52,6 +54,7 @@
     private final boolean mStartingInActivityBounds;
     private boolean mTargetHandledTouch;
     private boolean mHasSetTouchModeForFirstDPadEvent;
+    private boolean mIsWaitingForAttachToWindow;
 
     public OverviewInputConsumer(GestureState gestureState, T activity,
             @Nullable InputMonitorCompat inputMonitor, boolean startingInActivityBounds) {
@@ -118,21 +121,47 @@
                 break;
             case KeyEvent.KEYCODE_DPAD_LEFT:
             case KeyEvent.KEYCODE_DPAD_RIGHT:
-                if (!mHasSetTouchModeForFirstDPadEvent) {
-                    // When Overview is launched via meta+tab or swipe up from an app, the touch
-                    // mode somehow is not changed to false by the Android framework. The subsequent
-                    // key events (e.g. DPAD_LEFT, DPAD_RIGHT) can only be dispatched to focused
-                    // views, while focus can only be requested in
-                    // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To
-                    // note, here we launch overview with live tile.
-                    mHasSetTouchModeForFirstDPadEvent = true;
-                    mActivity.getRootView().getViewRootImpl().touchModeChanged(false);
+                if (mHasSetTouchModeForFirstDPadEvent) {
+                    break;
                 }
+                View viewRoot = mActivity.getRootView();
+                if (viewRoot.isAttachedToWindow()) {
+                    setTouchModeChanged(viewRoot);
+                    break;
+                }
+                if (mIsWaitingForAttachToWindow) {
+                    break;
+                }
+                mIsWaitingForAttachToWindow = true;
+                viewRoot.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+                    @Override
+                    public void onViewAttachedToWindow(View view) {
+                        view.removeOnAttachStateChangeListener(this);
+                        mIsWaitingForAttachToWindow = false;
+                        setTouchModeChanged(viewRoot);
+                    }
+
+                    @Override
+                    public void onViewDetachedFromWindow(View view) {
+                        // Do nothing
+                    }
+                });
                 break;
             default:
                 break;
         }
         mActivity.dispatchKeyEvent(ev);
     }
+
+    private void setTouchModeChanged(@NonNull View viewRoot) {
+        // When Overview is launched via meta+tab or swipe up from an app, the touch
+        // mode somehow is not changed to false by the Android framework. The
+        // subsequent key events (e.g. DPAD_LEFT, DPAD_RIGHT) can only be dispatched
+        // to focused views, while focus can only be requested in
+        // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To
+        // note, here we launch overview with live tile.
+        mHasSetTouchModeForFirstDPadEvent = true;
+        viewRoot.getViewRootImpl().touchModeChanged(false);
+    }
 }
 
diff --git a/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java b/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java
index 0dee5b3..cda87c0 100644
--- a/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java
+++ b/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java
@@ -32,6 +32,8 @@
 
 import androidx.annotation.WorkerThread;
 
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.SettingsCache.OnChangeListener;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -42,7 +44,11 @@
 /**
  * Extension of {@link ClockEventDelegate} to support async event registration
  */
-public class AsyncClockEventDelegate extends ClockEventDelegate implements OnChangeListener {
+public class AsyncClockEventDelegate extends ClockEventDelegate
+        implements OnChangeListener, SafeCloseable {
+
+    public static final MainThreadInitializedObject<AsyncClockEventDelegate> INSTANCE =
+            new MainThreadInitializedObject<>(AsyncClockEventDelegate::new);
 
     private final Context mContext;
     private final SimpleBroadcastReceiver mReceiver =
@@ -55,7 +61,7 @@
     private boolean mFormatRegistered = false;
     private boolean mDestroyed = false;
 
-    public AsyncClockEventDelegate(Context context) {
+    private AsyncClockEventDelegate(Context context) {
         super(context);
         mContext = context;
 
@@ -79,6 +85,9 @@
 
     @Override
     public void registerFormatChangeObserver(ContentObserver observer, int userHandle) {
+        if (mDestroyed) {
+            return;
+        }
         synchronized (mFormatObservers) {
             if (!mFormatRegistered && !mDestroyed) {
                 SettingsCache.INSTANCE.get(mContext).register(mFormatUri, this);
@@ -114,10 +123,8 @@
         }
     }
 
-    /**
-     * Unregisters all system callbacks and destroys this delegate
-     */
-    public void onDestroy() {
+    @Override
+    public void close() {
         mDestroyed = true;
         SettingsCache.INSTANCE.get(mContext).unregister(mFormatUri, this);
         UI_HELPER_EXECUTOR.execute(() -> mReceiver.unregisterReceiverSafely(mContext));
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index 689402b..ae497f0 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -30,6 +30,7 @@
 import com.android.launcher3.DeviceProfile
 import com.android.launcher3.Utilities
 import com.android.launcher3.anim.PendingAnimation
+import com.android.launcher3.config.FeatureFlags
 import com.android.launcher3.statemanager.StatefulActivity
 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource
 import com.android.launcher3.views.BaseDragLayer
@@ -40,6 +41,7 @@
 import com.android.quickstep.views.TaskView
 import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
 import com.android.quickstep.views.TaskViewIcon
+import java.util.Optional
 import java.util.function.Supplier
 
 /**
@@ -270,6 +272,45 @@
         safeRemoveViewFromDragLayer(launcher, splitSelectStateController.splitInstructionsView)
     }
 
+    /**
+     * Animates the first placeholder view to fullscreen and launches its task.
+     * TODO(b/276361926): Remove the [resetCallback] option once contextual launches
+     */
+    fun playAnimPlaceholderToFullscreen(launcher: StatefulActivity<*>, view: View,
+                                        resetCallback: Optional<Runnable>) {
+        val stagedTaskView = view as FloatingTaskView
+
+        val isTablet: Boolean = launcher.deviceProfile.isTablet
+        val duration = if (isTablet) SplitAnimationTimings.TABLET_CONFIRM_DURATION else
+            SplitAnimationTimings.PHONE_CONFIRM_DURATION
+        val pendingAnimation = PendingAnimation(duration.toLong())
+        val firstTaskStartingBounds = Rect()
+        val firstTaskEndingBounds = Rect()
+
+        stagedTaskView.getBoundsOnScreen(firstTaskStartingBounds)
+        launcher.dragLayer.getBoundsOnScreen(firstTaskEndingBounds)
+        splitSelectStateController.setLaunchingFirstAppFullscreen()
+
+        stagedTaskView.addConfirmAnimation(
+                pendingAnimation,
+                RectF(firstTaskStartingBounds),
+                firstTaskEndingBounds,
+                false /* fadeWithThumbnail */,
+                true /* isStagedTask */)
+
+        pendingAnimation.addEndListener {
+            splitSelectStateController.launchInitialAppFullscreen {
+                if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+                    splitSelectStateController.resetState()
+                } else if (resetCallback.isPresent) {
+                    resetCallback.get().run()
+                }
+            }
+        }
+
+        pendingAnimation.buildAnim().start()
+    }
+
     private fun safeRemoveViewFromDragLayer(launcher: StatefulActivity<*>, view: View?) {
         if (view != null) {
             launcher.dragLayer.removeView(view)
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index b9829f7..f67d7dc 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -140,6 +140,9 @@
     @Nullable
     private GroupedTaskView mLaunchingTaskView;
 
+    /** True when the first selected split app is being launched in fullscreen. */
+    private boolean mLaunchingFirstAppFullscreen;
+
     private FloatingTaskView mFirstFloatingTaskView;
     private SplitInstructionsView mSplitInstructionsView;
 
@@ -599,7 +602,7 @@
 
         private final int mInitialTaskId;
         private final int mSecondTaskId;
-        private final Consumer<Boolean> mSuccessCallback;
+        private Consumer<Boolean> mSuccessCallback;
 
         RemoteSplitLaunchTransitionRunner(int initialTaskId, int secondTaskId,
                 @Nullable Consumer<Boolean> callback) {
@@ -626,6 +629,7 @@
                             finishAdapter.run();
                             if (mSuccessCallback != null) {
                                 mSuccessCallback.accept(true);
+                                mSuccessCallback = null;
                             }
                             resetState();
                         });
@@ -699,6 +703,7 @@
         mDismissingFromSplitPair = false;
         mFirstFloatingTaskView = null;
         mSplitInstructionsView = null;
+        mLaunchingFirstAppFullscreen = false;
     }
 
     /**
@@ -717,6 +722,10 @@
         return mSplitSelectDataHolder.isBothSplitAppsConfirmed();
     }
 
+    public boolean isLaunchingFirstAppFullscreen() {
+        return mLaunchingFirstAppFullscreen;
+    }
+
     public int getInitialTaskId() {
         return mSplitSelectDataHolder.getInitialTaskId();
     }
@@ -725,6 +734,9 @@
         return mSplitSelectDataHolder.getSecondTaskId();
     }
 
+    public void setLaunchingFirstAppFullscreen() {
+        mLaunchingFirstAppFullscreen = true;
+    }
     public void setFirstFloatingTaskView(FloatingTaskView floatingTaskView) {
         mFirstFloatingTaskView = floatingTaskView;
     }
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
index 056f9aa..0fa8a29 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -30,15 +31,19 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.view.View;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.quickstep.views.FloatingTaskView;
 import com.android.quickstep.views.RecentsView;
@@ -52,12 +57,13 @@
     private final SplitSelectStateController mController;
 
     private final int mHalfDividerSize;
+    private final IconCache mIconCache;
 
     public SplitToWorkspaceController(Launcher launcher, SplitSelectStateController controller) {
         mLauncher = launcher;
         mDP = mLauncher.getDeviceProfile();
         mController = controller;
-
+        mIconCache = LauncherAppState.getInstanceNoCreate().getIconCache();
         mHalfDividerSize = mLauncher.getResources().getDimensionPixelSize(
                 R.dimen.multi_window_task_divider_size) / 2;
     }
@@ -74,17 +80,24 @@
             return false;
         }
 
-        // Convert original widgetView into bitmap to use for animation
-        // TODO(b/276361926) get the icon for this widget via PackageManager?
         int width = view.getWidth();
         int height = view.getHeight();
-        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        view.draw(canvas);
+        MODEL_EXECUTOR.execute(() -> {
+            PackageItemInfo infoInOut = new PackageItemInfo(pendingIntent.getCreatorPackage(),
+                    pendingIntent.getCreatorUserHandle());
+            mIconCache.getTitleAndIconForApp(infoInOut, false);
+            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
 
-        mController.setSecondTask(pendingIntent);
+            view.post(() -> {
+                mController.setSecondTask(pendingIntent);
+                // Convert original widgetView into bitmap to use for animation
+                Canvas canvas = new Canvas(bitmap);
+                view.draw(canvas);
+                startWorkspaceAnimation(view, bitmap,
+                        new BitmapDrawable(mLauncher.getResources(), infoInOut.bitmap.icon));
+            });
+        });
 
-        startWorkspaceAnimation(view, bitmap, null /*icon*/);
         return true;
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 510044d..767aa15 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -384,6 +384,7 @@
         mInversePositionMatrix.mapRect(mTempRectF);
         mTempRectF.roundOut(mTmpCropRect);
 
+        params.setProgress(1f - fullScreenProgress);
         params.applySurfaceParams(params.createSurfaceParams(this));
 
         if (!DEBUG) {
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index 1cbded6..ca680db 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -15,8 +15,8 @@
  */
 package com.android.quickstep.util;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 
 import android.util.FloatProperty;
 import android.view.RemoteAnimationTarget;
@@ -54,6 +54,7 @@
         }
     };
 
+    /** Progress from 0 to 1 where 0 is in-app and 1 is Overview */
     private float mProgress;
     private float mTargetAlpha;
     private float mCornerRadius;
@@ -135,6 +136,7 @@
         return this;
     }
 
+    /** Builds the SurfaceTransaction from the given BuilderProxy params. */
     public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
         RemoteAnimationTargets targets = mTargetSet;
         SurfaceTransaction transaction = new SurfaceTransaction();
@@ -150,8 +152,12 @@
                 if (activityType == ACTIVITY_TYPE_HOME) {
                     mHomeBuilderProxy.onBuildTargetParams(builder, app, this);
                 } else {
-                    // Fade out Assistant overlay.
-                    if (activityType == ACTIVITY_TYPE_ASSISTANT && app.isNotInRecents) {
+                    // Fade out translucent overlay.
+                    // TODO(b/303351074): use app.isNotInRecents directly once it is fixed.
+                    boolean isNotInRecents = app.taskInfo != null
+                            && (app.taskInfo.baseIntent.getFlags()
+                                    & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
+                    if (app.isTranslucent && isNotInRecents) {
                         float progress = Utilities.boundToRange(getProgress(), 0, 1);
                         builder.setAlpha(1 - Interpolators.DECELERATE_QUINT
                                 .getInterpolation(progress));
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index cfb4d0d..fba847f 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -16,7 +16,7 @@
 
 package com.android.quickstep.views;
 
-import static com.android.launcher3.config.FeatureFlags.enableGridOnlyOverview;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 9141c99..7f1d619 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -29,9 +29,9 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
 import com.android.launcher3.util.MultiValueAlpha;
@@ -289,7 +289,7 @@
             return 0;
         }
 
-        if (mDp.isTablet && FeatureFlags.enableGridOnlyOverview()) {
+        if (mDp.isTablet && Flags.enableGridOnlyOverview()) {
             return mDp.stashedTaskbarHeight;
         }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 825c0ae..2aba4d0 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -42,7 +42,7 @@
 import static com.android.launcher3.Utilities.mapToRange;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
-import static com.android.launcher3.config.FeatureFlags.enableGridOnlyOverview;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
 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;
@@ -217,6 +217,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
@@ -3263,7 +3264,10 @@
         mSplitSelectStateController.setFirstFloatingTaskView(firstFloatingTaskView);
 
         // Allow user to click staged app to launch into fullscreen
-        firstFloatingTaskView.setOnClickListener(this::animateToFullscreen);
+        firstFloatingTaskView.setOnClickListener(view ->
+                mSplitSelectStateController.getSplitAnimationController().
+                        playAnimPlaceholderToFullscreen(mActivity, view,
+                                Optional.of(() -> resetFromSplitSelectionState())));
 
         // SplitInstructionsView: animate in
         safeRemoveDragLayerView(mSplitSelectStateController.getSplitInstructionsView());
@@ -3316,41 +3320,6 @@
         });
     }
 
-    private void animateToFullscreen(View view) {
-        FloatingTaskView stagedTaskView = (FloatingTaskView) view;
-
-        boolean isTablet = mActivity.getDeviceProfile().isTablet;
-        int duration = isTablet
-                ? SplitAnimationTimings.TABLET_CONFIRM_DURATION
-                : SplitAnimationTimings.PHONE_CONFIRM_DURATION;
-
-        PendingAnimation pendingAnimation = new PendingAnimation(duration);
-
-        Rect firstTaskStartingBounds = new Rect();
-        Rect firstTaskEndingBounds = new Rect();
-
-        stagedTaskView.getBoundsOnScreen(firstTaskStartingBounds);
-        mActivity.getDragLayer().getBoundsOnScreen(firstTaskEndingBounds);
-
-        stagedTaskView.addConfirmAnimation(
-                pendingAnimation,
-                new RectF(firstTaskStartingBounds),
-                firstTaskEndingBounds,
-                false /* fadeWithThumbnail */,
-                true /* isStagedTask */);
-
-        pendingAnimation.addEndListener(animationSuccess ->
-                mSplitSelectStateController.launchInitialAppFullscreen(launchSuccess -> {
-                    if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
-                        mSplitSelectStateController.resetState();
-                    } else {
-                        resetFromSplitSelectionState();
-                    }
-                }));
-
-        pendingAnimation.buildAnim().start();
-    }
-
     /**
      * Creates a {@link PendingAnimation} for dismissing the specified {@link TaskView}.
      * @param dismissedTaskView the {@link TaskView} to be dismissed
@@ -4980,12 +4949,13 @@
 
     protected void maybeDrawEmptyMessage(Canvas canvas) {
         if (mShowEmptyMessage && mEmptyTextLayout != null) {
-            // Offset to center in the visible (non-padded) part of RecentsView
-            mTempRect.set(mInsets.left + getPaddingLeft(), mInsets.top + getPaddingTop(),
-                    mInsets.right + getPaddingRight(), mInsets.bottom + getPaddingBottom());
+            // Offsets icon and text up so that the vertical center of screen (accounting for
+            // insets) is between icon and text.
+            int offset = (mEmptyIcon.getIntrinsicHeight() + mEmptyMessagePadding) / 2;
+
             canvas.save();
-            canvas.translate(getScrollX() + (mTempRect.left - mTempRect.right) / 2,
-                    (mTempRect.top - mTempRect.bottom) / 2);
+            canvas.translate(getScrollX() + (mInsets.left - mInsets.right) / 2f,
+                    (mInsets.top - mInsets.bottom) / 2f - offset);
             mEmptyIcon.draw(canvas);
             canvas.translate(mEmptyMessagePadding,
                     mEmptyIcon.getBounds().bottom + mEmptyMessagePadding);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 6ae1973..df907e7 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -77,6 +77,7 @@
 
 import com.android.app.animation.Interpolators;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -114,6 +115,8 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 
+import kotlin.Unit;
+
 import java.lang.annotation.Retention;
 import java.util.Arrays;
 import java.util.Collections;
@@ -122,8 +125,6 @@
 import java.util.function.Consumer;
 import java.util.stream.Stream;
 
-import kotlin.Unit;
-
 /**
  * A task in the Recents view.
  */
@@ -1019,14 +1020,6 @@
                     mActivity.getStateManager(), recentsView,
                     recentsView.getDepthController());
             anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    recentsView.runActionOnRemoteHandles(
-                            (Consumer<RemoteTargetHandle>) remoteTargetHandle ->
-                                    remoteTargetHandle
-                                            .getTaskViewSimulator()
-                                            .setDrawsBelowRecents(false));
-                }
 
                 @Override
                 public void onAnimationEnd(Animator animator) {
@@ -1153,7 +1146,7 @@
         } else if (dp.isTablet) {
             int alignedOptionIndex = 0;
             if (getRecentsView().isOnGridBottomRow(menuContainer.getTaskView()) && dp.isLandscape) {
-                if (FeatureFlags.enableGridOnlyOverview()) {
+                if (Flags.enableGridOnlyOverview()) {
                     // With no focused task, there is less available space below the tasks, so align
                     // the arrow to the third option in the menu.
                     alignedOptionIndex = 2;
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt
index 8c13fe3..f3115c6 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt
@@ -24,11 +24,11 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Mock
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 class FallbackTaskbarUIControllerTest : TaskbarBaseTestCase() {
@@ -36,8 +36,8 @@
     lateinit var fallbackTaskbarUIController: FallbackTaskbarUIController
     lateinit var stateListener: StateManager.StateListener<RecentsState>
 
-    @Mock lateinit var recentsActivity: RecentsActivity
-    @Mock lateinit var stateManager: StateManager<RecentsState>
+    private val recentsActivity: RecentsActivity = mock()
+    private val stateManager: StateManager<RecentsState> = mock()
 
     @Before
     override fun setup() {
@@ -46,10 +46,10 @@
         fallbackTaskbarUIController = FallbackTaskbarUIController(recentsActivity)
 
         // Capture registered state listener to send events to in our tests
-        val captor = ArgumentCaptor.forClass(StateManager.StateListener::class.java)
+        val captor = argumentCaptor<StateManager.StateListener<RecentsState>>()
         fallbackTaskbarUIController.init(taskbarControllers)
         verify(stateManager).addStateListener(captor.capture())
-        stateListener = captor.value as StateManager.StateListener<RecentsState>
+        stateListener = captor.lastValue
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
index 148e36c..ed88c29 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt
@@ -23,17 +23,17 @@
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
 import org.junit.Before
 import org.junit.Test
-import org.mockito.Mock
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 class TaskbarKeyguardControllerTest : TaskbarBaseTestCase() {
 
-    @Mock lateinit var baseDragLayer: TaskbarDragLayer
-    @Mock lateinit var keyguardManager: KeyguardManager
+    private val baseDragLayer: TaskbarDragLayer = mock()
+    private val keyguardManager: KeyguardManager = mock()
 
     @Before
     override fun setup() {
@@ -50,7 +50,7 @@
     @Test
     fun uninterestingFlags_noActions() {
         setFlags(0)
-        verify(navbarButtonsViewController, never()).setKeyguardVisible(anyBoolean(), anyBoolean())
+        verify(navbarButtonsViewController, never()).setKeyguardVisible(any(), any())
     }
 
     @Test
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
index 16bfe70..37fcf43 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
@@ -19,32 +19,29 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 class NavButtonLayoutFactoryTest {
 
-    @Mock lateinit var mockDeviceProfile: DeviceProfile
-    @Mock lateinit var mockParentButtonContainer: FrameLayout
-    @Mock lateinit var mockNavLayout: LinearLayout
-    @Mock lateinit var mockStartContextualLayout: ViewGroup
-    @Mock lateinit var mockEndContextualLayout: ViewGroup
-    @Mock lateinit var mockResources: Resources
-    @Mock lateinit var mockBackButton: ImageView
-    @Mock lateinit var mockRecentsButton: ImageView
-    @Mock lateinit var mockHomeButton: ImageView
-    @Mock lateinit var mockImeSwitcher: ImageView
-    @Mock lateinit var mockRotationButton: RotationButton
-    @Mock lateinit var mockA11yButton: ImageView
+    private val mockDeviceProfile: DeviceProfile = mock()
+    private val mockParentButtonContainer: FrameLayout = mock()
+    private val mockNavLayout: LinearLayout = mock()
+    private val mockStartContextualLayout: ViewGroup = mock()
+    private val mockEndContextualLayout: ViewGroup = mock()
+    private val mockResources: Resources = mock()
+    private val mockBackButton: ImageView = mock()
+    private val mockRecentsButton: ImageView = mock()
+    private val mockHomeButton: ImageView = mock()
+    private val mockImeSwitcher: ImageView = mock()
+    private val mockRotationButton: RotationButton = mock()
+    private val mockA11yButton: ImageView = mock()
 
     private var surfaceRotation = Surface.ROTATION_0
 
     @Before
     fun setup() {
-        MockitoAnnotations.initMocks(this)
-
         // Init end nav buttons
         whenever(mockNavLayout.childCount).thenReturn(3)
         whenever(mockNavLayout.findViewById<View>(R.id.back)).thenReturn(mockBackButton)
@@ -155,13 +152,13 @@
         mockDeviceProfile.isTaskbarPresent = false
         setDeviceProfileLandscape()
         val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
-                getLayoutter(
-                        isKidsMode = false,
-                        isInSetup = false,
-                        isThreeButtonNav = true,
-                        phoneMode = true,
-                        surfaceRotation = ROTATION_270
-                )
+            getLayoutter(
+                isKidsMode = false,
+                isInSetup = false,
+                isThreeButtonNav = true,
+                phoneMode = true,
+                surfaceRotation = ROTATION_270
+            )
         assert(layoutter is PhoneSeascapeNavLayoutter)
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java
index 9c6c93d..efc00bb 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java
@@ -95,6 +95,6 @@
         launcher.enableTransientTaskbar(expectTransientTaskbar);
         launcher.recreateTaskbar();
         launcher.checkForAnomaly(true, true);
-        AbstractLauncherUiTest.checkDetectedLeaks(launcher);
+        AbstractLauncherUiTest.checkDetectedLeaks(launcher, true);
     }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 99c79b3..10602d3 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -167,7 +167,7 @@
     public void tearDown() {
         try {
             // Limits UI tests affecting tests running after them.
-            AbstractQuickStepTest.checkDetectedLeaks(mLauncher);
+            AbstractQuickStepTest.checkDetectedLeaks(mLauncher, true);
         } finally {
             mLauncher.onTestFinish();
         }
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index eded1c9..a5d7724 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -185,7 +185,7 @@
                         + launcher.getNavigationModeMismatchError(false),
                 () -> launcher.getNavigationModeMismatchError(false) == null,
                 WAIT_TIME_MS, launcher);
-        AbstractLauncherUiTest.checkDetectedLeaks(launcher);
+        AbstractLauncherUiTest.checkDetectedLeaks(launcher, true);
         return true;
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/TaskbarModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/TaskbarModeSwitchRule.java
index 9e41f74..e5657fb 100644
--- a/quickstep/tests/src/com/android/quickstep/TaskbarModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/TaskbarModeSwitchRule.java
@@ -123,7 +123,7 @@
         assertTrue(launcher, "Couldn't set taskbar=" + expectTransientTaskbar,
                 isTaskbarTransientMode(context) == expectTransientTaskbar, description);
 
-        AbstractLauncherUiTest.checkDetectedLeaks(launcher);
+        AbstractLauncherUiTest.checkDetectedLeaks(launcher, true);
     }
 
     private static void assertTrue(LauncherInstrumentation launcher, String message,
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
index 7e07b81..50803fe 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
@@ -32,39 +32,36 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 class SplitAnimationControllerTest {
 
     private val taskId = 9
 
-    @Mock lateinit var mockSplitSelectStateController: SplitSelectStateController
+    private val mockSplitSelectStateController: SplitSelectStateController = mock()
     // TaskView
-    @Mock lateinit var mockTaskView: TaskView
-    @Mock lateinit var mockThumbnailView: TaskThumbnailView
-    @Mock lateinit var mockBitmap: Bitmap
-    @Mock lateinit var mockIconView: IconView
-    @Mock lateinit var mockTaskViewDrawable: Drawable
+    private val mockTaskView: TaskView = mock()
+    private val mockThumbnailView: TaskThumbnailView = mock()
+    private val mockBitmap: Bitmap = mock()
+    private val mockIconView: IconView = mock()
+    private val mockTaskViewDrawable: Drawable = mock()
     // GroupedTaskView
-    @Mock lateinit var mockGroupedTaskView: GroupedTaskView
-    @Mock lateinit var mockTask: Task
-    @Mock lateinit var mockTaskKey: Task.TaskKey
-    @Mock lateinit var mockTaskIdAttributeContainer: TaskIdAttributeContainer
+    private val mockGroupedTaskView: GroupedTaskView = mock()
+    private val mockTask: Task = mock()
+    private val mockTaskKey: Task.TaskKey = mock()
+    private val mockTaskIdAttributeContainer: TaskIdAttributeContainer = mock()
 
     // SplitSelectSource
-    @Mock lateinit var splitSelectSource: SplitConfigurationOptions.SplitSelectSource
-    @Mock lateinit var mockSplitSourceDrawable: Drawable
-    @Mock lateinit var mockSplitSourceView: View
+    private val splitSelectSource: SplitConfigurationOptions.SplitSelectSource = mock()
+    private val mockSplitSourceDrawable: Drawable = mock()
+    private val mockSplitSourceView: View = mock()
 
     lateinit var splitAnimationController: SplitAnimationController
 
     @Before
     fun setup() {
-        MockitoAnnotations.initMocks(this)
-
         whenever(mockTaskView.thumbnail).thenReturn(mockThumbnailView)
         whenever(mockThumbnailView.thumbnail).thenReturn(mockBitmap)
         whenever(mockTaskView.iconView).thenReturn(mockIconView)
@@ -85,12 +82,14 @@
         // Missing taskView icon
         whenever(mockIconView.drawable).thenReturn(null)
 
-        val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps =
-                splitAnimationController.getFirstAnimInitViews(
-                        { mockTaskView }, { splitSelectSource })
+        val splitAnimInitProps: SplitAnimationController.Companion.SplitAnimInitProps =
+            splitAnimationController.getFirstAnimInitViews({ mockTaskView }, { splitSelectSource })
 
-        assertEquals("Did not fallback to use splitSource icon drawable",
-                mockSplitSourceDrawable, splitAnimInitProps.iconDrawable)
+        assertEquals(
+            "Did not fallback to use splitSource icon drawable",
+            mockSplitSourceDrawable,
+            splitAnimInitProps.iconDrawable
+        )
     }
 
     @Test
@@ -99,12 +98,14 @@
         whenever(mockSplitSelectStateController.isAnimateCurrentTaskDismissal).thenReturn(true)
         whenever(mockSplitSelectStateController.isDismissingFromSplitPair).thenReturn(false)
 
-        val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps =
-                splitAnimationController.getFirstAnimInitViews(
-                        { mockTaskView }, { splitSelectSource })
+        val splitAnimInitProps: SplitAnimationController.Companion.SplitAnimInitProps =
+            splitAnimationController.getFirstAnimInitViews({ mockTaskView }, { splitSelectSource })
 
-        assertEquals("Did not use taskView icon drawable", mockTaskViewDrawable,
-                splitAnimInitProps.iconDrawable)
+        assertEquals(
+            "Did not use taskView icon drawable",
+            mockTaskViewDrawable,
+            splitAnimInitProps.iconDrawable
+        )
     }
 
     @Test
@@ -116,12 +117,14 @@
         // Set split source to null
         whenever(splitSelectSource.drawable).thenReturn(null)
 
-        val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps =
-                splitAnimationController.getFirstAnimInitViews(
-                        { mockTaskView }, { splitSelectSource })
+        val splitAnimInitProps: SplitAnimationController.Companion.SplitAnimInitProps =
+            splitAnimationController.getFirstAnimInitViews({ mockTaskView }, { splitSelectSource })
 
-        assertEquals("Did not use taskView icon drawable", mockTaskViewDrawable,
-                splitAnimInitProps.iconDrawable)
+        assertEquals(
+            "Did not use taskView icon drawable",
+            mockTaskViewDrawable,
+            splitAnimInitProps.iconDrawable
+        )
     }
 
     @Test
@@ -130,12 +133,14 @@
         whenever(mockSplitSelectStateController.isAnimateCurrentTaskDismissal).thenReturn(false)
         whenever(mockSplitSelectStateController.isDismissingFromSplitPair).thenReturn(false)
 
-        val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps =
-                splitAnimationController.getFirstAnimInitViews(
-                        { mockTaskView }, { splitSelectSource })
+        val splitAnimInitProps: SplitAnimationController.Companion.SplitAnimInitProps =
+            splitAnimationController.getFirstAnimInitViews({ mockTaskView }, { splitSelectSource })
 
-        assertEquals("Did not use splitSource icon drawable", mockSplitSourceDrawable,
-                splitAnimInitProps.iconDrawable)
+        assertEquals(
+            "Did not use splitSource icon drawable",
+            mockSplitSourceDrawable,
+            splitAnimInitProps.iconDrawable
+        )
     }
 
     @Test
@@ -154,12 +159,17 @@
         whenever(mockTaskKey.getId()).thenReturn(taskId)
         whenever(mockSplitSelectStateController.initialTaskId).thenReturn(taskId)
         whenever(mockGroupedTaskView.taskIdAttributeContainers)
-                .thenReturn(Array(1) { mockTaskIdAttributeContainer })
-        val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps =
-                splitAnimationController.getFirstAnimInitViews(
-                        { mockGroupedTaskView }, { splitSelectSource })
+            .thenReturn(Array(1) { mockTaskIdAttributeContainer })
+        val splitAnimInitProps: SplitAnimationController.Companion.SplitAnimInitProps =
+            splitAnimationController.getFirstAnimInitViews(
+                { mockGroupedTaskView },
+                { splitSelectSource }
+            )
 
-        assertEquals("Did not use splitSource icon drawable", mockSplitSourceDrawable,
-                splitAnimInitProps.iconDrawable)
+        assertEquals(
+            "Did not use splitSource icon drawable",
+            mockSplitSourceDrawable,
+            splitAnimInitProps.iconDrawable
+        )
     }
-}
\ No newline at end of file
+}
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
index f198741..f292f9a 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -33,11 +33,11 @@
 import com.android.launcher3.statemanager.StatefulActivity
 import com.android.launcher3.util.ComponentKey
 import com.android.launcher3.util.SplitConfigurationOptions
-import com.android.launcher3.util.withArgCaptor
 import com.android.quickstep.RecentsModel
 import com.android.quickstep.SystemUiProxy
 import com.android.systemui.shared.recents.model.Task
 import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50
+import java.util.function.Consumer
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNull
@@ -45,23 +45,22 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-import java.util.function.Consumer
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 class SplitSelectStateControllerTest {
 
-    @Mock lateinit var systemUiProxy: SystemUiProxy
-    @Mock lateinit var depthController: DepthController
-    @Mock lateinit var statsLogManager: StatsLogManager
-    @Mock lateinit var stateManager: StateManager<LauncherState>
-    @Mock lateinit var handler: Handler
-    @Mock lateinit var context: StatefulActivity<*>
-    @Mock lateinit var recentsModel: RecentsModel
-    @Mock lateinit var pendingIntent: PendingIntent
+    private val systemUiProxy: SystemUiProxy = mock()
+    private val depthController: DepthController = mock()
+    private val statsLogManager: StatsLogManager = mock()
+    private val stateManager: StateManager<LauncherState> = mock()
+    private val handler: Handler = mock()
+    private val context: StatefulActivity<*> = mock()
+    private val recentsModel: RecentsModel = mock()
+    private val pendingIntent: PendingIntent = mock()
 
     lateinit var splitSelectStateController: SplitSelectStateController
 
@@ -69,11 +68,12 @@
     private val nonPrimaryUserHandle = UserHandle(ActivityManager.RunningTaskInfo().userId + 10)
 
     private var taskIdCounter = 0
-    private fun getUniqueId(): Int { return ++taskIdCounter }
+    private fun getUniqueId(): Int {
+        return ++taskIdCounter
+    }
 
     @Before
     fun setup() {
-        MockitoAnnotations.initMocks(this)
         splitSelectStateController =
             SplitSelectStateController(
                 context,
@@ -111,13 +111,14 @@
 
         // Capture callback from recentsModel#getTasks()
         val consumer =
-            withArgCaptor<Consumer<ArrayList<GroupTask>>> {
-                splitSelectStateController.findLastActiveTasksAndRunCallback(
-                    listOf(nonMatchingComponent),
-                    taskConsumer
-                )
-                verify(recentsModel).getTasks(capture())
-            }
+            argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+                    splitSelectStateController.findLastActiveTasksAndRunCallback(
+                        listOf(nonMatchingComponent),
+                        taskConsumer
+                    )
+                    verify(recentsModel).getTasks(capture())
+                }
+                .lastValue
 
         // Send our mocked tasks
         consumer.accept(tasks)
@@ -162,13 +163,14 @@
 
         // Capture callback from recentsModel#getTasks()
         val consumer =
-            withArgCaptor<Consumer<ArrayList<GroupTask>>> {
-                splitSelectStateController.findLastActiveTasksAndRunCallback(
-                    listOf(matchingComponent),
-                    taskConsumer
-                )
-                verify(recentsModel).getTasks(capture())
-            }
+            argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+                    splitSelectStateController.findLastActiveTasksAndRunCallback(
+                        listOf(matchingComponent),
+                        taskConsumer
+                    )
+                    verify(recentsModel).getTasks(capture())
+                }
+                .lastValue
 
         // Send our mocked tasks
         consumer.accept(tasks)
@@ -201,13 +203,14 @@
 
         // Capture callback from recentsModel#getTasks()
         val consumer =
-            withArgCaptor<Consumer<ArrayList<GroupTask>>> {
-                splitSelectStateController.findLastActiveTasksAndRunCallback(
-                    listOf(nonPrimaryUserComponent),
-                    taskConsumer
-                )
-                verify(recentsModel).getTasks(capture())
-            }
+            argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+                    splitSelectStateController.findLastActiveTasksAndRunCallback(
+                        listOf(nonPrimaryUserComponent),
+                        taskConsumer
+                    )
+                    verify(recentsModel).getTasks(capture())
+                }
+                .lastValue
 
         // Send our mocked tasks
         consumer.accept(tasks)
@@ -255,13 +258,14 @@
 
         // Capture callback from recentsModel#getTasks()
         val consumer =
-            withArgCaptor<Consumer<ArrayList<GroupTask>>> {
-                splitSelectStateController.findLastActiveTasksAndRunCallback(
-                    listOf(nonPrimaryUserComponent),
-                    taskConsumer
-                )
-                verify(recentsModel).getTasks(capture())
-            }
+            argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+                    splitSelectStateController.findLastActiveTasksAndRunCallback(
+                        listOf(nonPrimaryUserComponent),
+                        taskConsumer
+                    )
+                    verify(recentsModel).getTasks(capture())
+                }
+                .lastValue
 
         // Send our mocked tasks
         consumer.accept(tasks)
@@ -306,13 +310,14 @@
 
         // Capture callback from recentsModel#getTasks()
         val consumer =
-            withArgCaptor<Consumer<ArrayList<GroupTask>>> {
-                splitSelectStateController.findLastActiveTasksAndRunCallback(
-                    listOf(matchingComponent),
-                    taskConsumer
-                )
-                verify(recentsModel).getTasks(capture())
-            }
+            argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+                    splitSelectStateController.findLastActiveTasksAndRunCallback(
+                        listOf(matchingComponent),
+                        taskConsumer
+                    )
+                    verify(recentsModel).getTasks(capture())
+                }
+                .lastValue
 
         // Send our mocked tasks
         consumer.accept(tasks)
@@ -327,10 +332,7 @@
             ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
 
         val groupTask1 =
-            generateGroupTask(
-                ComponentName("hotdog", "pie"),
-                ComponentName("pumpkin", "pie")
-            )
+            generateGroupTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie"))
         val groupTask2 =
             generateGroupTask(
                 ComponentName("pomegranate", "juice"),
@@ -361,13 +363,14 @@
 
         // Capture callback from recentsModel#getTasks()
         val consumer =
-            withArgCaptor<Consumer<ArrayList<GroupTask>>> {
-                splitSelectStateController.findLastActiveTasksAndRunCallback(
-                    listOf(nonMatchingComponent, matchingComponent),
-                    taskConsumer
-                )
-                verify(recentsModel).getTasks(capture())
-            }
+            argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+                    splitSelectStateController.findLastActiveTasksAndRunCallback(
+                        listOf(nonMatchingComponent, matchingComponent),
+                        taskConsumer
+                    )
+                    verify(recentsModel).getTasks(capture())
+                }
+                .lastValue
 
         // Send our mocked tasks
         consumer.accept(tasks)
@@ -381,10 +384,7 @@
             ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle)
 
         val groupTask1 =
-            generateGroupTask(
-                ComponentName("hotdog", "pie"),
-                ComponentName("pumpkin", "pie")
-            )
+            generateGroupTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie"))
         val groupTask2 =
             generateGroupTask(
                 ComponentName("pomegranate", "juice"),
@@ -415,13 +415,14 @@
 
         // Capture callback from recentsModel#getTasks()
         val consumer =
-            withArgCaptor<Consumer<ArrayList<GroupTask>>> {
-                splitSelectStateController.findLastActiveTasksAndRunCallback(
-                    listOf(matchingComponent, matchingComponent),
-                    taskConsumer
-                )
-                verify(recentsModel).getTasks(capture())
-            }
+            argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+                    splitSelectStateController.findLastActiveTasksAndRunCallback(
+                        listOf(matchingComponent, matchingComponent),
+                        taskConsumer
+                    )
+                    verify(recentsModel).getTasks(capture())
+                }
+                .lastValue
 
         // Send our mocked tasks
         consumer.accept(tasks)
@@ -479,13 +480,14 @@
 
         // Capture callback from recentsModel#getTasks()
         val consumer =
-            withArgCaptor<Consumer<ArrayList<GroupTask>>> {
-                splitSelectStateController.findLastActiveTasksAndRunCallback(
-                    listOf(matchingComponent, matchingComponent),
-                    taskConsumer
-                )
-                verify(recentsModel).getTasks(capture())
-            }
+            argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+                    splitSelectStateController.findLastActiveTasksAndRunCallback(
+                        listOf(matchingComponent, matchingComponent),
+                        taskConsumer
+                    )
+                    verify(recentsModel).getTasks(capture())
+                }
+                .lastValue
 
         // Send our mocked tasks
         consumer.accept(tasks)
@@ -531,7 +533,7 @@
     @Test
     fun secondPendingIntentSet() {
         val itemInfo = ItemInfo()
-        `when`(pendingIntent.creatorUserHandle).thenReturn(primaryUserHandle)
+        whenever(pendingIntent.creatorUserHandle).thenReturn(primaryUserHandle)
         splitSelectStateController.setInitialTaskSelect(null, 0, itemInfo, null, 1)
         splitSelectStateController.setSecondTask(pendingIntent)
         assertTrue(splitSelectStateController.isBothSplitAppsConfirmed)
diff --git a/quickstep/tests/src/com/android/quickstep/util/unfold/PreemptiveUnfoldTransitionProgressProviderTest.kt b/quickstep/tests/src/com/android/quickstep/util/unfold/PreemptiveUnfoldTransitionProgressProviderTest.kt
index f73be72..6a418a4 100644
--- a/quickstep/tests/src/com/android/quickstep/util/unfold/PreemptiveUnfoldTransitionProgressProviderTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/unfold/PreemptiveUnfoldTransitionProgressProviderTest.kt
@@ -21,19 +21,17 @@
 import android.testing.TestableLooper.RunWithLooper
 import android.util.Log
 import androidx.test.filters.SmallTest
-import com.android.launcher3.util.any
-import com.android.launcher3.util.mock
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.anyFloat
-import org.mockito.Mockito.inOrder
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.inOrder
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -74,7 +72,7 @@
         provider.preemptivelyStartTransition(initialProgress = null)
 
         verify(listener).onTransitionStarted()
-        verify(listener, never()).onTransitionProgress(anyFloat())
+        verify(listener, never()).onTransitionProgress(any())
     }
 
     @Test
@@ -90,7 +88,7 @@
         provider.preemptivelyStartTransition()
         provider.cancelPreemptiveStart()
 
-        with(inOrder(listener)) {
+        inOrder(listener) {
             verify(listener).onTransitionStarted()
             verify(listener).onTransitionFinished()
         }
@@ -111,7 +109,7 @@
         source.onTransitionStarted()
         source.onTransitionFinished()
 
-        with(inOrder(listener)) {
+        inOrder(listener) {
             verify(listener).onTransitionStarted()
             verify(listener).onTransitionFinished()
         }
@@ -152,7 +150,7 @@
         provider.preemptivelyStartTransition()
         source.onTransitionFinished()
 
-        with(inOrder(listener)) {
+        inOrder(listener) {
             verify(listener).onTransitionStarted()
             verify(listener).onTransitionFinished()
         }
@@ -165,7 +163,7 @@
         testableLooper.moveTimeForward(PREEMPTIVE_UNFOLD_TIMEOUT_MS + 1)
         testableLooper.processAllMessages()
 
-        with(inOrder(listener)) {
+        inOrder(listener) {
             verify(listener).onTransitionStarted()
             verify(listener).onTransitionFinished()
         }
@@ -178,7 +176,7 @@
         testableLooper.moveTimeForward(PREEMPTIVE_UNFOLD_TIMEOUT_MS + 1)
         testableLooper.processAllMessages()
 
-        verify(testWtfHandler).onTerribleFailure(any(), any(), anyBoolean())
+        verify(testWtfHandler).onTerribleFailure(any(), any(), any())
     }
 
     @Test
@@ -225,7 +223,7 @@
 
         source.onTransitionFinished()
 
-        with(inOrder(listener)) {
+        inOrder(listener) {
             verify(listener).onTransitionStarted()
             verify(listener).onTransitionFinished()
         }
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 7661bd7..070d024 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -245,8 +245,7 @@
     <dimen name="keyboard_drag_stroke_width">4dp</dimen>
 
     <!-- Folders -->
-    <dimen name="page_indicator_dot_size">8dp</dimen>
-    <dimen name="page_indicator_dot_size_v2">6dp</dimen>
+    <dimen name="page_indicator_dot_size">6dp</dimen>
     <dimen name="page_indicator_size">10dp</dimen>
 
 
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 879000a..d7b50a0 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -386,10 +386,12 @@
         setTag(itemInfo);
     }
 
+    @VisibleForTesting
     @UiThread
-    protected void applyIconAndLabel(ItemInfoWithIcon info) {
+    public void applyIconAndLabel(ItemInfoWithIcon info) {
         int flags = shouldUseTheme() ? FLAG_THEMED : 0;
-        if (mHideBadge) {
+        // Remove badge on icons smaller than 48dp.
+        if (mHideBadge || mDisplay == DISPLAY_SEARCH_RESULT_SMALL) {
             flags |= FLAG_NO_BADGE;
         }
         FastBitmapDrawable iconDrawable = info.newIcon(getContext(), flags);
@@ -813,6 +815,8 @@
         float currentWordWidth, runningWidth = 0;
         CharSequence currentWord;
         StringBuilder newString = new StringBuilder();
+        // TODO: Remove when ENABLE_ICON_LABEL_AUTO_SCALING feature flag is being cleaned up.
+        paint.setLetterSpacing(MIN_LETTER_SPACING);
         int stringPtr = 0;
         for (int i = 0; i < breakPoints.size()+1; i++) {
             if (i < breakPoints.size()) {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 9a2193f..c96e22d 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -1772,7 +1772,7 @@
 
     /** Gets the space that the overview actions will take, including bottom margin. */
     public int getOverviewActionsClaimedSpace() {
-        int overviewActionsSpace = isTablet && FeatureFlags.enableGridOnlyOverview()
+        int overviewActionsSpace = isTablet && Flags.enableGridOnlyOverview()
                 ? 0
                 : (overviewActionsTopMarginPx + overviewActionsHeight);
         return overviewActionsSpace + getOverviewActionsClaimedSpaceBelow();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 66a35d7..7a16e1f 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -26,7 +26,6 @@
 import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER;
 import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE;
 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
-import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
 import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
 import static com.android.launcher3.BuildConfig.APPLICATION_ID;
 import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY;
@@ -48,7 +47,6 @@
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
 import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
 import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
-import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION;
 import static com.android.launcher3.config.FeatureFlags.shouldShowFirstPageWidget;
 import static com.android.launcher3.logging.StatsLogManager.EventEnum;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
@@ -186,7 +184,6 @@
 import com.android.launcher3.pageindicators.WorkspacePageIndicator;
 import com.android.launcher3.pm.PinRequestHelper;
 import com.android.launcher3.popup.ArrowPopup;
-import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.qsb.QsbContainerView;
@@ -211,7 +208,6 @@
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.PendingRequestArgs;
-import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.ScreenOnTracker;
 import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
@@ -325,9 +321,11 @@
     @Thunk @VisibleForTesting public static final int NEW_APPS_ANIMATION_DELAY = 500;
 
     private static final String DISPLAY_WORKSPACE_TRACE_METHOD_NAME = "DisplayWorkspaceFirstFrame";
-    private static final String DISPLAY_ALL_APPS_TRACE_METHOD_NAME = "DisplayAllApps";
+    public static final String DISPLAY_ALL_APPS_TRACE_METHOD_NAME = "DisplayAllApps";
     public static final int DISPLAY_WORKSPACE_TRACE_COOKIE = 0;
     public static final int DISPLAY_ALL_APPS_TRACE_COOKIE = 1;
+    private static final String COLD_STARTUP_TRACE_METHOD_NAME = "LauncherColdStartup";
+    public static final int COLD_STARTUP_TRACE_COOKIE = 2;
 
     private static final FloatProperty<Workspace<?>> WORKSPACE_WIDGET_SCALE =
             WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION);
@@ -337,7 +335,11 @@
     private static final boolean DESKTOP_MODE_SUPPORTED =
             "1".equals(Utilities.getSystemProperty("persist.wm.debug.desktop_mode_2", "0"));
 
-    KeyboardShortcutsDelegate mKeyboardShortcutsDelegate = new KeyboardShortcutsDelegate(this);
+    private final ModelCallbacks mModelCallbacks = createModelCallbacks();
+
+    private final KeyboardShortcutsDelegate mKeyboardShortcutsDelegate =
+            new KeyboardShortcutsDelegate(this);
+
     @Thunk
     Workspace<?> mWorkspace;
     @Thunk
@@ -429,6 +431,7 @@
             new CannedAnimationCoordinator(this);
 
     private final List<BackPressHandler> mBackPressedHandlers = new ArrayList<>();
+    private boolean mIsColdStartupAfterReboot;
 
     public static Launcher getLauncher(Context context) {
         return fromContext(context);
@@ -443,6 +446,14 @@
                             ? COLD
                             : COLD_DEVICE_REBOOTING
                         : WARM);
+
+        mIsColdStartupAfterReboot = sIsNewProcess
+            && !LockedUserState.get(this).isUserUnlockedAtLauncherStartup();
+        if (mIsColdStartupAfterReboot) {
+            Trace.beginAsyncSection(
+                    COLD_STARTUP_TRACE_METHOD_NAME, COLD_STARTUP_TRACE_COOKIE);
+        }
+
         sIsNewProcess = false;
         mStartupLatencyLogger
                 .logStart(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION)
@@ -580,9 +591,6 @@
         getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
                 Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
 
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onCreate(savedInstanceState);
-        }
         mOverlayManager = getDefaultOverlay();
         PluginManagerWrapper.INSTANCE.get(this).addPluginListener(this,
                 LauncherOverlayPlugin.class, false /* allowedMultiple */);
@@ -597,6 +605,10 @@
         mStartupLatencyLogger.logEnd(LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE);
     }
 
+    protected ModelCallbacks createModelCallbacks() {
+        return new ModelCallbacks(this);
+    }
+
     /**
      * Create {@link StartupLatencyLogger} that only collects launcher startup latency metrics
      * without sending them anywhere. Child class can override this method to create logger
@@ -792,8 +804,6 @@
         return true;
     }
 
-    private LauncherCallbacks mLauncherCallbacks;
-
     @Override
     public void invalidateParent(ItemInfo info) {
         if (info.container >= 0) {
@@ -1325,18 +1335,14 @@
         mDropTargetBar.setup(mDragController);
         mAllAppsController.setupViews(mScrimView, mAppsView);
 
-        if (SHOW_DOT_PAGINATION.get()) {
-            mWorkspace.getPageIndicator().setShouldAutoHide(true);
-            mWorkspace.getPageIndicator().setPaintColor(
-                    Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)
-                            ? Color.BLACK
-                            : Color.WHITE);
-        }
+        mWorkspace.getPageIndicator().setShouldAutoHide(true);
+        mWorkspace.getPageIndicator().setPaintColor(Themes.getAttrBoolean(
+                this, R.attr.isWorkspaceDarkText) ? Color.BLACK : Color.WHITE);
     }
 
     @Override
     public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
-        if (SHOW_DOT_PAGINATION.get() && WorkspacePageIndicator.class.getName().equals(name)) {
+        if (WorkspacePageIndicator.class.getName().equals(name)) {
             return LayoutInflater.from(context).inflate(R.layout.page_indicator_dots,
                     (ViewGroup) parent, false);
         }
@@ -1585,9 +1591,6 @@
                 }
             }
 
-            if (mLauncherCallbacks != null) {
-                mLauncherCallbacks.onHomeIntent(internalStateHandled);
-            }
             if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
                 handleSplitAnimationGoingToHome();
             }
@@ -1756,28 +1759,6 @@
         }
     }
 
-    /**
-     * Indicates that we want global search for this activity by setting the globalSearch
-     * argument for {@link #startSearch} to true.
-     */
-    @Override
-    public void startSearch(String initialQuery, boolean selectInitialQuery,
-            Bundle appSearchData, boolean globalSearch) {
-        if (appSearchData == null) {
-            appSearchData = new Bundle();
-            appSearchData.putString("source", "launcher-search");
-        }
-
-        if (mLauncherCallbacks == null ||
-                !mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData)) {
-            // Starting search from the callbacks failed. Start the default global search.
-            super.startSearch(initialQuery, selectInitialQuery, appSearchData, true);
-        }
-
-        // We need to show the workspace after starting the search
-        mStateManager.goToState(NORMAL);
-    }
-
     void addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
             WidgetAddFlowHandler addFlowHandler) {
         if (LOGD) {
@@ -2248,13 +2229,7 @@
 
     @Override
     public void preAddApps() {
-        // If there's an undo snackbar, force it to complete to ensure empty screens are removed
-        // before trying to add new items.
-        mModelWriter.commitDelete();
-        AbstractFloatingView snackbar = AbstractFloatingView.getOpenView(this, TYPE_SNACKBAR);
-        if (snackbar != null) {
-            snackbar.post(() -> snackbar.close(true));
-        }
+        mModelCallbacks.preAddApps();
     }
 
     @Override
@@ -2668,6 +2643,11 @@
                                 .logEnd(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION)
                                 .log()
                                 .reset();
+                        if (mIsColdStartupAfterReboot) {
+                            Trace.endAsyncSection(COLD_STARTUP_TRACE_METHOD_NAME,
+                                    COLD_STARTUP_TRACE_COOKIE);
+                        }
+
                         MAIN_EXECUTOR.getHandler().postAtFrontOfQueue(
                                 () -> getRootView().getViewTreeObserver()
                                         .removeOnDrawListener(this));
@@ -2852,90 +2832,70 @@
     public void onPageEndTransition() {}
 
     /**
-     * Add the icons for all apps.
-     *
-     * Implementation of the method from LauncherModel.Callbacks.
+     * See {@code LauncherBindingDelegate}
      */
     @Override
     @TargetApi(Build.VERSION_CODES.S)
     @UiThread
     public void bindAllApplications(AppInfo[] apps, int flags,
             Map<PackageUserKey, Integer> packageUserKeytoUidMap) {
-        Preconditions.assertUIThread();
-        boolean hadWorkApps = mAppsView.shouldShowTabs();
-        AllAppsStore<Launcher> appsStore = mAppsView.getAppsStore();
-        appsStore.setApps(apps, flags, packageUserKeytoUidMap);
-        PopupContainerWithArrow.dismissInvalidPopup(this);
-        if (hadWorkApps != mAppsView.shouldShowTabs()) {
-            getStateManager().goToState(NORMAL);
-        }
-
+        mModelCallbacks.bindAllApplications(apps, flags, packageUserKeytoUidMap);
         if (Utilities.ATLEAST_S) {
-            Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
-                    DISPLAY_ALL_APPS_TRACE_COOKIE);
+            Trace.endAsyncSection(
+                    Launcher.DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
+                    Launcher.DISPLAY_ALL_APPS_TRACE_COOKIE
+            );
         }
     }
 
     /**
-     * Copies LauncherModel's map of activities to shortcut counts to Launcher's. This is necessary
-     * because LauncherModel's map is updated in the background, while Launcher runs on the UI.
+     * See {@code LauncherBindingDelegate}
      */
     @Override
     public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) {
-        mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
+        mModelCallbacks.bindDeepShortcutMap(deepShortcutMapCopy);
     }
 
     @Override
     public void bindIncrementalDownloadProgressUpdated(AppInfo app) {
-        mAppsView.getAppsStore().updateProgressBar(app);
+        mModelCallbacks.bindIncrementalDownloadProgressUpdated(app);
     }
 
     @Override
     public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) {
-        mWorkspace.widgetsRestored(widgets);
+        mModelCallbacks.bindWidgetsRestored(widgets);
     }
 
     /**
-     * Some shortcuts were updated in the background.
-     * Implementation of the method from LauncherModel.Callbacks.
-     *
-     * @param updated list of shortcuts which have changed.
+     * See {@code LauncherBindingDelegate}
      */
     @Override
     public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
-        if (!updated.isEmpty()) {
-            mWorkspace.updateWorkspaceItems(updated, this);
-            PopupContainerWithArrow.dismissInvalidPopup(this);
-        }
+        mModelCallbacks.bindWorkspaceItemsChanged(updated);
     }
 
     /**
-     * Update the state of a package, typically related to install state.
-     *
-     * Implementation of the method from LauncherModel.Callbacks.
+     * See {@code LauncherBindingDelegate}
      */
     @Override
     public void bindRestoreItemsChange(HashSet<ItemInfo> updates) {
-        mWorkspace.updateRestoreItems(updates, this);
+        mModelCallbacks.bindRestoreItemsChange(updates);
     }
 
     /**
-     * A package was uninstalled/updated.  We take both the super set of packageNames
-     * in addition to specific applications to remove, the reason being that
-     * this can be called when a package is updated as well.  In that scenario,
-     * we only remove specific components from the workspace and hotseat, where as
-     * package-removal should clear all items by package name.
+     * See {@code LauncherBindingDelegate}
      */
     @Override
     public void bindWorkspaceComponentsRemoved(Predicate<ItemInfo> matcher) {
-        mWorkspace.removeItemsByMatcher(matcher);
-        mDragController.onAppsRemoved(matcher);
-        PopupContainerWithArrow.dismissInvalidPopup(this);
+        mModelCallbacks.bindWorkspaceComponentsRemoved(matcher);
     }
 
+    /**
+     * See {@code LauncherBindingDelegate}
+     */
     @Override
     public void bindAllWidgets(final List<WidgetsListBaseEntry> allWidgets) {
-        mPopupDataProvider.setAllWidgets(allWidgets);
+        mModelCallbacks.bindAllWidgets(allWidgets);
     }
 
     @Override
@@ -3036,10 +2996,6 @@
         }
 
         mModel.dumpState(prefix, fd, writer, args);
-
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.dump(prefix, fd, writer, args);
-        }
         mOverlayManager.dump(prefix, writer);
     }
 
@@ -3283,11 +3239,6 @@
         mWorkspace.setLauncherOverlay(overlay);
     }
 
-    public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
-        mLauncherCallbacks = callbacks;
-        return true;
-    }
-
     /**
      * Persistent callback which notifies when an activity launch is deferred because the activity
      * was not yet resumed.
@@ -3482,4 +3433,4 @@
     }
 
     // End of Getters and Setters
-}
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
deleted file mode 100644
index 0e529bd..0000000
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2016 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.os.Bundle;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * LauncherCallbacks is an interface used to extend the Launcher activity. It includes many hooks
- * in order to add additional functionality. Some of these are very general, and give extending
- * classes the ability to react to Activity life-cycle or specific user interactions. Others
- * are more specific and relate to replacing parts of the application, for example, the search
- * interface or the wallpaper picker.
- */
-public interface LauncherCallbacks {
-
-    /*
-     * Activity life-cycle methods. These methods are triggered after
-     * the code in the corresponding Launcher method is executed.
-     */
-    void onCreate(Bundle savedInstanceState);
-    void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args);
-    void onHomeIntent(boolean internalStateHandled);
-
-    /**
-     * Starts a search with {@param initialQuery}. Return false if search was not started.
-     */
-    boolean startSearch(
-            String initialQuery, boolean selectInitialQuery, Bundle appSearchData);
-}
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index bdd92eb..4db2fca 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -60,11 +60,11 @@
                 IS_STARTUP_DATA_MIGRATED.defaultValue
             )
 
-    // TODO: Remove `item == TASKBAR_PINNING` once isBootAwareStartupDataEnabled is always true
     private fun chooseSharedPreferences(item: Item): SharedPreferences =
         if (
-            (isBootAwareStartupDataEnabled && item.isBootAware && isStartupDataMigrated) ||
-                item == TASKBAR_PINNING
+            (moveStartupDataToDeviceProtectedStorageIsEnabled &&
+                item.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
+                isStartupDataMigrated) || item.encryptionType == EncryptionType.DEVICE_PROTECTED
         )
             bootAwarePrefs
         else item.encryptedPrefs
@@ -139,13 +139,20 @@
     private fun prepareToPutValues(
         updates: Array<out Pair<Item, Any>>
     ): List<SharedPreferences.Editor> {
-        val updatesPerPrefFile = updates.groupBy { it.first.encryptedPrefs }.toMutableMap()
+        val updatesPerPrefFile =
+            updates
+                .filter { it.first.encryptionType != EncryptionType.DEVICE_PROTECTED }
+                .groupBy { it.first.encryptedPrefs }
+                .toMutableMap()
 
-        if (isBootAwareStartupDataEnabled) {
-            val bootAwareUpdates = updates.filter { it.first.isBootAware }
-            if (bootAwareUpdates.isNotEmpty()) {
-                updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
+        val bootAwareUpdates =
+            updates.filter {
+                (it.first.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
+                    moveStartupDataToDeviceProtectedStorageIsEnabled) ||
+                    it.first.encryptionType == EncryptionType.DEVICE_PROTECTED
             }
+        if (bootAwareUpdates.isNotEmpty()) {
+            updatesPerPrefFile[bootAwarePrefs] = bootAwareUpdates
         }
 
         return updatesPerPrefFile.map { prefToItemValueList ->
@@ -238,13 +245,20 @@
      *   .apply() or .commit()
      */
     private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
-        val itemsPerFile = items.groupBy { it.encryptedPrefs }.toMutableMap()
+        val itemsPerFile =
+            items
+                .filter { it.encryptionType != EncryptionType.DEVICE_PROTECTED }
+                .groupBy { it.encryptedPrefs }
+                .toMutableMap()
 
-        if (isBootAwareStartupDataEnabled) {
-            val bootAwareUpdates = items.filter { it.isBootAware }
-            if (bootAwareUpdates.isNotEmpty()) {
-                itemsPerFile[bootAwarePrefs] = bootAwareUpdates
+        val bootAwareUpdates =
+            items.filter {
+                (it.encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
+                    moveStartupDataToDeviceProtectedStorageIsEnabled) ||
+                    it.encryptionType == EncryptionType.DEVICE_PROTECTED
             }
+        if (bootAwareUpdates.isNotEmpty()) {
+            itemsPerFile[bootAwarePrefs] = bootAwareUpdates
         }
 
         return itemsPerFile.map { (prefs, items) ->
@@ -255,7 +269,7 @@
     }
 
     fun migrateStartupDataToDeviceProtectedStorage() {
-        if (!isBootAwareStartupDataEnabled) return
+        if (!moveStartupDataToDeviceProtectedStorageIsEnabled) return
 
         Log.d(
             TAG,
@@ -264,7 +278,7 @@
         )
 
         with(bootAwarePrefs.edit()) {
-            BOOT_AWARE_ITEMS.forEach { putValue(it, get(it)) }
+            ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE.forEach { putValue(it, get(it)) }
             putBoolean(IS_STARTUP_DATA_MIGRATED.sharedPrefKey, true)
             apply()
         }
@@ -280,30 +294,66 @@
 
         const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
         const val SHOULD_SHOW_SMARTSPACE_KEY = "SHOULD_SHOW_SMARTSPACE_KEY"
-        @JvmField val ICON_STATE = nonRestorableItem(LauncherAppState.KEY_ICON_STATE, "", true)
+        @JvmField
+        val ICON_STATE =
+            nonRestorableItem(
+                LauncherAppState.KEY_ICON_STATE,
+                "",
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
         @JvmField
         val ALL_APPS_OVERVIEW_THRESHOLD =
-            nonRestorableItem(LauncherAppState.KEY_ALL_APPS_OVERVIEW_THRESHOLD, 180, true)
-        @JvmField val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false, true)
+            nonRestorableItem(
+                LauncherAppState.KEY_ALL_APPS_OVERVIEW_THRESHOLD,
+                180,
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
+        @JvmField
+        val THEMED_ICONS =
+            backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
         @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, "", true)
-        @JvmField val HOTSEAT_COUNT = backedUpItem(DeviceGridState.KEY_HOTSEAT_COUNT, -1, true)
-        @JvmField val TASKBAR_PINNING = backedUpItem(TASKBAR_PINNING_KEY, false, true)
+        @JvmField
+        val WORKSPACE_SIZE =
+            backedUpItem(
+                DeviceGridState.KEY_WORKSPACE_SIZE,
+                "",
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
+        @JvmField
+        val HOTSEAT_COUNT =
+            backedUpItem(
+                DeviceGridState.KEY_HOTSEAT_COUNT,
+                -1,
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
+        @JvmField
+        val TASKBAR_PINNING =
+            backedUpItem(TASKBAR_PINNING_KEY, false, EncryptionType.DEVICE_PROTECTED)
 
         @JvmField
         val DEVICE_TYPE =
-            backedUpItem(DeviceGridState.KEY_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE, true)
+            backedUpItem(
+                DeviceGridState.KEY_DEVICE_TYPE,
+                InvariantDeviceProfile.TYPE_PHONE,
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
+        @JvmField
+        val DB_FILE =
+            backedUpItem(DeviceGridState.KEY_DB_FILE, "", EncryptionType.MOVE_TO_DEVICE_PROTECTED)
         @JvmField
         val SHOULD_SHOW_SMARTSPACE =
-            backedUpItem("SHOULD_SHOW_SMARTSPACE_KEY", WIDGET_ON_FIRST_SCREEN, true)
-        @JvmField val DB_FILE = backedUpItem(DeviceGridState.KEY_DB_FILE, "", true)
+            backedUpItem(
+                SHOULD_SHOW_SMARTSPACE_KEY,
+                WIDGET_ON_FIRST_SCREEN,
+                EncryptionType.DEVICE_PROTECTED
+            )
         @JvmField
         val RESTORE_DEVICE =
             backedUpItem(
                 RestoreDbTask.RESTORED_DEVICE_TYPE,
                 InvariantDeviceProfile.TYPE_PHONE,
-                true
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
             )
         @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
         @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
@@ -313,7 +363,7 @@
                 "idp_grid_name",
                 isBackedUp = true,
                 defaultValue = null,
-                isBootAware = true,
+                encryptionType = EncryptionType.MOVE_TO_DEVICE_PROTECTED,
                 type = String::class.java
             )
         @JvmField
@@ -327,7 +377,7 @@
                 "is_startup_data_boot_aware",
                 isBackedUp = false,
                 defaultValue = false,
-                isBootAware = true
+                encryptionType = EncryptionType.DEVICE_PROTECTED
             )
 
         @VisibleForTesting
@@ -335,22 +385,22 @@
         fun <T> backedUpItem(
             sharedPrefKey: String,
             defaultValue: T,
-            isBootAware: Boolean = false
+            encryptionType: EncryptionType = EncryptionType.ENCRYPTED
         ): ConstantItem<T> =
-            ConstantItem(sharedPrefKey, isBackedUp = true, defaultValue, isBootAware)
+            ConstantItem(sharedPrefKey, isBackedUp = true, defaultValue, encryptionType)
 
         @JvmStatic
         fun <T> backedUpItem(
             sharedPrefKey: String,
             type: Class<out T>,
-            isBootAware: Boolean = false,
+            encryptionType: EncryptionType = EncryptionType.ENCRYPTED,
             defaultValueFromContext: (c: Context) -> T
         ): ContextualItem<T> =
             ContextualItem(
                 sharedPrefKey,
                 isBackedUp = true,
                 defaultValueFromContext,
-                isBootAware,
+                encryptionType,
                 type
             )
 
@@ -359,9 +409,9 @@
         fun <T> nonRestorableItem(
             sharedPrefKey: String,
             defaultValue: T,
-            isBootAware: Boolean = false
+            encryptionType: EncryptionType = EncryptionType.ENCRYPTED
         ): ConstantItem<T> =
-            ConstantItem(sharedPrefKey, isBackedUp = false, defaultValue, isBootAware)
+            ConstantItem(sharedPrefKey, isBackedUp = false, defaultValue, encryptionType)
 
         @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
         @JvmStatic
@@ -386,17 +436,16 @@
 }
 
 // It is a var because the unit tests are setting this to true so they can run.
-@VisibleForTesting
-var isBootAwareStartupDataEnabled: Boolean =
-    com.android.launcher3.config.FeatureFlags.ENABLE_BOOT_AWARE_STARTUP_DATA.get()
+var moveStartupDataToDeviceProtectedStorageIsEnabled: Boolean =
+    com.android.launcher3.config.FeatureFlags.MOVE_STARTUP_DATA_TO_DEVICE_PROTECTED_STORAGE.get()
 
-private val BOOT_AWARE_ITEMS: MutableSet<ConstantItem<*>> = mutableSetOf()
+private val ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE: MutableSet<ConstantItem<*>> = mutableSetOf()
 
 abstract class Item {
     abstract val sharedPrefKey: String
     abstract val isBackedUp: Boolean
     abstract val type: Class<*>
-    abstract val isBootAware: Boolean
+    abstract val encryptionType: EncryptionType
     val sharedPrefFile: String
         get() = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY
 
@@ -407,13 +456,16 @@
     override val sharedPrefKey: String,
     override val isBackedUp: Boolean,
     val defaultValue: T,
-    override val isBootAware: Boolean,
+    override val encryptionType: EncryptionType,
     // 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() {
     init {
-        if (isBootAware && isBootAwareStartupDataEnabled) {
-            BOOT_AWARE_ITEMS.add(this)
+        if (
+            encryptionType == EncryptionType.MOVE_TO_DEVICE_PROTECTED &&
+                moveStartupDataToDeviceProtectedStorageIsEnabled
+        ) {
+            ITEMS_TO_MOVE_TO_DEVICE_PROTECTED_STORAGE.add(this)
         }
     }
 }
@@ -422,7 +474,7 @@
     override val sharedPrefKey: String,
     override val isBackedUp: Boolean,
     private val defaultSupplier: (c: Context) -> T,
-    override val isBootAware: Boolean,
+    override val encryptionType: EncryptionType,
     override val type: Class<out T>
 ) : Item() {
     private var default: T? = null
@@ -434,3 +486,9 @@
         return default!!
     }
 }
+
+enum class EncryptionType {
+    ENCRYPTED,
+    DEVICE_PROTECTED,
+    MOVE_TO_DEVICE_PROTECTED
+}
diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt
new file mode 100644
index 0000000..679f7d4
--- /dev/null
+++ b/src/com/android/launcher3/ModelCallbacks.kt
@@ -0,0 +1,96 @@
+package com.android.launcher3
+
+import androidx.annotation.UiThread
+import com.android.launcher3.model.BgDataModel
+import com.android.launcher3.model.data.AppInfo
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.LauncherAppWidgetInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.popup.PopupContainerWithArrow
+import com.android.launcher3.util.ComponentKey
+import com.android.launcher3.util.PackageUserKey
+import com.android.launcher3.util.Preconditions
+import com.android.launcher3.widget.model.WidgetsListBaseEntry
+import java.util.function.Predicate
+
+class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks {
+    override fun preAddApps() {
+        // If there's an undo snackbar, force it to complete to ensure empty screens are removed
+        // before trying to add new items.
+        launcher.modelWriter.commitDelete()
+        val snackbar =
+            AbstractFloatingView.getOpenView<AbstractFloatingView>(
+                launcher,
+                AbstractFloatingView.TYPE_SNACKBAR
+            )
+        snackbar?.post { snackbar.close(true) }
+    }
+
+    @UiThread
+    override fun bindAllApplications(
+        apps: Array<AppInfo?>?,
+        flags: Int,
+        packageUserKeytoUidMap: Map<PackageUserKey?, Int?>?
+    ) {
+        Preconditions.assertUIThread()
+        val hadWorkApps = launcher.appsView.shouldShowTabs()
+        launcher.appsView.appsStore.setApps(apps, flags, packageUserKeytoUidMap)
+        PopupContainerWithArrow.dismissInvalidPopup(launcher)
+        if (hadWorkApps != launcher.appsView.shouldShowTabs()) {
+            launcher.stateManager.goToState(LauncherState.NORMAL)
+        }
+    }
+
+    /**
+     * Copies LauncherModel's map of activities to shortcut counts to Launcher's. This is necessary
+     * because LauncherModel's map is updated in the background, while Launcher runs on the UI.
+     */
+    override fun bindDeepShortcutMap(deepShortcutMapCopy: HashMap<ComponentKey?, Int?>?) {
+        launcher.popupDataProvider.setDeepShortcutMap(deepShortcutMapCopy)
+    }
+
+    override fun bindIncrementalDownloadProgressUpdated(app: AppInfo?) {
+        launcher.appsView.appsStore.updateProgressBar(app)
+    }
+
+    override fun bindWidgetsRestored(widgets: ArrayList<LauncherAppWidgetInfo?>?) {
+        launcher.workspace.widgetsRestored(widgets)
+    }
+
+    /**
+     * Some shortcuts were updated in the background. Implementation of the method from
+     * LauncherModel.Callbacks.
+     *
+     * @param updated list of shortcuts which have changed.
+     */
+    override fun bindWorkspaceItemsChanged(updated: List<WorkspaceItemInfo?>) {
+        if (updated.isNotEmpty()) {
+            launcher.workspace.updateWorkspaceItems(updated, launcher)
+            PopupContainerWithArrow.dismissInvalidPopup(launcher)
+        }
+    }
+
+    /**
+     * Update the state of a package, typically related to install state. Implementation of the
+     * method from LauncherModel.Callbacks.
+     */
+    override fun bindRestoreItemsChange(updates: HashSet<ItemInfo?>?) {
+        launcher.workspace.updateRestoreItems(updates, launcher)
+    }
+
+    /**
+     * A package was uninstalled/updated. We take both the super set of packageNames in addition to
+     * specific applications to remove, the reason being that this can be called when a package is
+     * updated as well. In that scenario, we only remove specific components from the workspace and
+     * hotseat, where as package-removal should clear all items by package name.
+     */
+    override fun bindWorkspaceComponentsRemoved(matcher: Predicate<ItemInfo?>?) {
+        launcher.workspace.removeItemsByMatcher(matcher)
+        launcher.dragController.onAppsRemoved(matcher)
+        PopupContainerWithArrow.dismissInvalidPopup(launcher)
+    }
+
+    override fun bindAllWidgets(allWidgets: List<WidgetsListBaseEntry?>?) {
+        launcher.popupDataProvider.allWidgets = allWidgets
+    }
+}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index d728aa1..0b8bdeb 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -152,9 +152,6 @@
     // TODO(Block 8): Clean up flags
 
     // TODO(Block 9): Clean up flags
-
-    public static final BooleanFlag SHOW_DOT_PAGINATION = getDebugFlag(270395278,
-            "SHOW_DOT_PAGINATION", ENABLED, "Enable showing dot pagination in workspace");
     public static final BooleanFlag UNFOLDED_WIDGET_PICKER = getDebugFlag(301918659,
             "UNFOLDED_WIDGET_PICKER", DISABLED, "Enable new widget picker that takes "
                     + "advantage of the unfolded foldable format");
@@ -267,10 +264,11 @@
             "Enables taskbar pinning to allow user to switch between transient and persistent "
                     + "taskbar flavors");
 
-    public static final BooleanFlag ENABLE_BOOT_AWARE_STARTUP_DATA = getDebugFlag(251502424,
-            "ENABLE_BOOT_AWARE_STARTUP_DATA", DISABLED, "Marks LauncherPref data as (and allows it "
-                    + "to) available while the device is locked. Enabling this causes a 1-time "
-                    + "migration of certain SharedPreferences data. Improves startup latency.");
+    public static final BooleanFlag MOVE_STARTUP_DATA_TO_DEVICE_PROTECTED_STORAGE = getDebugFlag(
+            251502424, "ENABLE_BOOT_AWARE_STARTUP_DATA", DISABLED,
+            "Marks LauncherPref data as (and allows it to) available while the device is"
+                    + " locked. Enabling this causes a 1-time movement of certain SharedPreferences"
+                    + " data. Improves startup latency.");
 
     // TODO(Block 18): Clean up flags
     public static final BooleanFlag ENABLE_APP_PAIRS = getDebugFlag(274189428,
@@ -314,16 +312,6 @@
                     + "waiting for SystemUI and then merging the SystemUI progress whenever we "
                     + "start receiving the events");
 
-    // TODO(Block 23): Clean up flags
-    // Aconfig migration complete for ENABLE_GRID_ONLY_OVERVIEW.
-    @VisibleForTesting
-    public static final BooleanFlag ENABLE_GRID_ONLY_OVERVIEW = getDebugFlag(270397206,
-            "ENABLE_GRID_ONLY_OVERVIEW", TEAMFOOD,
-            "Enable a grid-only overview without a focused task.");
-    public static boolean enableGridOnlyOverview() {
-        return ENABLE_GRID_ONLY_OVERVIEW.get() || Flags.enableGridOnlyOverview();
-    }
-
     // Aconfig migration complete for ENABLE_OVERVIEW_ICON_MENU.
     @VisibleForTesting
     public static final BooleanFlag ENABLE_OVERVIEW_ICON_MENU = getDebugFlag(257950105,
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 6ba6642..15666cf 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -17,7 +17,9 @@
 package com.android.launcher3.model;
 
 import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
+import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE;
 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+import static com.android.launcher3.config.FeatureFlags.SMARTSPACE_AS_A_WIDGET;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
@@ -298,17 +300,17 @@
             logASplit("bindWidgets");
             verifyNotStopped();
 
-            if (LauncherPrefs.get(mApp.getContext()).get(LauncherPrefs.SHOULD_SHOW_SMARTSPACE)) {
+            LauncherPrefs prefs = LauncherPrefs.get(mApp.getContext());
+            if (SMARTSPACE_AS_A_WIDGET.get() && prefs.get(SHOULD_SHOW_SMARTSPACE)) {
                 mLauncherBinder.bindSmartspaceWidget();
                 // Turn off pref.
-                LauncherPrefs.get(mApp.getContext()).putSync(
-                        LauncherPrefs.backedUpItem(
-                                        LauncherPrefs.SHOULD_SHOW_SMARTSPACE_KEY,
-                                        WIDGET_ON_FIRST_SCREEN,
-                                        true)
-                                .to(false));
+                prefs.putSync(SHOULD_SHOW_SMARTSPACE.to(false));
                 logASplit("bindSmartspaceWidget");
                 verifyNotStopped();
+            } else if (!SMARTSPACE_AS_A_WIDGET.get() && WIDGET_ON_FIRST_SCREEN
+                    && !prefs.get(LauncherPrefs.SHOULD_SHOW_SMARTSPACE)) {
+                // Turn on pref.
+                prefs.putSync(SHOULD_SHOW_SMARTSPACE.to(true));
             }
 
             if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index e10e72d..139efc3 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -54,6 +54,7 @@
 import com.android.launcher3.AutoInstallsLayout.SourceResources;
 import com.android.launcher3.ConstantItem;
 import com.android.launcher3.DefaultLayoutParser;
+import com.android.launcher3.EncryptionType;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherFiles;
@@ -499,11 +500,11 @@
     private ConstantItem<Boolean> getEmptyDbCreatedKey(String dbName) {
         if (mContext instanceof SandboxContext) {
             return LauncherPrefs.nonRestorableItem(EMPTY_DATABASE_CREATED,
-                    false /* default value */, false /* boot aware */);
+                    false /* default value */, EncryptionType.ENCRYPTED);
         }
         String key = TextUtils.equals(dbName, LauncherFiles.LAUNCHER_DB)
                 ? EMPTY_DATABASE_CREATED : EMPTY_DATABASE_CREATED + "@" + dbName;
-        return LauncherPrefs.backedUpItem(key, false /* default value */, false /* boot aware */);
+        return LauncherPrefs.backedUpItem(key, false /* default value */, EncryptionType.ENCRYPTED);
     }
 
     /**
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index ce71275..323b3a7 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.pageindicators;
 
-import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -69,7 +67,7 @@
     private static final int PAGE_INDICATOR_ALPHA = 255;
     private static final int DOT_ALPHA = 128;
     private static final float DOT_ALPHA_FRACTION = 0.5f;
-    private static final int DOT_GAP_FACTOR = SHOW_DOT_PAGINATION.get() ? 4 : 3;
+    private static final int DOT_GAP_FACTOR = 4;
     private static final int VISIBLE_ALPHA = 255;
     private static final int INVISIBLE_ALPHA = 0;
     private Paint mPaginationPaint;
@@ -153,10 +151,7 @@
         mPaginationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mPaginationPaint.setStyle(Style.FILL);
         mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
-        mDotRadius = (SHOW_DOT_PAGINATION.get()
-                ? getResources().getDimension(R.dimen.page_indicator_dot_size_v2)
-                : getResources().getDimension(R.dimen.page_indicator_dot_size))
-                / 2;
+        mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
         mCircleGap = DOT_GAP_FACTOR * mDotRadius;
         setOutlineProvider(new MyOutlineProver());
         mIsRtl = Utilities.isRtl(getResources());
@@ -164,7 +159,7 @@
 
     @Override
     public void setScroll(int currentScroll, int totalScroll) {
-        if (SHOW_DOT_PAGINATION.get() && currentScroll == 0 && totalScroll == 0) {
+        if (currentScroll == 0 && totalScroll == 0) {
             CURRENT_POSITION.set(this, (float) mActivePage);
             return;
         }
@@ -217,7 +212,7 @@
 
     @Override
     public void setShouldAutoHide(boolean shouldAutoHide) {
-        mShouldAutoHide = shouldAutoHide && SHOW_DOT_PAGINATION.get();
+        mShouldAutoHide = shouldAutoHide;
         if (shouldAutoHide && mPaginationPaint.getAlpha() > INVISIBLE_ALPHA) {
             hideAfterDelay();
         } else if (!shouldAutoHide) {
@@ -420,16 +415,14 @@
             int alpha = mPaginationPaint.getAlpha();
 
             // Here we draw the dots
-            mPaginationPaint.setAlpha(SHOW_DOT_PAGINATION.get()
-                    ? ((int) (alpha * DOT_ALPHA_FRACTION))
-                    : DOT_ALPHA);
+            mPaginationPaint.setAlpha((int) (alpha * DOT_ALPHA_FRACTION));
             for (int i = 0; i < mNumPages; i++) {
                 canvas.drawCircle(x, y, mDotRadius, mPaginationPaint);
                 x += circleGap;
             }
 
             // Here we draw the current page indicator
-            mPaginationPaint.setAlpha(SHOW_DOT_PAGINATION.get() ? alpha : PAGE_INDICATOR_ALPHA);
+            mPaginationPaint.setAlpha(alpha);
             canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint);
         }
     }
@@ -498,7 +491,7 @@
         @Override
         public void onAnimationEnd(Animator animation) {
             if (!mCancelled) {
-                if (mShouldAutoHide && SHOW_DOT_PAGINATION.get()) {
+                if (mShouldAutoHide) {
                     hideAfterDelay();
                 }
                 mAnimator = null;
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 7b4e248..6950fb5 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -25,14 +25,13 @@
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
 
-import android.app.Activity;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.os.Handler;
 import android.os.Message;
 
-import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.BaseActivity;
@@ -62,8 +61,8 @@
     public static final int REQUEST_ROTATE = 1;
     public static final int REQUEST_LOCK = 2;
 
-    @Nullable
-    private BaseActivity mActivity;
+    @NonNull
+    private final BaseActivity mActivity;
     private final Handler mRequestOrientationHandler;
 
     private boolean mIgnoreAutoRotateSettings;
@@ -92,14 +91,14 @@
     // Initialize mLastActivityFlags to a value not used by SCREEN_ORIENTATION flags
     private int mLastActivityFlags = -999;
 
-    public RotationHelper(BaseActivity activity) {
+    public RotationHelper(@NonNull BaseActivity activity) {
         mActivity = activity;
         mRequestOrientationHandler =
                 new Handler(UI_HELPER_EXECUTOR.getLooper(), this::setOrientationAsync);
     }
 
-    private void setIgnoreAutoRotateSettings(boolean ignoreAutoRotateSettings,
-            DisplayController.Info info) {
+    private void setIgnoreAutoRotateSettings(boolean ignoreAutoRotateSettings) {
+        if (mDestroyed) return;
         // On large devices we do not handle auto-rotate differently.
         mIgnoreAutoRotateSettings = ignoreAutoRotateSettings;
         if (!mIgnoreAutoRotateSettings) {
@@ -122,58 +121,54 @@
 
     @Override
     public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
+        if (mDestroyed) return;
         boolean ignoreAutoRotateSettings = info.isTablet(info.realBounds);
         if (mIgnoreAutoRotateSettings != ignoreAutoRotateSettings) {
-            setIgnoreAutoRotateSettings(ignoreAutoRotateSettings, info);
+            setIgnoreAutoRotateSettings(ignoreAutoRotateSettings);
             notifyChange();
         }
     }
 
     public void setStateHandlerRequest(int request) {
-        if (mStateHandlerRequest != request) {
-            mStateHandlerRequest = request;
-            notifyChange();
-        }
+        if (mDestroyed || mStateHandlerRequest == request) return;
+        mStateHandlerRequest = request;
+        notifyChange();
     }
 
     public void setCurrentTransitionRequest(int request) {
-        if (mCurrentTransitionRequest != request) {
-            mCurrentTransitionRequest = request;
-            notifyChange();
-        }
+        if (mDestroyed || mCurrentTransitionRequest == request) return;
+        mCurrentTransitionRequest = request;
+        notifyChange();
     }
 
     public void setCurrentStateRequest(int request) {
-        if (mCurrentStateRequest != request) {
-            mCurrentStateRequest = request;
-            notifyChange();
-        }
+        if (mDestroyed || mCurrentStateRequest == request) return;
+        mCurrentStateRequest = request;
+        notifyChange();
     }
 
     // Used by tests only.
     public void forceAllowRotationForTesting(boolean allowRotation) {
+        if (mDestroyed) return;
         mForceAllowRotationForTesting = allowRotation;
         notifyChange();
     }
 
     public void initialize() {
-        if (!mInitialized) {
-            mInitialized = true;
-            DisplayController displayController = DisplayController.INSTANCE.get(mActivity);
-            DisplayController.Info info = displayController.getInfo();
-            setIgnoreAutoRotateSettings(info.isTablet(info.realBounds), info);
-            displayController.addChangeListener(this);
-            notifyChange();
-        }
+        if (mInitialized) return;
+        mInitialized = true;
+        DisplayController displayController = DisplayController.INSTANCE.get(mActivity);
+        DisplayController.Info info = displayController.getInfo();
+        setIgnoreAutoRotateSettings(info.isTablet(info.realBounds));
+        displayController.addChangeListener(this);
+        notifyChange();
     }
 
     public void destroy() {
-        if (!mDestroyed) {
-            mDestroyed = true;
-            DisplayController.INSTANCE.get(mActivity).removeChangeListener(this);
-            LauncherPrefs.get(mActivity).removeListener(this, ALLOW_ROTATION);
-            mActivity = null;
-        }
+        if (mDestroyed) return;
+        mDestroyed = true;
+        DisplayController.INSTANCE.get(mActivity).removeChangeListener(this);
+        LauncherPrefs.get(mActivity).removeListener(this, ALLOW_ROTATION);
     }
 
     private void notifyChange() {
@@ -206,10 +201,8 @@
 
     @WorkerThread
     private boolean setOrientationAsync(Message msg) {
-        Activity activity = mActivity;
-        if (activity != null) {
-            activity.setRequestedOrientation(msg.what);
-        }
+        if (mDestroyed) return true;
+        mActivity.setRequestedOrientation(msg.what);
         return true;
     }
 
@@ -228,8 +221,10 @@
     public String toString() {
         return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d, "
                         + "mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, "
-                        + "mHomeRotationEnabled=%b, mForceAllowRotationForTesting=%b]",
+                        + "mHomeRotationEnabled=%b, mForceAllowRotationForTesting=%b,"
+                        + " mDestroyed=%b]",
                 mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags,
-                mIgnoreAutoRotateSettings, mHomeRotationEnabled, mForceAllowRotationForTesting);
+                mIgnoreAutoRotateSettings, mHomeRotationEnabled, mForceAllowRotationForTesting,
+                mDestroyed);
     }
 }
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index a75f326..e52e878 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -16,7 +16,7 @@
 package com.android.launcher3.testing;
 
 import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
-import static com.android.launcher3.config.FeatureFlags.enableGridOnlyOverview;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
 import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java
index 07000ed..c622b71 100644
--- a/src/com/android/launcher3/util/Executors.java
+++ b/src/com/android/launcher3/util/Executors.java
@@ -49,6 +49,12 @@
             POOL_SIZE, POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
 
     /**
+     * An {@link LooperExecutor} to be used with async task where order is important.
+     */
+    public static final LooperExecutor ORDERED_BG_EXECUTOR = new LooperExecutor(
+            createAndStartNewLooper("BackgroundExecutor", THREAD_PRIORITY_BACKGROUND));
+
+    /**
      * Returns the executor for running tasks on the main thread.
      */
     public static final LooperExecutor MAIN_EXECUTOR =
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 2c094f2..37ec65c 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -39,6 +39,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
+import android.view.WindowInsetsController;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.Button;
@@ -818,7 +819,10 @@
     @Override
     public void onDragStart(boolean start, float startDisplacement) {
         super.onDragStart(start, startDisplacement);
-        getWindowInsetsController().hide(WindowInsets.Type.ime());
+        WindowInsetsController insetsController = getWindowInsetsController();
+        if (insetsController != null) {
+            insetsController.hide(WindowInsets.Type.ime());
+        }
     }
 
     @Nullable private View getViewToShowEducationTip() {
diff --git a/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
index 582ab23..6b27503 100644
--- a/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
+++ b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java
@@ -41,10 +41,6 @@
 
     default void hideOverlay(int duration) { }
 
-    default boolean startSearch(byte[] config, Bundle extras) {
-        return false;
-    }
-
     @Override
     default void onActivityCreated(Activity activity, Bundle bundle) { }
 
diff --git a/tests/Android.bp b/tests/Android.bp
index 62d232c..da447b3 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -42,7 +42,8 @@
 filegroup {
     name: "launcher-oop-tests-src",
     srcs: [
-      "src/com/android/launcher3/allapps/OopTaplOpenCloseAllApps.java",
+      "src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java",
+      "src/com/android/launcher3/allapps/TaplTestsAllAppsIconsWorking.java",
       "src/com/android/launcher3/appiconmenu/TaplAppIconMenuTest.java",
       "src/com/android/launcher3/dragging/TaplDragTest.java",
       "src/com/android/launcher3/dragging/TaplUninstallRemove.java",
@@ -50,6 +51,7 @@
       "src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
       "src/com/android/launcher3/ui/TaplTestsLauncher3.java",
       "src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java",
+      "src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java",
       "src/com/android/launcher3/util/LauncherLayoutBuilder.java",
       "src/com/android/launcher3/util/TestUtil.java",
       "src/com/android/launcher3/util/Wait.java",
@@ -61,7 +63,6 @@
       "src/com/android/launcher3/util/rule/ShellCommandRule.java",
       "src/com/android/launcher3/util/rule/TestIsolationRule.java",
       "src/com/android/launcher3/util/rule/TestStabilityRule.java",
-      "src/com/android/launcher3/util/rule/TISBindRule.java",
       "src/com/android/launcher3/util/viewcapture_analysis/*.java",
       "src/com/android/launcher3/testcomponent/BaseTestingActivity.java",
       "src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java",
@@ -89,6 +90,7 @@
         "androidx.test.espresso.contrib",
         "androidx.test.espresso.intents",
         "androidx.test.uiautomator_uiautomator",
+        "mockito-kotlin2",
         "mockito-target-extended-minus-junit4",
         "launcher_log_protos_lite",
         "truth-prebuilt",
diff --git a/tests/assets/databases/workspace_items.sql b/tests/assets/databases/workspace_items.sql
new file mode 100644
index 0000000..68f7d50
--- /dev/null
+++ b/tests/assets/databases/workspace_items.sql
@@ -0,0 +1,79 @@
+DROP TABLE IF EXISTS 'favorites';
+CREATE TABLE favorites (_id INTEGER PRIMARY KEY,title TEXT,intent TEXT,container INTEGER,screen INTEGER,cellX INTEGER,cellY INTEGER,spanX INTEGER,spanY INTEGER,itemType INTEGER,appWidgetId INTEGER NOT NULL DEFAULT -1,icon BLOB,appWidgetProvider TEXT,modified INTEGER NOT NULL DEFAULT 0,restored INTEGER NOT NULL DEFAULT 0,profileId INTEGER DEFAULT 0,rank INTEGER NOT NULL DEFAULT 0,options INTEGER NOT NULL DEFAULT 0,appWidgetSource INTEGER NOT NULL DEFAULT -1);
+INSERT INTO 'favorites' VALUES(1,'Phone','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.dialer/.extensions.GoogleDialtactsActivity;end',-101,0,0,0,1,1,0,-1,NULL,NULL,0,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(2,'Messages','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.messaging/.ui.ConversationListActivity;end',-101,1,1,0,1,1,0,-1,NULL,NULL,0,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(3,'Play Store','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.vending/.AssetBrowserActivity;end',-101,2,2,0,1,1,0,-1,NULL,NULL,0,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(4,'Chrome','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.chrome/com.google.android.apps.chrome.Main;end',-101,3,3,0,1,1,0,-1,NULL,NULL,0,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(6,'Settings','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.settings/.Settings;end',-100,0,0,1,1,1,0,-1,X'',NULL,1693590010654,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(7,'Drive','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.docs/.app.NewMainProxyActivity;end',-100,0,1,2,1,1,0,-1,X'',NULL,1693589533751,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(8,'Better Bug','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.internal.betterbug/com.google.android.apps.betterbug.bugslist.BugsListActivity;end',16,0,0,0,1,1,0,-1,X'',NULL,1693589597917,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(9,'Flag Flipper','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.theflippinapp/.MainActivity;end',-100,0,3,2,1,1,0,-1,X'',NULL,1693589546863,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(10,NULL,NULL,-100,0,1,3,3,2,4,3,NULL,'com.google.android.apps.docs/com.google.android.apps.docs.drive.widget.suggestion.SuggestionAppWidgetProvider',1693589554018,0,0,0,0,-112);
+INSERT INTO 'favorites' VALUES(11,'Scan','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.apps.docs;component=com.google.android.apps.docs/.app.NewMainProxyActivity;S.shortcut_id=launcher_shortcut_scan;end',-100,0,0,3,1,1,6,-1,X'',NULL,1693589559601,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(12,'Upload','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.apps.docs;component=com.google.android.apps.docs/.app.NewMainProxyActivity;S.shortcut_id=launcher_shortcut_upload;end',-100,0,0,4,1,1,6,-1,X'',NULL,1693589576040,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(13,'Search','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.apps.docs;component=com.google.android.apps.docs/.app.NewMainProxyActivity;S.shortcut_id=launcher_shortcut_search;end',-100,0,4,4,1,1,6,-1,X'',NULL,1693589582487,0,0,2,0,-1);
+INSERT INTO 'favorites' VALUES(15,'BluetoothCompanionApp','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.bluetooth.companion/.MainActivity;end',-100,0,1,1,1,1,0,-1,X'',NULL,1693589594115,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(16,'Better Bug',NULL,-100,0,3,1,1,1,2,-1,NULL,NULL,1693589600675,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(17,'Better Bug','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.internal.betterbug/com.google.android.apps.betterbug.bugslist.BugsListActivity;end',16,0,1,0,1,1,0,-1,X'',NULL,1693589597936,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(18,'Better Bug','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.internal.betterbug/com.google.android.apps.betterbug.bugslist.BugsListActivity;end',16,0,0,1,1,1,0,-1,X'',NULL,1693589603123,0,0,2,0,-1);
+INSERT INTO 'favorites' VALUES(19,'Incognito Tab','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.android.chrome;component=com.android.chrome/com.google.android.apps.chrome.Main;S.shortcut_id=dynamic-new-incognito-tab-shortcut;end',-100,0,2,2,1,1,6,-1,X'',NULL,1693589609963,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(20,NULL,NULL,-100,1,0,0,3,2,4,5,NULL,'com.google.android.deskclock/com.android.alarmclock.AnalogAppWidgetProvider',1693589630235,0,0,0,0,-112);
+INSERT INTO 'favorites' VALUES(21,NULL,NULL,-100,1,1,3,2,2,4,6,NULL,'com.android.chrome/org.chromium.chrome.browser.quickactionsearchwidget.QuickActionSearchWidgetProvider$QuickActionSearchWidgetProviderDino',1693589677239,0,0,0,0,-112);
+INSERT INTO 'favorites' VALUES(22,'New tab','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.android.chrome;component=com.android.chrome/com.google.android.apps.chrome.Main;S.shortcut_id=new-tab-shortcut;end',36,0,0,0,1,1,6,-1,X'',NULL,1693589694253,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(25,'Selfie','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.GoogleCamera;component=com.google.android.GoogleCamera/com.android.camera.CameraLauncher;S.shortcut_id=selfie;end',33,0,0,0,1,1,6,-1,X'',NULL,1693589686832,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(26,'Calculator','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.calculator/com.android.calculator2.Calculator;end',-100,1,4,2,1,1,0,-1,X'',NULL,1693589656954,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(27,'Calendar','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.calendar/com.android.calendar.AllInOneActivity;end',-100,1,4,0,1,1,0,-1,X'',NULL,1693589660516,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(28,'New event','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.calendar;component=com.google.android.calendar/com.android.calendar.AllInOneActivity;S.shortcut_id=launcher_shortcuts_shortcut_new_event;end',38,0,0,0,1,1,6,-1,X'',NULL,1693589698615,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(29,'New task','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.calendar;component=com.google.android.calendar/com.android.calendar.AllInOneActivity;S.shortcut_id=launcher_shortcuts_shortcut_create_task;end',-100,1,0,4,1,1,6,-1,X'',NULL,1693589677243,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(30,'Contacts','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.contacts/com.android.contacts.activities.PeopleActivity;end',-100,1,4,1,1,1,0,-1,X'',NULL,1693589671550,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(31,'Alex Eaves','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.contacts;component=com.google.android.contacts/com.android.contacts.activities.PeopleActivity;S.shortcut_id=822i60c772551678bd93;end',-100,1,4,3,1,1,6,-1,X'',NULL,1693589681873,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(32,'Add contact','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.contacts;component=com.google.android.contacts/com.android.contacts.activities.PeopleActivity;S.shortcut_id=shortcut-add-contact;end',-100,1,3,3,1,1,6,-1,X'',NULL,1693589681880,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(33,'Call',NULL,-100,1,3,2,1,1,2,-1,NULL,NULL,1693589687263,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(34,'Contacts','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.contacts/com.android.contacts.activities.PeopleActivity;end',33,0,1,0,1,1,0,-1,X'',NULL,1693589686853,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(35,'Gmail','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.gm/.ConversationListActivityGmail;end',33,0,0,1,1,1,0,-1,X'',NULL,1693589690561,0,0,2,0,-1);
+INSERT INTO 'favorites' VALUES(36,'Files',NULL,-100,1,3,1,1,1,2,-1,NULL,NULL,1693589706696,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(37,'Files','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.nbu.files/.home.HomeActivity;end',36,0,1,0,1,1,0,-1,X'',NULL,1693589694261,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(38,NULL,NULL,-100,1,0,3,1,1,2,-1,NULL,NULL,1693589698611,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(39,'Scan','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.apps.docs;component=com.google.android.apps.docs/.app.NewMainProxyActivity;S.shortcut_id=launcher_shortcut_scan;end',38,0,1,0,1,1,6,-1,X'',NULL,1693589698621,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(40,'Search','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.apps.docs;component=com.google.android.apps.docs/.app.NewMainProxyActivity;S.shortcut_id=launcher_shortcut_search;end',-100,1,3,4,1,1,6,-1,X'',NULL,1693589702696,0,0,2,0,-1);
+INSERT INTO 'favorites' VALUES(41,'Upload','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.apps.docs;component=com.google.android.apps.docs/.app.NewMainProxyActivity;S.shortcut_id=launcher_shortcut_upload;end',-100,1,1,2,1,1,6,-1,X'',NULL,1693589706711,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(42,'Camera Obfuscator','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.internal.camera.imageobfuscator/.activities.MainActivity;end',-100,1,3,0,1,1,0,-1,X'',NULL,1693589710458,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(43,'Files','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.nbu.files/.home.HomeActivity;end',45,0,0,0,1,1,0,-1,X'',NULL,1693589727388,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(44,'Flag Flipper','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.theflippinapp/.MainActivity;end',-100,2,1,0,1,1,0,-1,X'',NULL,1693589724756,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(45,NULL,NULL,-100,2,0,0,1,1,2,-1,NULL,NULL,1693589727385,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(46,'Flag Flipper','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.theflippinapp/.MainActivity;end',45,0,1,0,1,1,0,-1,X'',NULL,1693589727398,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(47,'Gmail','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.gm/.ConversationListActivityGmail;end',-100,2,2,0,1,1,0,-1,X'',NULL,1693589730037,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(48,'Compose','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.gm;component=com.google.android.gm/.ConversationListActivityGmail;S.shortcut_id=manifest_compose_shortcut;end',-100,2,3,0,1,1,6,-1,X'',NULL,1693589733121,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(49,NULL,NULL,-100,2,0,1,3,2,4,7,NULL,'com.google.android.gm/com.google.android.gm.widget.GmailWidgetProvider',1693589740752,0,0,0,0,-112);
+INSERT INTO 'favorites' VALUES(50,NULL,NULL,-100,3,1,0,4,5,4,8,NULL,'com.google.android.calendar/com.google.android.calendar.widgetmonth.MonthViewWidgetProvider',1693589746495,0,0,0,0,-111);
+INSERT INTO 'favorites' VALUES(54,'Maps','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.maps/com.google.android.maps.MapsActivity;end',-100,2,3,2,1,1,0,-1,X'',NULL,1693589785990,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(55,'Productivity',NULL,-100,2,3,4,1,1,2,-1,NULL,NULL,1693589797590,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(56,'Work','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.apps.maps;component=com.google.android.apps.maps/com.google.android.maps.MapsActivity;S.shortcut_id=manifest_work;end',55,0,1,0,1,1,6,-1,X'',NULL,1693589789538,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(57,'Home','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.apps.maps;component=com.google.android.apps.maps/com.google.android.maps.MapsActivity;S.shortcut_id=manifest_home;end',-100,2,3,1,1,1,6,-1,X'',NULL,1693589793825,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(58,'Gyotaku','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.internal.gyotaku/.Launcher;end',-100,2,3,3,1,1,0,-1,X'',NULL,1693589797615,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(60,'Phone','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.dialer/.extensions.GoogleDialtactsActivity;end',-100,2,4,3,1,1,0,-1,X'',NULL,1693589805582,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(61,'Photos','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.photos/.home.HomeActivity;end',-100,2,4,4,1,1,0,-1,X'',NULL,1693589809050,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(63,'Pixel Logger',NULL,-100,3,0,3,1,1,2,-1,NULL,NULL,1693589820775,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(65,'Pixel Tips','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.tips/.TipsMain;end',-100,3,0,4,1,1,0,-1,X'',NULL,1693589823832,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(66,'Play Store','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.vending/.AssetBrowserActivity;end',-100,3,0,1,1,1,0,-1,X'',NULL,1693589834647,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(67,NULL,NULL,-100,4,2,0,3,2,4,10,NULL,'com.google.android.apps.youtube.music/com.google.android.apps.youtube.music.player.widget.gm3.FreeformMusicWidgetProvider',1693589842256,0,0,0,0,-112);
+INSERT INTO 'favorites' VALUES(68,NULL,NULL,-100,4,0,2,4,3,4,11,NULL,'com.google.android.apps.youtube.music/com.google.android.apps.youtube.music.player.widget.gm3.FreeformMusicWidgetProvider',1693589854706,0,0,0,0,-112);
+INSERT INTO 'favorites' VALUES(69,'YouTube','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.youtube/.app.honeycomb.Shell%24HomeActivity;end',-100,4,4,4,1,1,0,-1,X'',NULL,1693589859008,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(70,'Explore','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.youtube;component=com.google.android.youtube/.app.honeycomb.Shell%24HomeActivity;S.shortcut_id=explore-shortcut;end',-100,4,4,3,1,1,6,-1,X'',NULL,1693589867283,0,0,3,0,-1);
+INSERT INTO 'favorites' VALUES(71,'Search','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.youtube;component=com.google.android.youtube/.app.honeycomb.Shell%24HomeActivity;S.shortcut_id=search-shortcut;end',-100,4,4,2,1,1,6,-1,X'',NULL,1693589871989,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(72,'Shorts','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.youtube;component=com.google.android.youtube/.app.honeycomb.Shell%24HomeActivity;S.shortcut_id=shorts-shortcut;end',-100,4,0,1,1,1,6,-1,X'',NULL,1693589882256,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(73,'Subscriptions','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.google.android.youtube;component=com.google.android.youtube/.app.honeycomb.Shell%24HomeActivity;S.shortcut_id=subscriptions-shortcut;end',-100,4,0,0,1,1,6,-1,X'',NULL,1693589888244,0,0,2,0,-1);
+INSERT INTO 'favorites' VALUES(74,'Safety','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.safetyhub/.LauncherActivity;end',-100,4,1,1,1,1,0,-1,X'',NULL,1693589891720,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(75,'Wi‑Fi','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.android.settings;component=com.android.settings/.Settings;S.shortcut_id=manifest-shortcut-wifi;end',-100,5,2,0,1,1,6,-1,X'',NULL,1693589897994,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(76,'Data usage','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.android.settings;component=com.android.settings/.Settings;S.shortcut_id=manifest-shortcut-data-usage;end',-100,5,3,1,1,1,6,-1,X'',NULL,1693589904331,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(77,'Battery','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.android.settings;component=com.android.settings/.Settings;S.shortcut_id=manifest-shortcut-battery;end',-100,5,1,2,1,1,6,-1,X'',NULL,1693589907795,0,0,2,0,-1);
+INSERT INTO 'favorites' VALUES(78,'Internet','#Intent;action=android.intent.action.MAIN;category=com.android.launcher3.DEEP_SHORTCUT;launchFlags=0x10200000;package=com.android.settings;component=com.android.settings/.Settings;S.shortcut_id=component-shortcut-com.android.settings%2F.Settings%24NetworkProviderSettingsActivity;end',-100,5,2,1,1,1,6,-1,X'',NULL,1693589914187,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(79,'Safety','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.safetyhub/.LauncherActivity;end',-100,5,2,3,1,1,0,-1,X'',NULL,1693589917447,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(80,'Recorder','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.recorder/.ui.recordings.MainActivity;end',-100,5,0,4,1,1,0,-1,X'',NULL,1693589920866,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(81,'Maps','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.maps/com.google.android.maps.MapsActivity;end',82,0,0,0,1,1,0,-1,X'',NULL,1693589929103,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(82,NULL,NULL,-100,5,3,3,1,1,2,-1,NULL,NULL,1693589929099,0,0,0,0,-1);
+INSERT INTO 'favorites' VALUES(83,'Flag Flipper','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.theflippinapp/.MainActivity;end',82,0,1,0,1,1,0,-1,X'',NULL,1693589929134,0,0,1,0,-1);
+INSERT INTO 'favorites' VALUES(84,'Gmail','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.gm/.ConversationListActivityGmail;end',82,0,2,0,1,1,0,-1,X'',NULL,1693589938320,0,0,2,0,-1);
+INSERT INTO 'favorites' VALUES(85,'Google','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.googlequicksearchbox/.SearchActivity;end',82,0,0,1,1,1,0,-1,X'',NULL,1693589938321,0,0,3,0,-1);
+INSERT INTO 'favorites' VALUES(86,'Calendar','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.calendar/com.android.calendar.AllInOneActivity;end',82,0,1,1,1,1,0,-1,X'',NULL,1693589938316,0,0,4,0,-1);
+INSERT INTO 'favorites' VALUES(87,'Chrome','#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.chrome/com.google.android.apps.chrome.Main;end',82,0,2,1,1,1,0,-1,X'',NULL,1693589941181,0,0,5,0,-1);
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index a52ba9e..84fa988 100644
--- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -38,10 +38,10 @@
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
-import org.mockito.ArgumentMatchers
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.`when` as whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
 
 /**
  * This is an abstract class for DeviceProfile tests that create an InvariantDeviceProfile based on
@@ -52,8 +52,8 @@
 abstract class AbstractDeviceProfileTest {
     protected var context: Context? = null
     protected open val runningContext: Context = ApplicationProvider.getApplicationContext()
-    private var displayController: DisplayController = mock(DisplayController::class.java)
-    private var windowManagerProxy: WindowManagerProxy = mock(WindowManagerProxy::class.java)
+    private val displayController: DisplayController = mock()
+    private val windowManagerProxy: WindowManagerProxy = mock()
     private lateinit var originalDisplayController: DisplayController
     private lateinit var originalWindowManagerProxy: WindowManagerProxy
 
@@ -288,11 +288,10 @@
     ) {
         val windowsBounds = perDisplayBoundsCache[displayInfo]!!
         val realBounds = windowsBounds[rotation]
-        whenever(windowManagerProxy.getDisplayInfo(ArgumentMatchers.any())).thenReturn(displayInfo)
-        whenever(windowManagerProxy.getRealBounds(ArgumentMatchers.any(), ArgumentMatchers.any()))
-            .thenReturn(realBounds)
-        whenever(windowManagerProxy.getRotation(ArgumentMatchers.any())).thenReturn(rotation)
-        whenever(windowManagerProxy.getNavigationMode(ArgumentMatchers.any()))
+        whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo)
+        whenever(windowManagerProxy.getRealBounds(any(), any())).thenReturn(realBounds)
+        whenever(windowManagerProxy.getRotation(any())).thenReturn(rotation)
+        whenever(windowManagerProxy.getNavigationMode(any()))
             .thenReturn(
                 if (isGestureMode) NavigationMode.NO_BUTTON else NavigationMode.THREE_BUTTONS
             )
diff --git a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
index 42338bf..a421006 100644
--- a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt
@@ -27,9 +27,9 @@
 import java.io.PrintWriter
 import java.io.StringWriter
 import org.junit.Before
-import org.mockito.ArgumentMatchers.any
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when` as whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 /**
  * This is an abstract class for DeviceProfile tests that don't need the real Context and mock an
@@ -41,7 +41,7 @@
 
     protected var context: Context? = null
     protected var inv: InvariantDeviceProfile? = null
-    protected var info: Info = mock(Info::class.java)
+    protected val info: Info = mock()
     protected var windowBounds: WindowBounds? = null
     protected var isMultiWindowMode: Boolean = false
     protected var transposeLayoutWithOrientation: Boolean = false
diff --git a/tests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/src/com/android/launcher3/LauncherPrefsTest.kt
index d59e02a..88a430b 100644
--- a/tests/src/com/android/launcher3/LauncherPrefsTest.kt
+++ b/tests/src/com/android/launcher3/LauncherPrefsTest.kt
@@ -33,7 +33,8 @@
 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 }, false, Boolean::class.java)
+private val TEST_CONTEXTUAL_ITEM =
+    ContextualItem("4", true, { true }, EncryptionType.ENCRYPTED, Boolean::class.java)
 
 private const val TEST_DEFAULT_VALUE = "default"
 private const val TEST_PREF_KEY = "test_pref_key"
@@ -51,13 +52,13 @@
         @BeforeClass
         @JvmStatic
         fun setup() {
-            isBootAwareStartupDataEnabled = true
+            moveStartupDataToDeviceProtectedStorageIsEnabled = true
         }
 
         @AfterClass
         @JvmStatic
         fun teardown() {
-            isBootAwareStartupDataEnabled = false
+            moveStartupDataToDeviceProtectedStorageIsEnabled = false
         }
     }
 
@@ -203,7 +204,11 @@
     @Test
     fun put_bootAwareItem_updatesDeviceProtectedStorage() {
         val bootAwareItem =
-            LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
+            LauncherPrefs.backedUpItem(
+                TEST_PREF_KEY,
+                TEST_DEFAULT_VALUE,
+                EncryptionType.DEVICE_PROTECTED
+            )
 
         val bootAwarePrefs: SharedPreferences =
             context
@@ -220,7 +225,11 @@
     @Test
     fun put_bootAwareItem_updatesEncryptedStorage() {
         val bootAwareItem =
-            LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
+            LauncherPrefs.backedUpItem(
+                TEST_PREF_KEY,
+                TEST_DEFAULT_VALUE,
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
 
         val encryptedPrefs: SharedPreferences =
             context.getSharedPreferences(bootAwareItem.sharedPrefFile, Context.MODE_PRIVATE)
@@ -235,7 +244,11 @@
     @Test
     fun remove_bootAwareItem_removesFromDeviceProtectedStorage() {
         val bootAwareItem =
-            LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
+            LauncherPrefs.backedUpItem(
+                TEST_PREF_KEY,
+                TEST_DEFAULT_VALUE,
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
 
         val bootAwarePrefs: SharedPreferences =
             context
@@ -254,7 +267,11 @@
     @Test
     fun remove_bootAwareItem_removesFromEncryptedStorage() {
         val bootAwareItem =
-            LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
+            LauncherPrefs.backedUpItem(
+                TEST_PREF_KEY,
+                TEST_DEFAULT_VALUE,
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
 
         val encryptedPrefs: SharedPreferences =
             context.getSharedPreferences(bootAwareItem.sharedPrefFile, Context.MODE_PRIVATE)
@@ -271,7 +288,11 @@
     @Test
     fun migrate_bootAwareItemsToDeviceProtectedStorage_worksAsIntended() {
         val bootAwareItem =
-            LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true)
+            LauncherPrefs.backedUpItem(
+                TEST_PREF_KEY,
+                TEST_DEFAULT_VALUE,
+                EncryptionType.MOVE_TO_DEVICE_PROTECTED
+            )
         launcherPrefs.removeSync(bootAwareItem)
 
         val bootAwarePrefs: SharedPreferences =
@@ -303,7 +324,7 @@
             LauncherPrefs.backedUpItem(
                 TEST_PREF_KEY + "_",
                 TEST_DEFAULT_VALUE + "_",
-                isBootAware = false
+                EncryptionType.ENCRYPTED
             )
 
         val bootAwarePrefs: SharedPreferences =
diff --git a/tests/src/com/android/launcher3/allapps/OopTaplOpenCloseAllApps.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java
similarity index 84%
rename from tests/src/com/android/launcher3/allapps/OopTaplOpenCloseAllApps.java
rename to tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java
index f9dadaa..39dbcb2 100644
--- a/tests/src/com/android/launcher3/allapps/OopTaplOpenCloseAllApps.java
+++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java
@@ -21,11 +21,17 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
+import android.content.Intent;
 import android.platform.test.annotations.PlatinumTest;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.tapl.AllApps;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
@@ -37,7 +43,10 @@
  * Test that we can open and close the all apps in multiple situations.
  * The test runs in Out of process (Oop) and in process.
  */
-public class OopTaplOpenCloseAllApps extends AbstractLauncherUiTest {
+public class TaplOpenCloseAllApps extends AbstractLauncherUiTest {
+
+    public static final String READ_DEVICE_CONFIG_PERMISSION =
+            "android.permission.READ_DEVICE_CONFIG";
 
     /**
      * Calls static method initialize
@@ -188,4 +197,24 @@
             allApps.unfreeze();
         }
     }
+
+    /**
+     * Makes sure that when pressing back when AllApps is open we go back to the Home screen.
+     */
+    @FlakyTest(bugId = 256615483)
+    @Test
+    @PortraitLandscape
+    public void testPressBackFromAllAppsToHome() {
+        InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+                READ_DEVICE_CONFIG_PERMISSION);
+        assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get());
+        mLauncher.getWorkspace().switchToAllApps();
+        mLauncher.pressBack();
+        mLauncher.getWorkspace();
+        waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
+        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+        mLauncher.pressBack();
+        mLauncher.getWorkspace();
+        waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
+    }
 }
diff --git a/tests/src/com/android/launcher3/allapps/TaplTestsAllAppsIconsWorking.java b/tests/src/com/android/launcher3/allapps/TaplTestsAllAppsIconsWorking.java
new file mode 100644
index 0000000..fd4619e
--- /dev/null
+++ b/tests/src/com/android/launcher3/allapps/TaplTestsAllAppsIconsWorking.java
@@ -0,0 +1,66 @@
+/*
+ * 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.allapps;
+
+import static com.android.launcher3.ui.TaplTestsLauncher3.initialize;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.tapl.AppIcon;
+import com.android.launcher3.tapl.HomeAllApps;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * The test runs in Out of process (Oop) and in process.
+ * Makes sure the basic behaviors of Icons on AllApps are working.
+ */
+public class TaplTestsAllAppsIconsWorking extends AbstractLauncherUiTest {
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        initialize(this);
+    }
+
+    /**
+     * Makes sure we can launch an icon from All apps
+     */
+    @Test
+    @PortraitLandscape
+    public void testAppIconLaunchFromAllAppsFromHome() {
+        final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        assertTrue("Launcher internal state is not All Apps",
+                isInState(() -> LauncherState.ALL_APPS));
+
+        allApps.freeze();
+        try {
+            final AppIcon app = allApps.getAppIcon("TestActivity7");
+            assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName()));
+            executeOnLauncher(launcher -> assertTrue(
+                    "Launcher activity is the top activity; expecting another activity to be the "
+                            + "top one",
+                    isInLaunchedApp(launcher)));
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
index 1155227..78c61d5 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
@@ -24,20 +24,19 @@
 import com.android.launcher3.util.Executors
 import com.android.launcher3.util.IntArray
 import com.android.launcher3.util.TestUtil.runOnExecutorSync
-import com.android.launcher3.util.any
-import com.android.launcher3.util.eq
-import com.android.launcher3.util.same
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when` as whenever
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.same
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.whenever
 
 /** Tests for [AddWorkspaceItemsTask] */
 @SmallTest
@@ -46,12 +45,11 @@
 
     private lateinit var mDataModelCallbacks: MyCallbacks
 
-    @Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder
+    private val mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder = mock()
 
     @Before
     override fun setup() {
         super.setup()
-        MockitoAnnotations.initMocks(this)
         mDataModelCallbacks = MyCallbacks()
         Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(mDataModelCallbacks) }
             .get()
diff --git a/tests/src/com/android/launcher3/model/FactitiousDbController.kt b/tests/src/com/android/launcher3/model/FactitiousDbController.kt
new file mode 100644
index 0000000..664f23e
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/FactitiousDbController.kt
@@ -0,0 +1,64 @@
+package com.android.launcher3.model
+
+import android.content.Context
+import android.database.Cursor
+import android.database.sqlite.SQLiteDatabase
+import androidx.test.platform.app.InstrumentationRegistry
+import java.io.BufferedReader
+import java.io.InputStreamReader
+
+private val All_COLUMNS =
+    arrayOf(
+        "_id",
+        "title",
+        "intent",
+        "container",
+        "screen",
+        "cellX",
+        "cellY",
+        "spanX",
+        "spanY",
+        "itemType",
+        "appWidgetId",
+        "icon",
+        "appWidgetProvider",
+        "modified",
+        "restored",
+        "profileId",
+        "rank",
+        "options",
+        "appWidgetSource"
+    )
+private const val INSERTION_STATEMENT_FILE = "databases/workspace_items.sql"
+
+class FactitiousDbController(context: Context) : ModelDbController(context) {
+
+    private val inMemoryDb: SQLiteDatabase by lazy {
+        SQLiteDatabase.createInMemory(SQLiteDatabase.OpenParams.Builder().build()).also { db ->
+            BufferedReader(
+                    InputStreamReader(
+                        InstrumentationRegistry.getInstrumentation()
+                            .context
+                            .assets
+                            .open(INSERTION_STATEMENT_FILE)
+                    )
+                )
+                .lines()
+                .forEach { sqlStatement -> db.execSQL(sqlStatement) }
+        }
+    }
+
+    override fun query(
+        table: String,
+        projection: Array<out String>?,
+        selection: String?,
+        selectionArgs: Array<out String>?,
+        sortOrder: String?
+    ): Cursor {
+        return inMemoryDb.query(table, All_COLUMNS, selection, selectionArgs, null, null, sortOrder)
+    }
+
+    override fun loadDefaultFavoritesIfNecessary() {
+        // No-Op
+    }
+}
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
new file mode 100644
index 0000000..1421087
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -0,0 +1,104 @@
+package com.android.launcher3.model
+
+import android.appwidget.AppWidgetManager
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherModel
+import com.android.launcher3.LauncherModel.LoaderTransaction
+import com.android.launcher3.icons.IconCache
+import com.android.launcher3.icons.cache.CachingLogic
+import com.android.launcher3.icons.cache.IconCacheUpdateHandler
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.LooperIdleLock
+import com.google.common.truth.Truth
+import java.util.concurrent.CountDownLatch
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LoaderTaskTest {
+    @Mock private lateinit var app: LauncherAppState
+    @Mock private lateinit var bgAllAppsList: AllAppsList
+    @Mock private lateinit var modelDelegate: ModelDelegate
+    @Mock private lateinit var launcherBinder: LauncherBinder
+    @Mock private lateinit var launcherModel: LauncherModel
+    @Mock private lateinit var transaction: LoaderTransaction
+    @Mock private lateinit var iconCache: IconCache
+    @Mock private lateinit var idleLock: LooperIdleLock
+    @Mock private lateinit var iconCacheUpdateHandler: IconCacheUpdateHandler
+    @Mock private lateinit var appWidgetManager: AppWidgetManager
+
+    @Before
+    fun setup() {
+        val context = InstrumentationRegistry.getInstrumentation().targetContext
+        val idp =
+            InvariantDeviceProfile.INSTANCE[context].apply {
+                numRows = 5
+                numColumns = 6
+                numDatabaseHotseatIcons = 5
+            }
+
+        MockitoAnnotations.initMocks(this)
+        `when`(app.context).thenReturn(context)
+        `when`(app.model).thenReturn(launcherModel)
+        `when`(launcherModel.beginLoader(any(LoaderTask::class.java))).thenReturn(transaction)
+        `when`(app.iconCache).thenReturn(iconCache)
+        `when`(launcherModel.modelDbController).thenReturn(FactitiousDbController(context))
+        `when`(app.invariantDeviceProfile).thenReturn(idp)
+        `when`(launcherBinder.newIdleLock(any(LoaderTask::class.java))).thenReturn(idleLock)
+        `when`(idleLock.awaitLocked(1000)).thenReturn(false)
+        `when`(iconCache.updateHandler).thenReturn(iconCacheUpdateHandler)
+        `when`(appWidgetManager.getInstalledProvidersForProfile(any(UserHandle::class.java)))
+            .thenReturn(emptyList())
+    }
+
+    @Test
+    fun loadsDataProperly() =
+        with(BgDataModel()) {
+            LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder)
+                .runSyncOnBackgroundThread()
+            Truth.assertThat(workspaceItems.size).isAtLeast(25)
+            Truth.assertThat(appWidgets.size).isAtLeast(7)
+            Truth.assertThat(folders.size()).isAtLeast(8)
+            Truth.assertThat(itemsIdMap.size()).isAtLeast(40)
+        }
+
+    @Test
+    fun bindsLoadedDataCorrectly() {
+        LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
+            .runSyncOnBackgroundThread()
+
+        verify(launcherBinder).bindWorkspace(true, false)
+        verify(modelDelegate).workspaceLoadComplete()
+        verify(modelDelegate).loadAndBindAllAppsItems(any(), any(), any())
+        verify(launcherBinder).bindAllApps()
+        verify(iconCacheUpdateHandler, times(4)).updateIcons(any(), any<CachingLogic<Any>>(), any())
+        verify(launcherBinder).bindDeepShortcuts()
+        verify(launcherBinder).bindWidgets()
+        verify(modelDelegate).loadAndBindOtherItems(any())
+        verify(iconCacheUpdateHandler).finish()
+        verify(modelDelegate).modelLoadComplete()
+        verify(transaction).commit()
+    }
+}
+
+private fun LoaderTask.runSyncOnBackgroundThread() {
+    val latch = CountDownLatch(1)
+    MODEL_EXECUTOR.execute {
+        run()
+        latch.countDown()
+    }
+    latch.await()
+}
diff --git a/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java b/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java
index 2cdcf24..6c2950c 100644
--- a/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java
+++ b/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java
@@ -20,21 +20,30 @@
 
 import static com.android.launcher3.BubbleTextView.DISPLAY_ALL_APPS;
 import static com.android.launcher3.BubbleTextView.DISPLAY_PREDICTION_ROW;
+import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT;
+import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT_SMALL;
+
+import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.graphics.Typeface;
 import android.platform.test.flag.junit.SetFlagsRule;
+import android.os.UserHandle;
 import android.view.ViewGroup;
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Flags;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.search.StringMatcherUtility;
 import com.android.launcher3.util.ActivityContextWrapper;
+import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.views.BaseDragLayer;
 
@@ -55,6 +64,8 @@
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private static final StringMatcherUtility.StringMatcher
             MATCHER = StringMatcherUtility.StringMatcher.getInstance();
+    private static final UserHandle WORK_HANDLE = new UserHandle(13);
+    private static final int WORK_FLAG = 1;
     private static final int ONE_LINE = 1;
     private static final int TWO_LINE = 2;
     private static final String TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT = "Battery Stats";
@@ -81,6 +92,7 @@
     private ItemInfoWithIcon mItemInfoWithIcon;
     private Context mContext;
     private int mLimitedWidth;
+    private AppInfo mGmailAppInfo;
 
     @Before
     public void setUp() throws Exception {
@@ -110,6 +122,9 @@
                 return null;
             }
         };
+        ComponentName componentName = new ComponentName(mContext,
+                "com.android.launcher3.tests.Activity" + "Gmail");
+        mGmailAppInfo = new AppInfo(componentName, "Gmail", WORK_HANDLE, new Intent());
     }
 
     @Test
@@ -359,4 +374,28 @@
 
         assertEquals(TWO_LINE, mBubbleTextView.getLineCount());
     }
+
+    @Test
+    public void applyIconAndLabel_whenDisplay_DISPLAY_SEARCH_RESULT_SMALL_noBadge() {
+        FlagOp op = FlagOp.NO_OP;
+        // apply the WORK bitmap flag to show work badge
+        mGmailAppInfo.bitmap.flags = op.apply(WORK_FLAG);
+        mBubbleTextView.setDisplay(DISPLAY_SEARCH_RESULT_SMALL);
+
+        mBubbleTextView.applyIconAndLabel(mGmailAppInfo);
+
+        assertThat(mBubbleTextView.getIcon().hasBadge()).isEqualTo(false);
+    }
+
+    @Test
+    public void applyIconAndLabel_whenDisplay_DISPLAY_SEARCH_RESULT_hasBadge() {
+        FlagOp op = FlagOp.NO_OP;
+        // apply the WORK bitmap flag to show work badge
+        mGmailAppInfo.bitmap.flags = op.apply(WORK_FLAG);
+        mBubbleTextView.setDisplay(DISPLAY_SEARCH_RESULT);
+
+        mBubbleTextView.applyIconAndLabel(mGmailAppInfo);
+
+        assertThat(mBubbleTextView.getIcon().hasBadge()).isEqualTo(true);
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index ad11268..b8ca43f 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -74,7 +74,7 @@
             private void evaluateInPortrait() throws Throwable {
                 mTest.mDevice.setOrientationNatural();
                 mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
-                AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher);
+                AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher, true);
                 base.evaluate();
                 mTest.getDevice().pressHome();
             }
@@ -82,7 +82,7 @@
             private void evaluateInLandscape() throws Throwable {
                 mTest.mDevice.setOrientationLeft();
                 mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90);
-                AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher);
+                AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher, true);
                 base.evaluate();
                 mTest.getDevice().pressHome();
             }
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 9aaca54..f37b676 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -16,36 +16,16 @@
 
 package com.android.launcher3.ui;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
 
-import android.content.Intent;
-import android.platform.test.annotations.PlatinumTest;
-
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.tapl.AllApps;
-import com.android.launcher3.tapl.AppIcon;
-import com.android.launcher3.tapl.HomeAllApps;
-import com.android.launcher3.tapl.HomeAppIcon;
-import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
-import com.android.launcher3.util.LauncherLayoutBuilder;
-import com.android.launcher3.util.TestUtil;
-import com.android.launcher3.util.rule.TISBindRule;
 
-import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -57,13 +37,6 @@
     public static final String MAPS_APP_NAME = "Maps";
     public static final String STORE_APP_NAME = "Play Store";
     public static final String GMAIL_APP_NAME = "Gmail";
-    private static final String READ_DEVICE_CONFIG_PERMISSION =
-            "android.permission.READ_DEVICE_CONFIG";
-
-    @Rule
-    public TISBindRule mTISBindRule = new TISBindRule();
-
-    private AutoCloseable mLauncherLayout;
 
     @Before
     public void setUp() throws Exception {
@@ -88,13 +61,6 @@
         AbstractLauncherUiTest.checkDetectedLeaks(test.mLauncher, true);
     }
 
-    @After
-    public void tearDown() throws Exception {
-        if (mLauncherLayout != null) {
-            mLauncherLayout.close();
-        }
-    }
-
     // Please don't add negative test cases for methods that fail only after a long wait.
     public static void expectFail(String message, Runnable action) {
         boolean failed = false;
@@ -106,14 +72,6 @@
         assertTrue(message, failed);
     }
 
-    public static boolean isWorkspaceScrollable(Launcher launcher) {
-        return launcher.getWorkspace().getPageCount() > launcher.getWorkspace().getPanelCount();
-    }
-
-    private int getCurrentWorkspacePage(Launcher launcher) {
-        return launcher.getWorkspace().getCurrentPage();
-    }
-
     @Test
     public void testDevicePressMenu() throws Exception {
         mDevice.pressMenu();
@@ -125,94 +83,6 @@
         mLauncher.goHome();
     }
 
-    @PlatinumTest(focusArea = "launcher")
-    @Test
-    public void testWorkspace() throws Exception {
-        // Set workspace  that includes the chrome Activity app icon on the hotseat.
-        LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
-                .atHotseat(0).putApp("com.android.chrome", "com.google.android.apps.chrome.Main");
-        mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, builder);
-        reinitializeLauncherData();
-
-        final Workspace workspace = mLauncher.getWorkspace();
-
-        // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
-        executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
-                isWorkspaceScrollable(launcher)));
-        assertEquals("Initial workspace doesn't have the correct page", workspace.pagesPerScreen(),
-                workspace.getPageCount());
-        workspace.verifyWorkspaceAppIconIsGone("Chrome app was found on empty workspace", "Chrome");
-        workspace.ensureWorkspaceIsScrollable();
-
-        executeOnLauncher(
-                launcher -> assertEquals(
-                        "Ensuring workspace scrollable didn't switch to next screen",
-                        workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher)));
-        executeOnLauncher(
-                launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
-                        isWorkspaceScrollable(launcher)));
-        assertNotNull("ensureScrollable didn't add Chrome app",
-                workspace.getWorkspaceAppIcon("Chrome"));
-
-        // Test flinging workspace.
-        workspace.flingBackward();
-        assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
-        executeOnLauncher(
-                launcher -> assertEquals("Flinging back didn't switch workspace to page #0",
-                        0, getCurrentWorkspacePage(launcher)));
-
-        workspace.flingForward();
-        executeOnLauncher(
-                launcher -> assertEquals("Flinging forward didn't switch workspace to next screen",
-                        workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher)));
-        assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
-
-        // Test starting a workspace app.
-        final HomeAppIcon app = workspace.getWorkspaceAppIcon("Chrome");
-        assertNotNull("No Chrome app in workspace", app);
-    }
-
-    public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
-        allApps.freeze();
-        try {
-            final AppIcon app = allApps.getAppIcon("TestActivity7");
-            assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName()));
-            test.executeOnLauncher(launcher -> assertTrue(
-                    "Launcher activity is the top activity; expecting another activity to be the "
-                            + "top one",
-                    test.isInLaunchedApp(launcher)));
-        } finally {
-            allApps.unfreeze();
-        }
-    }
-
-    @Test
-    @PortraitLandscape
-    public void testAppIconLaunchFromAllAppsFromHome() throws Exception {
-        final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
-        assertTrue("Launcher internal state is not All Apps",
-                isInState(() -> LauncherState.ALL_APPS));
-
-        runIconLaunchFromAllAppsTest(this, allApps);
-    }
-
-    @FlakyTest(bugId = 256615483)
-    @Test
-    @PortraitLandscape
-    public void testPressBack() throws Exception {
-        InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
-                READ_DEVICE_CONFIG_PERMISSION);
-        assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get());
-        mLauncher.getWorkspace().switchToAllApps();
-        mLauncher.pressBack();
-        mLauncher.getWorkspace();
-        waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
-        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
-        mLauncher.pressBack();
-        mLauncher.getWorkspace();
-        waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
-    }
-
     @Test
     @PortraitLandscape
     public void testAddDeleteShortcutOnHotseat() {
@@ -224,40 +94,4 @@
         mLauncher.getWorkspace().deleteAppIcon(
                 mLauncher.getWorkspace().getHotseatAppIcon(APP_NAME));
     }
-
-    @Test
-    public void testGetAppIconName() {
-        HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
-        allApps.freeze();
-        try {
-            // getAppIcon() already verifies that the icon is not null and is the correct icon name.
-            allApps.getAppIcon(APP_NAME);
-        } finally {
-            allApps.unfreeze();
-        }
-    }
-
-    @PlatinumTest(focusArea = "launcher")
-    @Test
-    public void testAddAndDeletePageAndFling() {
-        Workspace workspace = mLauncher.getWorkspace();
-        // Get the first app from the hotseat
-        HomeAppIcon hotSeatIcon = workspace.getHotseatAppIcon(0);
-        String appName = hotSeatIcon.getIconName();
-
-        // Add one page by dragging app to page 1.
-        workspace.dragIcon(hotSeatIcon, workspace.pagesPerScreen());
-        assertEquals("Incorrect Page count Number",
-                workspace.pagesPerScreen() * 2,
-                workspace.getPageCount());
-
-        // Delete one page by dragging app to hot seat.
-        workspace.getWorkspaceAppIcon(appName).dragToHotseat(0);
-
-        // Refresh workspace to avoid using stale container error.
-        workspace = mLauncher.getWorkspace();
-        assertEquals("Incorrect Page count Number",
-                workspace.pagesPerScreen(),
-                workspace.getPageCount());
-    }
 }
diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java
new file mode 100644
index 0000000..d8ae99c
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.ui.workspace;
+
+import static com.android.launcher3.ui.TaplTestsLauncher3.initialize;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.PlatinumTest;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.tapl.HomeAppIcon;
+import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.LauncherLayoutBuilder;
+import com.android.launcher3.util.TestUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test the basic interactions of the Workspace, adding pages, moving the pages and removing pages.
+ */
+public class TaplWorkspaceTest extends AbstractLauncherUiTest {
+
+    private AutoCloseable mLauncherLayout;
+
+    private static boolean isWorkspaceScrollable(Launcher launcher) {
+        return launcher.getWorkspace().getPageCount() > launcher.getWorkspace().getPanelCount();
+    }
+
+    private int getCurrentWorkspacePage(Launcher launcher) {
+        return launcher.getWorkspace().getCurrentPage();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        initialize(this);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mLauncherLayout != null) {
+            mLauncherLayout.close();
+        }
+    }
+
+    /**
+     * Add an icon and add a page to ensure the Workspace is scrollable and also make sure we can
+     * move between workspaces. After, make sure we can launch an app from the Workspace.
+     * @throws Exception if we can't set the defaults icons that will appear at the beginning.
+     */
+    @PlatinumTest(focusArea = "launcher")
+    @Test
+    public void testWorkspace() throws Exception {
+        // Set workspace  that includes the chrome Activity app icon on the hotseat.
+        LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
+                .atHotseat(0).putApp("com.android.chrome", "com.google.android.apps.chrome.Main");
+        mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, builder);
+        reinitializeLauncherData();
+
+        final Workspace workspace = mLauncher.getWorkspace();
+
+        // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
+        executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
+                isWorkspaceScrollable(launcher)));
+        assertEquals("Initial workspace doesn't have the correct page", workspace.pagesPerScreen(),
+                workspace.getPageCount());
+        workspace.verifyWorkspaceAppIconIsGone("Chrome app was found on empty workspace", "Chrome");
+        workspace.ensureWorkspaceIsScrollable();
+
+        executeOnLauncher(
+                launcher -> assertEquals(
+                        "Ensuring workspace scrollable didn't switch to next screen",
+                        workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher)));
+        executeOnLauncher(
+                launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
+                        isWorkspaceScrollable(launcher)));
+        assertNotNull("ensureScrollable didn't add Chrome app",
+                workspace.getWorkspaceAppIcon("Chrome"));
+
+        // Test flinging workspace.
+        workspace.flingBackward();
+        assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
+        executeOnLauncher(
+                launcher -> assertEquals("Flinging back didn't switch workspace to page #0",
+                        0, getCurrentWorkspacePage(launcher)));
+
+        workspace.flingForward();
+        executeOnLauncher(
+                launcher -> assertEquals("Flinging forward didn't switch workspace to next screen",
+                        workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher)));
+        assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
+
+        // Test starting a workspace app.
+        final HomeAppIcon app = workspace.getWorkspaceAppIcon("Chrome");
+        assertNotNull("No Chrome app in workspace", app);
+    }
+
+
+    /**
+     * Similar to {@link TaplWorkspaceTest#testWorkspace} but here we also make sure we can delete
+     * the pages.
+     */
+    @PlatinumTest(focusArea = "launcher")
+    @Test
+    public void testAddAndDeletePageAndFling() {
+        Workspace workspace = mLauncher.getWorkspace();
+        // Get the first app from the hotseat
+        HomeAppIcon hotSeatIcon = workspace.getHotseatAppIcon(0);
+        String appName = hotSeatIcon.getIconName();
+
+        // Add one page by dragging app to page 1.
+        workspace.dragIcon(hotSeatIcon, workspace.pagesPerScreen());
+        assertEquals("Incorrect Page count Number",
+                workspace.pagesPerScreen() * 2,
+                workspace.getPageCount());
+
+        // Delete one page by dragging app to hot seat.
+        workspace.getWorkspaceAppIcon(appName).dragToHotseat(0);
+
+        // Refresh workspace to avoid using stale container error.
+        workspace = mLauncher.getWorkspace();
+        assertEquals("Incorrect Page count Number",
+                workspace.pagesPerScreen(),
+                workspace.getPageCount());
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
index a94dd2e..8670d40 100644
--- a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
+++ b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
@@ -42,11 +42,13 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.doNothing
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 import org.mockito.stubbing.Answer
 
 /** Unit tests for {@link DisplayController} */
@@ -56,13 +58,13 @@
 
     private val appContext: Context = ApplicationProvider.getApplicationContext()
 
-    @Mock private lateinit var context: SandboxContext
-    @Mock private lateinit var windowManagerProxy: WindowManagerProxy
-    @Mock private lateinit var launcherPrefs: LauncherPrefs
-    @Mock private lateinit var displayManager: DisplayManager
-    @Mock private lateinit var display: Display
-    @Mock private lateinit var resources: Resources
-    @Mock private lateinit var displayInfoChangeListener: DisplayInfoChangeListener
+    private val context: SandboxContext = mock()
+    private val windowManagerProxy: WindowManagerProxy = mock()
+    private val launcherPrefs: LauncherPrefs = mock()
+    private val displayManager: DisplayManager = mock()
+    private val display: Display = mock()
+    private val resources: Resources = mock()
+    private val displayInfoChangeListener: DisplayInfoChangeListener = mock()
 
     private lateinit var displayController: DisplayController
 
@@ -88,7 +90,6 @@
 
     @Before
     fun setUp() {
-        MockitoAnnotations.initMocks(this)
         whenever(context.getObject(eq(WindowManagerProxy.INSTANCE))).thenReturn(windowManagerProxy)
         whenever(context.getObject(eq(LauncherPrefs.INSTANCE))).thenReturn(launcherPrefs)
         whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false)
@@ -112,10 +113,10 @@
 
         whenever(windowManagerProxy.getNavigationMode(any())).thenReturn(NavigationMode.NO_BUTTON)
         // Mock context
-        whenever(context.createWindowContext(any(), any(), nullable())).thenReturn(context)
+        whenever(context.createWindowContext(any(), any(), anyOrNull())).thenReturn(context)
         whenever(context.getSystemService(eq(DisplayManager::class.java)))
             .thenReturn(displayManager)
-        doNothing().`when`(context).registerComponentCallbacks(any())
+        doNothing().whenever(context).registerComponentCallbacks(any())
 
         // Mock display
         whenever(display.rotation).thenReturn(displayInfo.rotation)
diff --git a/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt b/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt
deleted file mode 100644
index c9c9616..0000000
--- a/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2022 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.util
-
-/**
- * Kotlin versions of popular mockito methods that can return null in situations when Kotlin expects
- * a non-null value. Kotlin will throw an IllegalStateException when this takes place ("x must not
- * be null"). To fix this, we can use methods that modify the return type to be nullable. This
- * causes Kotlin to skip the null checks.
- */
-import org.mockito.ArgumentCaptor
-import org.mockito.Mockito
-
-/**
- * Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when null is
- * returned.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-fun <T> eq(obj: T): T = Mockito.eq<T>(obj)
-
-/**
- * Returns Mockito.same() as nullable type to avoid java.lang.IllegalStateException when null is
- * returned.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-fun <T> same(obj: T): T = Mockito.same<T>(obj)
-
-/**
- * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when null is
- * returned.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
-
-inline fun <reified T> any(): T = any(T::class.java)
-
-/** Kotlin type-inferred version of Mockito.nullable() */
-inline fun <reified T> nullable(): T? = Mockito.nullable(T::class.java)
-
-/**
- * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException when
- * null is returned.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
-
-/**
- * Helper function for creating an argumentCaptor in kotlin.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-inline fun <reified T : Any> argumentCaptor(): ArgumentCaptor<T> =
-    ArgumentCaptor.forClass(T::class.java)
-
-/**
- * Helper function for creating new mocks, without the need to pass in a [Class] instance.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-inline fun <reified T : Any> mock(): T = Mockito.mock(T::class.java)
-
-/**
- * A kotlin implemented wrapper of [ArgumentCaptor] which prevents the following exception when
- * kotlin tests are mocking kotlin objects and the methods take non-null parameters:
- * ```
- *     java.lang.NullPointerException: capture() must not be null
- * ```
- */
-class KotlinArgumentCaptor<T> constructor(clazz: Class<T>) {
-    private val wrapped: ArgumentCaptor<T> = ArgumentCaptor.forClass(clazz)
-    fun capture(): T = wrapped.capture()
-    val value: T
-        get() = wrapped.value
-}
-
-/**
- * Helper function for creating an argumentCaptor in kotlin.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-inline fun <reified T : Any> kotlinArgumentCaptor(): KotlinArgumentCaptor<T> =
-    KotlinArgumentCaptor(T::class.java)
-
-/**
- * Helper function for creating and using a single-use ArgumentCaptor in kotlin.
- *
- * ```
- *    val captor = argumentCaptor<Foo>()
- *    verify(...).someMethod(captor.capture())
- *    val captured = captor.value
- * ```
- *
- * becomes:
- * ```
- *    val captured = withArgCaptor<Foo> { verify(...).someMethod(capture()) }
- * ```
- *
- * NOTE: this uses the KotlinArgumentCaptor to avoid the NullPointerException.
- */
-inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T =
-    kotlinArgumentCaptor<T>().apply { block() }.value
diff --git a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
index 92ab2cb..2c4a54f 100644
--- a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
+++ b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
@@ -26,29 +26,27 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.whenever
 
 /** Unit tests for {@link LockedUserState} */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class LockedUserStateTest {
 
-    @Mock lateinit var userManager: UserManager
-    @Mock lateinit var context: Context
+    private val userManager: UserManager = mock()
+    private val context: Context = mock()
 
     @Before
     fun setup() {
-        MockitoAnnotations.initMocks(this)
-        `when`(context.getSystemService(UserManager::class.java)).thenReturn(userManager)
+        whenever(context.getSystemService(UserManager::class.java)).thenReturn(userManager)
     }
 
     @Test
     fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() {
-        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
+        whenever(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
         val action: Runnable = mock()
         LockedUserState(context).runOnUserUnlocked(action)
         verify(action).run()
@@ -56,7 +54,7 @@
 
     @Test
     fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() {
-        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
+        whenever(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
         val action: Runnable = mock()
         val state = LockedUserState(context)
         state.runOnUserUnlocked(action)
@@ -67,13 +65,13 @@
 
     @Test
     fun isUserUnlocked_returns_true_when_user_is_unlocked() {
-        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
+        whenever(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
         assertThat(LockedUserState(context).isUserUnlocked).isTrue()
     }
 
     @Test
     fun isUserUnlocked_returns_false_when_user_is_locked() {
-        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
+        whenever(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
         assertThat(LockedUserState(context).isUserUnlocked).isFalse()
     }
 }
diff --git a/tests/src/com/android/launcher3/util/rule/TISBindRule.java b/tests/src/com/android/launcher3/util/rule/TISBindRule.java
deleted file mode 100644
index 3ec4a29..0000000
--- a/tests/src/com/android/launcher3/util/rule/TISBindRule.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util.rule;
-
-import android.app.UiAutomation;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-public class TISBindRule implements TestRule {
-    public static String TAG = "TISBindRule";
-    public static String INTENT_FILTER = "android.intent.action.QUICKSTEP_SERVICE";
-    public static String TIS_PERMISSIONS = "android.permission.STATUS_BAR_SERVICE";
-
-    private String getLauncherPackageName(Context context) {
-        return ComponentName.unflattenFromString(context.getString(
-                com.android.internal.R.string.config_recentsComponentName)).getPackageName();
-    }
-
-    private ServiceConnection createConnection() {
-        return new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
-                Log.d(TAG, "Connected to TouchInteractionService");
-            }
-
-            @Override
-            public void onServiceDisconnected(ComponentName componentName) {
-                Log.d(TAG, "Disconnected from TouchInteractionService");
-            }
-        };
-    }
-
-    @NonNull
-    @Override
-    public Statement apply(@NonNull Statement base, @NonNull Description description) {
-        return new Statement() {
-
-            @Override
-            public void evaluate() throws Throwable {
-                Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
-                final ServiceConnection connection = createConnection();
-                UiAutomation uiAutomation =
-                        InstrumentationRegistry.getInstrumentation().getUiAutomation();
-                uiAutomation.adoptShellPermissionIdentity(TIS_PERMISSIONS);
-                Intent launchIntent = new Intent(INTENT_FILTER);
-                launchIntent.setPackage(getLauncherPackageName(context));
-                context.bindService(launchIntent, connection, Context.BIND_AUTO_CREATE);
-                uiAutomation.dropShellPermissionIdentity();
-                try {
-                    base.evaluate();
-                } finally {
-                    context.unbindService(connection);
-                }
-            }
-        };
-    }
-}
diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java
index 4b65439..51b7b18 100644
--- a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java
+++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java
@@ -62,18 +62,6 @@
                     + "NexusOverviewActionsView:id/overview_actions_view|FrameLayout:id"
                     + "/select_mode_buttons|ImageButton:id/close",
             DRAG_LAYER
-                    + "NexusOverviewActionsView:id/overview_actions_view|LinearLayout:id"
-                    + "/action_buttons|Button:id/action_screenshot",
-            DRAG_LAYER
-                    + "NexusOverviewActionsView:id/overview_actions_view|LinearLayout:id"
-                    + "/action_buttons|Button:id/action_select",
-            DRAG_LAYER
-                    + "NexusOverviewActionsView:id/overview_actions_view|LinearLayout:id"
-                    + "/action_buttons|Button:id/action_split",
-            DRAG_LAYER
-                    + "NexusOverviewActionsView:id/overview_actions_view|LinearLayout:id"
-                    + "/action_buttons|Space:id/action_split_space",
-            DRAG_LAYER
                     + "PopupContainerWithArrow:id/popup_container|LinearLayout:id"
                     + "/deep_shortcuts_container|DeepShortcutView:id/deep_shortcut_material"
                     + "|DeepShortcutTextView:id/bubble_text",
@@ -116,23 +104,14 @@
             RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel",
             DRAG_LAYER
                     + "NexusOverviewActionsView:id/overview_actions_view"
-                    + "|LinearLayout:id/action_buttons|Button:id/action_screenshot",
+                    + "|LinearLayout:id/action_buttons",
             RECENTS_DRAG_LAYER
                     + "NexusOverviewActionsView:id/overview_actions_view"
-                    + "|LinearLayout:id/action_buttons|Button:id/action_screenshot",
+                    + "|LinearLayout:id/action_buttons",
+            DRAG_LAYER + "IconView",
             DRAG_LAYER
-                    + "NexusOverviewActionsView:id/overview_actions_view"
-                    + "|LinearLayout:id/action_buttons|Button:id/action_select",
-            RECENTS_DRAG_LAYER
-                    + "NexusOverviewActionsView:id/overview_actions_view"
-                    + "|LinearLayout:id/action_buttons|Button:id/action_select",
-            DRAG_LAYER
-                    + "NexusOverviewActionsView:id/overview_actions_view"
-                    + "|LinearLayout:id/action_buttons|Button:id/action_split",
-            RECENTS_DRAG_LAYER
-                    + "NexusOverviewActionsView:id/overview_actions_view"
-                    + "|LinearLayout:id/action_buttons|Button:id/action_split",
-            DRAG_LAYER + "IconView"
+                    + "OptionsPopupView:id/popup_container|DeepShortcutView:id/system_shortcut"
+                    + "|BubbleTextView:id/bubble_text"
     ));
 
     // Minimal increase or decrease of view's alpha between frames that triggers the error.
diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java
index 8b88ace..e333074 100644
--- a/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java
+++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java
@@ -38,7 +38,8 @@
 
     private static final IgnoreNode IGNORED_NODES_ROOT = buildIgnoreNodesTree(List.of(
             CONTENT + "LauncherRootView:id/launcher|FloatingIconView",
-            DRAG_LAYER + "LauncherRecentsView:id/overview_panel|TaskView|TextView",
+            DRAG_LAYER + "LauncherRecentsView:id/overview_panel|TaskView",
+            DRAG_LAYER + "LauncherRecentsView:id/overview_panel|ClearAllButton:id/clear_all",
             DRAG_LAYER
                     + "LauncherAllAppsContainerView:id/apps_view|AllAppsRecyclerView:id"
                     + "/apps_list_view|BubbleTextView:id/icon",
@@ -53,7 +54,9 @@
                     + "/add_item_bottom_sheet|LinearLayout:id/add_item_bottom_sheet_content"
                     + "|ScrollView:id/widget_preview_scroll_view|WidgetCell:id/widget_cell"
                     + "|WidgetCellPreview:id/widget_preview_container|ImageView:id/widget_badge",
-            RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel|TaskView|IconView:id/icon",
+            RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel|TaskView",
+            RECENTS_DRAG_LAYER
+                    + "FallbackRecentsView:id/overview_panel|ClearAllButton:id/clear_all",
             DRAG_LAYER + "SearchContainerView:id/apps_view",
             DRAG_LAYER + "LauncherDragView",
             DRAG_LAYER + "FloatingTaskView|FloatingTaskThumbnailView:id/thumbnail",
@@ -64,7 +67,8 @@
                     + "WidgetsTwoPaneSheet|SpringRelativeLayout:id/container|LinearLayout:id"
                     + "/linear_layout_container|FrameLayout:id/recycler_view_container"
                     + "|FrameLayout:id/widgets_two_pane_sheet_recyclerview|WidgetsRecyclerView:id"
-                    + "/primary_widgets_list_view|WidgetsListHeader:id/widgets_list_header"
+                    + "/primary_widgets_list_view|WidgetsListHeader:id/widgets_list_header",
+            DRAG_LAYER + "NexusOverviewActionsView:id/overview_actions_view"
     ));
 
     // Per-AnalysisNode data that's specific to this detector.
diff --git a/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
index 2a98a24..a1d8059 100644
--- a/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
+++ b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
@@ -37,7 +37,6 @@
     private static final Pattern EVENT_ALT_TAB_UP = Pattern.compile(
             "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_TAB"
                     + ".*?metaState=META_ALT_ON");
-
     private static final Pattern EVENT_ALT_SHIFT_TAB_DOWN = Pattern.compile(
             "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_TAB"
                     + ".*?metaState=META_ALT_ON|META_SHIFT_ON");
@@ -50,7 +49,10 @@
     private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
             "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP"
                     + ".*?keyCode=KEYCODE_ESCAPE.*?metaState=META_ALT_ON");
-    private static final Pattern EVENT_ALT_LEFT_UP = Pattern.compile(
+    private static final Pattern EVENT_KQS_ALT_LEFT_UP = Pattern.compile(
+            "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP"
+                    + ".*?keyCode=KEYCODE_ALT_LEFT");
+    private static final Pattern EVENT_HOME_ALT_LEFT_UP = Pattern.compile(
             "Key event: KeyEvent.*?action=ACTION_UP"
                     + ".*?keyCode=KEYCODE_ALT_LEFT");
 
@@ -82,22 +84,21 @@
      */
     public KeyboardQuickSwitch moveFocusForward() {
         try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
-                "want to move keyboard quick switch focus forward")) {
+                "want to move keyboard quick switch focus forward");
+             LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
 
-            try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
-                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_TAB_DOWN);
-                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_TAB_UP);
-                mLauncher.assertTrue("Failed to press alt+tab",
-                        mLauncher.getDevice().pressKeyCode(
-                                KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON));
+            mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_TAB_DOWN);
+            mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_TAB_UP);
+            mLauncher.assertTrue("Failed to press alt+tab",
+                    mLauncher.getDevice().pressKeyCode(
+                            KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON));
 
-                try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
-                        "pressed alt+tab")) {
-                    mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
+            try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+                    "pressed alt+tab")) {
+                mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
 
-                    return this;
-                }
+                return this;
             }
         }
     }
@@ -117,23 +118,22 @@
      */
     public KeyboardQuickSwitch moveFocusBackward() {
         try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
-                "want to move keyboard quick switch focus backward")) {
+                "want to move keyboard quick switch focus backward");
+             LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
 
-            try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
-                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_SHIFT_TAB_DOWN);
-                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_SHIFT_TAB_UP);
-                mLauncher.assertTrue("Failed to press alt+shift+tab",
-                        mLauncher.getDevice().pressKeyCode(
-                                KeyEvent.KEYCODE_TAB,
-                                KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON));
+            mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_SHIFT_TAB_DOWN);
+            mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_SHIFT_TAB_UP);
+            mLauncher.assertTrue("Failed to press alt+shift+tab",
+                    mLauncher.getDevice().pressKeyCode(
+                            KeyEvent.KEYCODE_TAB,
+                            KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON));
 
-                try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
-                        "pressed alt+shift+tab")) {
-                    mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
+            try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+                    "pressed alt+shift+tab")) {
+                mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
 
-                    return this;
-                }
+                return this;
             }
         }
     }
@@ -146,27 +146,28 @@
      */
     public void dismiss() {
         try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
-                "want to dismiss keyboard quick switch view")) {
+                "want to dismiss keyboard quick switch view");
+             LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
 
-            try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
-                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_DOWN);
-                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
-                mLauncher.assertTrue("Failed to press alt+tab",
-                        mLauncher.getDevice().pressKeyCode(
-                                KeyEvent.KEYCODE_ESCAPE, KeyEvent.META_ALT_ON));
+            mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_DOWN);
+            mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
+            mLauncher.assertTrue("Failed to press alt+tab",
+                    mLauncher.getDevice().pressKeyCode(
+                            KeyEvent.KEYCODE_ESCAPE, KeyEvent.META_ALT_ON));
 
-                try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
-                        "pressed alt+esc")) {
-                    mLauncher.waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
-                    if (mExpectHomeKeyEventsOnDismiss) {
-                        mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_LEFT_UP);
-                    }
-                    mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0);
+            try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+                    "pressed alt+esc")) {
+                mLauncher.waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
 
-                    // Verify the final state is the same as the initial state
-                    mLauncher.verifyContainerType(mStartingContainerType);
+                // Verify the final state is the same as the initial state
+                mLauncher.verifyContainerType(mStartingContainerType);
+
+                // Wait until the device has fully settled before unpressing the key code
+                if (mExpectHomeKeyEventsOnDismiss) {
+                    mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_HOME_ALT_LEFT_UP);
                 }
+                mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0);
             }
         }
     }
@@ -180,7 +181,9 @@
      * @param expectedPackageName the package name of the expected launched app
      */
     public LaunchedAppState launchFocusedAppTask(@NonNull String expectedPackageName) {
-        return (LaunchedAppState) launchFocusedTask(expectedPackageName);
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+            return (LaunchedAppState) launchFocusedTask(expectedPackageName);
+        }
     }
 
     /**
@@ -190,22 +193,29 @@
      * {@link #launchFocusedAppTask(String)}.
      */
     public Overview launchFocusedOverviewTask() {
-        return (Overview) launchFocusedTask(null);
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+            return (Overview) launchFocusedTask(null);
+        }
     }
 
     private LauncherInstrumentation.VisibleContainer launchFocusedTask(
             @Nullable String expectedPackageName) {
-        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+        try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
                 "want to launch focused task: "
                         + (expectedPackageName == null ? "Overview" : expectedPackageName))) {
+            mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KQS_ALT_LEFT_UP);
             mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0);
-            mLauncher.waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
 
-            if (expectedPackageName != null) {
-                mLauncher.assertAppLaunched(expectedPackageName);
-                return mLauncher.getLaunchedAppState();
-            } else {
-                return mLauncher.getOverview();
+            try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+                    "un-pressed left alt")) {
+                mLauncher.waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
+
+                if (expectedPackageName != null) {
+                    mLauncher.assertAppLaunched(expectedPackageName);
+                    return mLauncher.getLaunchedAppState();
+                } else {
+                    return mLauncher.getOverview();
+                }
             }
         }
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitchSource.java b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitchSource.java
index b7e3d38..677ed04 100644
--- a/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitchSource.java
+++ b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitchSource.java
@@ -32,7 +32,8 @@
         LauncherInstrumentation launcher = getLauncher();
 
         try (LauncherInstrumentation.Closable c1 = launcher.addContextLayer(
-                "want to show keyboard quick switch object")) {
+                "want to show keyboard quick switch object");
+             LauncherInstrumentation.Closable e = launcher.eventsCheck()) {
             launcher.pressAndHoldKeyCode(KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_LEFT_ON);
 
             try (LauncherInstrumentation.Closable c2 = launcher.addContextLayer(