Merge "Update overview actions top margin" into tm-qpr-dev
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 9123959..a77791f 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,2 +1,4 @@
 [Hook Scripts]
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --config_xml tools/checkstyle.xml --sha ${PREUPLOAD_COMMIT}
+
+ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check ${PREUPLOAD_FILES}
\ No newline at end of file
diff --git a/go/src/com/android/launcher3/model/LoaderResults.java b/go/src/com/android/launcher3/model/LauncherBinder.java
similarity index 82%
rename from go/src/com/android/launcher3/model/LoaderResults.java
rename to go/src/com/android/launcher3/model/LauncherBinder.java
index 5f71061..437d8ca 100644
--- a/go/src/com/android/launcher3/model/LoaderResults.java
+++ b/go/src/com/android/launcher3/model/LauncherBinder.java
@@ -22,11 +22,11 @@
 import com.android.launcher3.model.BgDataModel.Callbacks;
 
 /**
- * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
  */
-public class LoaderResults extends BaseLoaderResults {
+public class LauncherBinder extends BaseLauncherBinder {
 
-    public LoaderResults(LauncherAppState app, BgDataModel dataModel,
+    public LauncherBinder(LauncherAppState app, BgDataModel dataModel,
             AllAppsList allAppsList, Callbacks[] callbacks) {
         super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
     }
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 95a94ec..9f9f2c8 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -28,12 +28,15 @@
 import android.content.Context;
 import android.os.Build;
 import android.os.Handler;
+import android.os.RemoteException;
+import android.view.IRemoteAnimationFinishedCallback;
 import android.view.RemoteAnimationTarget;
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
+import com.android.systemui.animation.RemoteAnimationDelegate;
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 
 import java.lang.ref.WeakReference;
@@ -89,7 +92,7 @@
         Runnable r = () -> {
             finishExistingAnimation();
             mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);
-            getFactory().onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,
+            getFactory().onAnimationStart(transit, appTargets, wallpaperTargets, nonAppTargets,
                     mAnimationResult);
         };
         if (mStartAtFrontOfQueue) {
@@ -124,7 +127,11 @@
         });
     }
 
-    public static final class AnimationResult {
+    /**
+     * Used by RemoteAnimationFactory implementations to run the actual animation and its lifecycle
+     * callbacks.
+     */
+    public static final class AnimationResult extends IRemoteAnimationFinishedCallback.Stub {
 
         private final Runnable mSyncFinishRunnable;
         private final Runnable mASyncFinishRunnable;
@@ -199,25 +206,41 @@
                 }
             }
         }
+
+        /**
+         * When used as a simple IRemoteAnimationFinishedCallback, this method is used to run the
+         * animation finished runnable.
+         */
+        @Override
+        public void onAnimationFinished() throws RemoteException {
+            mASyncFinishRunnable.run();
+        }
     }
 
     /**
      * Used with LauncherAnimationRunner as an interface for the runner to call back to the
      * implementation.
      */
-    @FunctionalInterface
-    public interface RemoteAnimationFactory {
+    public interface RemoteAnimationFactory extends RemoteAnimationDelegate<AnimationResult> {
 
         /**
          * Called on the UI thread when the animation targets are received. The implementation must
          * call {@link AnimationResult#setAnimation} with the target animation to be run.
          */
-        void onCreateAnimation(int transit,
+        @Override
+        @UiThread
+        void onAnimationStart(int transit,
                 RemoteAnimationTarget[] appTargets,
                 RemoteAnimationTarget[] wallpaperTargets,
                 RemoteAnimationTarget[] nonAppTargets,
                 LauncherAnimationRunner.AnimationResult result);
 
+        @Override
+        @UiThread
+        default void onAnimationCancelled(boolean isKeyguardOccluded) {
+            onAnimationCancelled();
+        }
+
         /**
          * Called when the animation is cancelled. This can happen with or without
          * the create being called.
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 2aa0af4..aa9e272 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1560,7 +1560,8 @@
             RemoteAnimationTarget[] wallpaperTargets,
             boolean fromUnlock,
             RectF startRect,
-            float startWindowCornerRadius) {
+            float startWindowCornerRadius,
+            boolean fromPredictiveBack) {
         AnimatorSet anim = null;
         RectFSpringAnim rectFSpringAnim = null;
 
@@ -1594,7 +1595,11 @@
                 rectFSpringAnim = getClosingWindowAnimators(
                         anim, appTargets, launcherView, velocity, startRect,
                         startWindowCornerRadius);
-                if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
+                if (mLauncher.isInState(LauncherState.ALL_APPS)) {
+                    // Skip scaling all apps, otherwise FloatingIconView will get wrong
+                    // layout bounds.
+                    skipAllAppsScale = true;
+                } else if (!fromPredictiveBack) {
                     anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
                             true /* animateOverviewScrim */, launcherView).getAnimators());
 
@@ -1606,10 +1611,6 @@
 
                     // We play StaggeredWorkspaceAnim as a part of the closing window animation.
                     playWorkspaceReveal = false;
-                } else {
-                    // Skip scaling all apps, otherwise FloatingIconView will get wrong
-                    // layout bounds.
-                    skipAllAppsScale = true;
                 }
             } else {
                 anim.play(getFallbackClosingWindowAnimators(appTargets));
@@ -1665,7 +1666,7 @@
         }
 
         @Override
-        public void onCreateAnimation(int transit,
+        public void onAnimationStart(int transit,
                 RemoteAnimationTarget[] appTargets,
                 RemoteAnimationTarget[] wallpaperTargets,
                 RemoteAnimationTarget[] nonAppTargets,
@@ -1686,7 +1687,8 @@
                     new RectF(getWindowTargetBounds(appTargets, getRotationChange(appTargets)));
             Pair<RectFSpringAnim, AnimatorSet> pair = createWallpaperOpenAnimations(
                     appTargets, wallpaperTargets, mFromUnlock, windowTargetBounds,
-                    QuickStepContract.getWindowCornerRadius(mLauncher));
+                    QuickStepContract.getWindowCornerRadius(mLauncher),
+                    false /* fromPredictiveBack */);
 
             mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
             result.setAnimation(pair.second, mLauncher);
@@ -1707,7 +1709,7 @@
         }
 
         @Override
-        public void onCreateAnimation(int transit,
+        public void onAnimationStart(int transit,
                 RemoteAnimationTarget[] appTargets,
                 RemoteAnimationTarget[] wallpaperTargets,
                 RemoteAnimationTarget[] nonAppTargets,
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index bbc0627..ae121e2 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -33,6 +33,7 @@
 
     private boolean mFreeformTasksVisible;
     private boolean mInOverviewState;
+    private boolean mGestureInProgress;
 
     public DesktopVisibilityController(Launcher launcher) {
         mLauncher = launcher;
@@ -57,9 +58,24 @@
      * Sets whether freeform windows are visible and updates launcher visibility based on that.
      */
     public void setFreeformTasksVisible(boolean freeformTasksVisible) {
+        if (!isDesktopModeSupported()) {
+            return;
+        }
         if (freeformTasksVisible != mFreeformTasksVisible) {
             mFreeformTasksVisible = freeformTasksVisible;
-            updateLauncherVisibility();
+            if (mFreeformTasksVisible) {
+                setLauncherViewsVisibility(View.INVISIBLE);
+                if (!mInOverviewState) {
+                    // When freeform is visible & we're not in overview, we want launcher to appear
+                    // paused, this ensures that taskbar displays.
+                    markLauncherPaused();
+                }
+            } else {
+                setLauncherViewsVisibility(View.VISIBLE);
+                // If freeform isn't visible ensure that launcher appears resumed to behave
+                // normally.
+                markLauncherResumed();
+            }
         }
     }
 
@@ -67,40 +83,67 @@
      * Sets whether the overview is visible and updates launcher visibility based on that.
      */
     public void setOverviewStateEnabled(boolean overviewStateEnabled) {
+        if (!isDesktopModeSupported()) {
+            return;
+        }
         if (overviewStateEnabled != mInOverviewState) {
             mInOverviewState = overviewStateEnabled;
-            updateLauncherVisibility();
+            if (mInOverviewState) {
+                setLauncherViewsVisibility(View.VISIBLE);
+                markLauncherResumed();
+            } else if (mFreeformTasksVisible) {
+                setLauncherViewsVisibility(View.INVISIBLE);
+                markLauncherPaused();
+            }
         }
     }
 
     /**
-     * Updates launcher visibility and state to look like it is paused or resumed depending on
-     * whether freeform windows are showing in desktop mode.
+     * Whether recents gesture is currently in progress.
      */
-    private void updateLauncherVisibility() {
-        StatefulActivity<LauncherState> activity =
-                QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
-        View workspaceView = mLauncher.getWorkspace();
-        if (activity == null || workspaceView == null || !isDesktopModeSupported()) {
+    public boolean isGestureInProgress() {
+        return mGestureInProgress;
+    }
+
+    /**
+     * Sets whether recents gesture is in progress.
+     */
+    public void setGestureInProgress(boolean gestureInProgress) {
+        if (!isDesktopModeSupported()) {
             return;
         }
+        if (gestureInProgress != mGestureInProgress) {
+            mGestureInProgress = gestureInProgress;
+        }
+    }
 
-        if (mFreeformTasksVisible) {
-            workspaceView.setVisibility(View.INVISIBLE);
-            if (!mInOverviewState) {
-                // When freeform is visible & we're not in overview, we want launcher to appear
-                // paused, this ensures that taskbar displays.
-                activity.setPaused();
-            }
-        } else {
-            workspaceView.setVisibility(View.VISIBLE);
-            // If freeform isn't visible ensure that launcher appears resumed to behave normally.
-            // Check activity state before calling setResumed(). Launcher may have been actually
-            // paused (eg fullscreen task moved to front).
-            // In this case we should not mark the activity as resumed.
-            if (activity.isResumed()) {
-                activity.setResumed();
-            }
+    private void setLauncherViewsVisibility(int visibility) {
+        View workspaceView = mLauncher.getWorkspace();
+        if (workspaceView != null) {
+            workspaceView.setVisibility(visibility);
+        }
+        View dragLayer = mLauncher.getDragLayer();
+        if (dragLayer != null) {
+            dragLayer.setVisibility(visibility);
+        }
+    }
+
+    private void markLauncherPaused() {
+        StatefulActivity<LauncherState> activity =
+                QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
+        if (activity != null) {
+            activity.setPaused();
+        }
+    }
+
+    private void markLauncherResumed() {
+        StatefulActivity<LauncherState> activity =
+                QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
+        // Check activity state before calling setResumed(). Launcher may have been actually
+        // paused (eg fullscreen task moved to front).
+        // In this case we should not mark the activity as resumed.
+        if (activity != null && activity.isResumed()) {
+            activity.setResumed();
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 731eea7..4e795d9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -598,9 +598,6 @@
     }
 
     public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
-        if (!isUserSetupComplete()) {
-            return;
-        }
         mControllers.navbarButtonsViewController.getTaskbarNavButtonDarkIntensity()
                 .updateValue(darkIntensity);
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index ff7e8e9..cf3af08 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -16,20 +16,17 @@
 
 package com.android.launcher3.taskbar
 
-import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
-import com.android.launcher3.Utilities.mapToRange
-
 import android.graphics.Canvas
 import android.graphics.Color
 import android.graphics.Paint
 import android.graphics.Path
 import com.android.launcher3.R
+import com.android.launcher3.Utilities.mapToRange
 import com.android.launcher3.anim.Interpolators
+import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
 import com.android.launcher3.util.DisplayController
 
-/**
- * Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners.
- */
+/** Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. */
 class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
 
     val paint: Paint = Paint()
@@ -39,7 +36,7 @@
     private var maxBackgroundHeight = context.deviceProfile.taskbarSize.toFloat()
     private val transientBackgroundBounds = context.transientTaskbarBounds
 
-    private val isTransientTaskbar = DisplayController.isTransientTaskbar(context);
+    private val isTransientTaskbar = DisplayController.isTransientTaskbar(context)
 
     private var shadowBlur = 0f
     private var keyShadowDistance = 0f
@@ -98,9 +95,7 @@
         invertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE)
     }
 
-    /**
-     * Draws the background with the given paint and height, on the provided canvas.
-     */
+    /** Draws the background with the given paint and height, on the provided canvas. */
     fun draw(canvas: Canvas) {
         canvas.save()
         canvas.translate(0f, canvas.height - backgroundHeight - bottomMargin)
@@ -124,21 +119,26 @@
             canvas.translate(0f, bottomMargin * ((1f - scaleFactor) / 2f))
 
             // Draw shadow.
-            val shadowAlpha = mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, 25f,
-                Interpolators.LINEAR)
-            paint.setShadowLayer(shadowBlur, 0f, keyShadowDistance,
+            val shadowAlpha =
+                mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, 25f, Interpolators.LINEAR)
+            paint.setShadowLayer(
+                shadowBlur,
+                0f,
+                keyShadowDistance,
                 setColorAlphaBound(Color.BLACK, Math.round(shadowAlpha))
             )
 
             // Draw background.
-            val radius = backgroundHeight / 2f;
+            val radius = backgroundHeight / 2f
 
             canvas.drawRoundRect(
                 transientBackgroundBounds.left + (delta / 2f),
                 translationYForSwipe,
                 transientBackgroundBounds.right - (delta / 2f),
                 backgroundHeight + translationYForSwipe,
-                radius, radius, paint
+                radius,
+                radius,
+                paint
             )
         }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index a48b88f..9f24565 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -36,10 +36,8 @@
 import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
 import java.io.PrintWriter
 
-/**
- * Handles the insets that Taskbar provides to underlying apps and the IME.
- */
-class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTaskbarController {
+/** Handles the insets that Taskbar provides to underlying apps and the IME. */
+class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTaskbarController {
 
     /** The bottom insets taskbar provides to the IME when IME is visible. */
     val taskbarHeightForIme: Int = context.resources.getDimensionPixelSize(R.dimen.taskbar_ime_size)
@@ -77,13 +75,19 @@
 
     fun onTaskbarWindowHeightOrInsetsChanged() {
         val touchableHeight = controllers.taskbarStashController.touchableHeight
-        touchableRegion.set(0, windowLayoutParams.height - touchableHeight,
-            context.deviceProfile.widthPx, windowLayoutParams.height)
+        touchableRegion.set(
+            0,
+            windowLayoutParams.height - touchableHeight,
+            context.deviceProfile.widthPx,
+            windowLayoutParams.height
+        )
         val contentHeight = controllers.taskbarStashController.contentHeightToReportToApps
         val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
         for (provider in windowLayoutParams.providedInsets) {
-            if (provider.type == ITYPE_EXTRA_NAVIGATION_BAR
-                    || provider.type == ITYPE_BOTTOM_MANDATORY_GESTURES) {
+            if (
+                provider.type == ITYPE_EXTRA_NAVIGATION_BAR ||
+                    provider.type == ITYPE_BOTTOM_MANDATORY_GESTURES
+            ) {
                 provider.insetsSize = getInsetsByNavMode(contentHeight)
             } else if (provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT) {
                 provider.insetsSize = getInsetsByNavMode(tappableHeight)
@@ -91,24 +95,20 @@
         }
 
         val imeInsetsSize = getInsetsByNavMode(taskbarHeightForIme)
-        val insetsSizeOverride = arrayOf(
-            InsetsFrameProvider.InsetsSizeOverride(
-                TYPE_INPUT_METHOD,
-                imeInsetsSize
-            ),
-        )
+        val insetsSizeOverride =
+            arrayOf(
+                InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
+            )
         // Use 0 tappableElement insets for the VoiceInteractionWindow when gesture nav is enabled.
         val visInsetsSizeForGestureNavTappableElement = getInsetsByNavMode(0)
-        val insetsSizeOverrideForGestureNavTappableElement = arrayOf(
-            InsetsFrameProvider.InsetsSizeOverride(
-                TYPE_INPUT_METHOD,
-                imeInsetsSize
-            ),
-            InsetsFrameProvider.InsetsSizeOverride(
-                TYPE_VOICE_INTERACTION,
-                visInsetsSizeForGestureNavTappableElement
-            ),
-        )
+        val insetsSizeOverrideForGestureNavTappableElement =
+            arrayOf(
+                InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
+                InsetsFrameProvider.InsetsSizeOverride(
+                    TYPE_VOICE_INTERACTION,
+                    visInsetsSizeForGestureNavTappableElement
+                ),
+            )
         for (provider in windowLayoutParams.providedInsets) {
             if (context.isGestureNav && provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT) {
                 provider.insetsSizeOverrides = insetsSizeOverrideForGestureNavTappableElement
@@ -120,9 +120,11 @@
 
     /**
      * @return [Insets] where the [bottomInset] is either used as a bottom inset or
+     * ```
      *         right/left inset if using 3 button nav
+     * ```
      */
-    private fun getInsetsByNavMode(bottomInset: Int) : Insets {
+    private fun getInsetsByNavMode(bottomInset: Int): Insets {
         val devicePortrait = !context.deviceProfile.isLandscape
         if (!TaskbarManager.isPhoneButtonNavMode(context) || devicePortrait) {
             // Taskbar or portrait phone mode
@@ -139,9 +141,9 @@
      * @param providesInsetsTypes The inset types we would like this layout params to provide.
      */
     fun setProvidesInsetsTypes(params: WindowManager.LayoutParams, providesInsetsTypes: IntArray) {
-        params.providedInsets = arrayOfNulls<InsetsFrameProvider>(providesInsetsTypes.size);
+        params.providedInsets = arrayOfNulls<InsetsFrameProvider>(providesInsetsTypes.size)
         for (i in providesInsetsTypes.indices) {
-            params.providedInsets[i] = InsetsFrameProvider(providesInsetsTypes[i]);
+            params.providedInsets[i] = InsetsFrameProvider(providesInsetsTypes[i])
         }
     }
 
@@ -153,14 +155,17 @@
         insetsInfo.touchableRegion.setEmpty()
         // Always have nav buttons be touchable
         controllers.navbarButtonsViewController.addVisibleButtonsRegion(
-            context.dragLayer, insetsInfo.touchableRegion
+            context.dragLayer,
+            insetsInfo.touchableRegion
         )
         var insetsIsTouchableRegion = true
         if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
             // Let touches pass through us.
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
-        } else if (controllers.navbarButtonsViewController.isImeVisible
-                && controllers.taskbarStashController.isStashed()) {
+        } else if (
+            controllers.navbarButtonsViewController.isImeVisible &&
+                controllers.taskbarStashController.isStashed()
+        ) {
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
         } else if (!controllers.uiController.isTaskbarTouchable) {
             // Let touches pass through us.
@@ -174,9 +179,10 @@
                 insetsInfo.touchableRegion.set(touchableRegion)
             }
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
-        } else if (controllers.taskbarViewController.areIconsVisible()
-            || AbstractFloatingView.hasOpenView(context, AbstractFloatingView.TYPE_ALL)
-            || context.isNavBarKidsModeActive
+        } else if (
+            controllers.taskbarViewController.areIconsVisible() ||
+                AbstractFloatingView.hasOpenView(context, AbstractFloatingView.TYPE_ALL) ||
+                context.isNavBarKidsModeActive
         ) {
             // Taskbar has some touchable elements, take over the full taskbar area
             insetsInfo.setTouchableInsets(
@@ -198,8 +204,12 @@
         pw.println(prefix + "TaskbarInsetsController:")
         pw.println("$prefix\twindowHeight=${windowLayoutParams.height}")
         for (provider in windowLayoutParams.providedInsets) {
-            pw.print("$prefix\tprovidedInsets: (type=" + InsetsState.typeToString(provider.type)
-                    + " insetsSize=" + provider.insetsSize)
+            pw.print(
+                "$prefix\tprovidedInsets: (type=" +
+                    InsetsState.typeToString(provider.type) +
+                    " insetsSize=" +
+                    provider.insetsSize
+            )
             if (provider.insetsSizeOverrides != null) {
                 pw.print(" insetsSizeOverrides={")
                 for ((i, overrideSize) in provider.insetsSizeOverrides.withIndex()) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 86e1911..98c45d5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -32,7 +32,6 @@
 import android.os.Handler;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.util.Log;
 import android.view.Display;
 
 import androidx.annotation.NonNull;
@@ -227,10 +226,6 @@
         mActivity = activity;
         UnfoldTransitionProgressProvider unfoldTransitionProgressProvider =
                 getUnfoldTransitionProgressProviderForActivity(activity);
-        if (unfoldTransitionProgressProvider == null) {
-            Log.e("b/261320823", "UnfoldTransitionProgressProvider null in setActivity. "
-                    + "Unfold animation for launcher will not work.");
-        }
         mUnfoldProgressProvider.setSourceProvider(unfoldTransitionProgressProvider);
 
         if (mTaskbarActivityContext != null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index c269648..6031b49 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -364,7 +364,12 @@
      * Returns the height that taskbar will be touchable.
      */
     public int getTouchableHeight() {
-        return mIsStashed ? mStashedHeight : mUnstashedHeight;
+        int bottomMargin = 0;
+        if (DisplayController.isTransientTaskbar(mActivity)) {
+            bottomMargin = mActivity.getResources().getDimensionPixelSize(
+                    R.dimen.transient_taskbar_margin);
+        }
+        return mIsStashed ? mStashedHeight : (mUnstashedHeight + bottomMargin);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
index a033507..be34fb1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -10,12 +10,9 @@
 private const val TASKBAR_ICONS_FADE_DURATION = 300L
 private const val STASHED_HANDLE_FADE_DURATION = 180L
 
-/**
- * Controls Taskbar behavior while Voice Interaction Window (assistant) is showing.
- */
-class VoiceInteractionWindowController(val context: TaskbarActivityContext)
-    : TaskbarControllers.LoggableTaskbarController,
-        TaskbarControllers.BackgroundRendererController {
+/** Controls Taskbar behavior while Voice Interaction Window (assistant) is showing. */
+class VoiceInteractionWindowController(val context: TaskbarActivityContext) :
+    TaskbarControllers.LoggableTaskbarController, TaskbarControllers.BackgroundRendererController {
 
     private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context)
 
@@ -37,8 +34,10 @@
 
                 override fun draw(canvas: Canvas) {
                     super.draw(canvas)
-                    if (this@VoiceInteractionWindowController.context.isGestureNav
-                        && controllers.taskbarStashController.isInAppAndNotStashed) {
+                    if (
+                        this@VoiceInteractionWindowController.context.isGestureNav &&
+                            controllers.taskbarStashController.isInAppAndNotStashed
+                    ) {
                         taskbarBackgroundRenderer.draw(canvas)
                     }
                 }
@@ -46,8 +45,8 @@
         separateWindowForTaskbarBackground.recreateControllers()
         separateWindowForTaskbarBackground.setWillNotDraw(false)
 
-        separateWindowLayoutParams = context.createDefaultWindowLayoutParams(
-            TYPE_APPLICATION_OVERLAY)
+        separateWindowLayoutParams =
+            context.createDefaultWindowLayoutParams(TYPE_APPLICATION_OVERLAY)
         separateWindowLayoutParams.isSystemApplicationOverlay = true
     }
 
@@ -63,14 +62,16 @@
 
         // Fade out taskbar icons and stashed handle.
         val taskbarIconAlpha = if (isVoiceInteractionWindowVisible) 0f else 1f
-        val fadeTaskbarIcons = controllers.taskbarViewController.taskbarIconAlpha
-            .get(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
-            .animateToValue(taskbarIconAlpha)
-            .setDuration(TASKBAR_ICONS_FADE_DURATION)
-        val fadeStashedHandle = controllers.stashedHandleViewController.stashedHandleAlpha
-            .get(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
-            .animateToValue(taskbarIconAlpha)
-            .setDuration(STASHED_HANDLE_FADE_DURATION)
+        val fadeTaskbarIcons =
+            controllers.taskbarViewController.taskbarIconAlpha
+                .get(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
+                .animateToValue(taskbarIconAlpha)
+                .setDuration(TASKBAR_ICONS_FADE_DURATION)
+        val fadeStashedHandle =
+            controllers.stashedHandleViewController.stashedHandleAlpha
+                .get(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
+                .animateToValue(taskbarIconAlpha)
+                .setDuration(STASHED_HANDLE_FADE_DURATION)
         fadeTaskbarIcons.start()
         fadeStashedHandle.start()
         if (skipAnim) {
@@ -83,23 +84,30 @@
 
     /**
      * Either:
+     *
      * Hides the TaskbarDragLayer background and creates a new window to draw just that background.
+     *
      * OR
+     *
      * Removes the temporary window and show the TaskbarDragLayer background again.
      */
     private fun moveTaskbarBackgroundToAppropriateLayer(skipAnim: Boolean) {
-        val taskbarBackgroundOverride = controllers.taskbarDragLayerController
-            .overrideBackgroundAlpha
+        val taskbarBackgroundOverride =
+            controllers.taskbarDragLayerController.overrideBackgroundAlpha
         val moveToLowerLayer = isVoiceInteractionWindowVisible
-        val onWindowsSynchronized = if (moveToLowerLayer) {
-            // First add the temporary window, then hide the overlapping taskbar background.
-            context.addWindowView(separateWindowForTaskbarBackground, separateWindowLayoutParams);
-            { taskbarBackgroundOverride.updateValue(0f) }
-        } else {
-            // First reapply the original taskbar background, then remove the temporary window.
-            taskbarBackgroundOverride.updateValue(1f);
-            { context.removeWindowView(separateWindowForTaskbarBackground) }
-        }
+        val onWindowsSynchronized =
+            if (moveToLowerLayer) {
+                // First add the temporary window, then hide the overlapping taskbar background.
+                context.addWindowView(
+                    separateWindowForTaskbarBackground,
+                    separateWindowLayoutParams
+                );
+                { taskbarBackgroundOverride.updateValue(0f) }
+            } else {
+                // First reapply the original taskbar background, then remove the temporary window.
+                taskbarBackgroundOverride.updateValue(1f);
+                { context.removeWindowView(separateWindowForTaskbarBackground) }
+            }
 
         if (skipAnim) {
             onWindowsSynchronized()
@@ -121,4 +129,4 @@
         pw.println(prefix + "VoiceInteractionWindowController:")
         pw.println("$prefix\tisVoiceInteractionWindowVisible=$isVoiceInteractionWindowVisible")
     }
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
index 68ea27a..a82902f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -30,20 +30,17 @@
  * [navButtonContainer]
  *
  * @property navButtonContainer ViewGroup that holds the 3 navigation buttons.
- * @property endContextualContainer ViewGroup that holds the end contextual button (ex, IME dismiss).
+ * @property endContextualContainer ViewGroup that holds the end contextual button (ex, IME
+ * dismiss).
  * @property startContextualContainer ViewGroup that holds the start contextual button (ex, A11y).
  */
 abstract class AbstractNavButtonLayoutter(
-        val resources: Resources,
-        val navButtonContainer: LinearLayout,
-        protected val endContextualContainer: ViewGroup,
-        protected val startContextualContainer: ViewGroup
+    val resources: Resources,
+    val navButtonContainer: LinearLayout,
+    protected val endContextualContainer: ViewGroup,
+    protected val startContextualContainer: ViewGroup
 ) : NavButtonLayoutter {
-    protected val homeButton: ImageView = navButtonContainer
-            .findViewById(R.id.home)
-    protected val recentsButton: ImageView = navButtonContainer
-            .findViewById(R.id.recent_apps)
-    protected val backButton: ImageView = navButtonContainer
-            .findViewById(R.id.back)
+    protected val homeButton: ImageView = navButtonContainer.findViewById(R.id.home)
+    protected val recentsButton: ImageView = navButtonContainer.findViewById(R.id.recent_apps)
+    protected val backButton: ImageView = navButtonContainer.findViewById(R.id.back)
 }
-
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
index c67ab79..c093c92 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
@@ -35,56 +35,47 @@
 import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_HOME_KIDS
 
 class KidsNavLayoutter(
-        resources: Resources,
-        navBarContainer: LinearLayout,
-        endContextualContainer: ViewGroup,
-        startContextualContainer: ViewGroup
-) : AbstractNavButtonLayoutter(
+    resources: Resources,
+    navBarContainer: LinearLayout,
+    endContextualContainer: ViewGroup,
+    startContextualContainer: ViewGroup
+) :
+    AbstractNavButtonLayoutter(
         resources,
         navBarContainer,
         endContextualContainer,
         startContextualContainer
-) {
+    ) {
 
     override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
-        val iconSize: Int = resources.getDimensionPixelSize(
-                DIMEN_TASKBAR_ICON_SIZE_KIDS)
-        val buttonWidth: Int = resources.getDimensionPixelSize(
-                DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS)
-        val buttonHeight: Int = resources.getDimensionPixelSize(
-                DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS)
-        val buttonRadius: Int = resources.getDimensionPixelSize(
-                DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS)
+        val iconSize: Int = resources.getDimensionPixelSize(DIMEN_TASKBAR_ICON_SIZE_KIDS)
+        val buttonWidth: Int = resources.getDimensionPixelSize(DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS)
+        val buttonHeight: Int =
+            resources.getDimensionPixelSize(DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS)
+        val buttonRadius: Int =
+            resources.getDimensionPixelSize(DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS)
         val paddingLeft = (buttonWidth - iconSize) / 2
         val paddingTop = (buttonHeight - iconSize) / 2
 
         // Update icons
-        backButton.setImageDrawable(
-                backButton.context.getDrawable(DRAWABLE_SYSBAR_BACK_KIDS))
+        backButton.setImageDrawable(backButton.context.getDrawable(DRAWABLE_SYSBAR_BACK_KIDS))
         backButton.scaleType = ImageView.ScaleType.FIT_CENTER
         backButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop)
-        homeButton.setImageDrawable(
-                homeButton.getContext().getDrawable(DRAWABLE_SYSBAR_HOME_KIDS))
+        homeButton.setImageDrawable(homeButton.getContext().getDrawable(DRAWABLE_SYSBAR_HOME_KIDS))
         homeButton.scaleType = ImageView.ScaleType.FIT_CENTER
         homeButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop)
 
         // Home button layout
-        val homeLayoutparams = LinearLayout.LayoutParams(
-                buttonWidth,
-                buttonHeight
-        )
-        val homeButtonLeftMargin: Int = resources.getDimensionPixelSize(
-                DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS)
+        val homeLayoutparams = LinearLayout.LayoutParams(buttonWidth, buttonHeight)
+        val homeButtonLeftMargin: Int =
+            resources.getDimensionPixelSize(DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS)
         homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0)
         homeButton.layoutParams = homeLayoutparams
 
         // Back button layout
-        val backLayoutParams = LinearLayout.LayoutParams(
-                buttonWidth,
-                buttonHeight
-        )
-        val backButtonLeftMargin: Int = resources.getDimensionPixelSize(
-                DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS)
+        val backLayoutParams = LinearLayout.LayoutParams(buttonWidth, buttonHeight)
+        val backButtonLeftMargin: Int =
+            resources.getDimensionPixelSize(DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS)
         backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0)
         backButton.layoutParams = backLayoutParams
 
@@ -106,4 +97,4 @@
 
         homeButton.onLongClickListener = null
     }
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
index db0a2d8..2092721 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -30,10 +30,9 @@
 /**
  * Select the correct layout for nav buttons
  *
- * Since layouts are done dynamically for the nav buttons on Taskbar, this
- * class returns a corresponding [NavButtonLayoutter] via
- * [Companion.getUiLayoutter]
- * that can help position the buttons based on the current [DeviceProfile]
+ * Since layouts are done dynamically for the nav buttons on Taskbar, this class returns a
+ * corresponding [NavButtonLayoutter] via [Companion.getUiLayoutter] that can help position the
+ * buttons based on the current [DeviceProfile]
  */
 class NavButtonLayoutFactory {
     companion object {
@@ -41,11 +40,10 @@
          * Get the correct instance of [NavButtonLayoutter]
          *
          * No layouts supported for configurations where:
-         *  * taskbar isn't showing AND
-         *  * the device is not in [phoneMode]
-         * OR
-         *  * phone is showing
-         *  * device is using gesture navigation
+         * * taskbar isn't showing AND
+         * * the device is not in [phoneMode] OR
+         * * phone is showing
+         * * device is using gesture navigation
          *
          * @param navButtonsView ViewGroup that contains start, end, nav button ViewGroups
          * @param isKidsMode no-op when taskbar is hidden/not showing
@@ -53,44 +51,64 @@
          * @param phoneMode refers to the device using the taskbar window on phones
          * @param isThreeButtonNav are no-ops when taskbar is present/showing
          */
-        fun getUiLayoutter(deviceProfile: DeviceProfile,
-                           navButtonsView: FrameLayout,
-                           resources: Resources,
-                           isKidsMode: Boolean,
-                           isInSetup: Boolean,
-                           isThreeButtonNav: Boolean,
-                           phoneMode: Boolean):
-                NavButtonLayoutter {
-            val navButtonContainer =
-                    navButtonsView.findViewById<LinearLayout>(ID_END_NAV_BUTTONS)
+        fun getUiLayoutter(
+            deviceProfile: DeviceProfile,
+            navButtonsView: FrameLayout,
+            resources: Resources,
+            isKidsMode: Boolean,
+            isInSetup: Boolean,
+            isThreeButtonNav: Boolean,
+            phoneMode: Boolean
+        ): NavButtonLayoutter {
+            val navButtonContainer = navButtonsView.findViewById<LinearLayout>(ID_END_NAV_BUTTONS)
             val endContextualContainer =
-                    navButtonsView.findViewById<ViewGroup>(ID_END_CONTEXTUAL_BUTTONS)
+                navButtonsView.findViewById<ViewGroup>(ID_END_CONTEXTUAL_BUTTONS)
             val startContextualContainer =
-                    navButtonsView.findViewById<ViewGroup>(ID_START_CONTEXTUAL_BUTTONS)
+                navButtonsView.findViewById<ViewGroup>(ID_START_CONTEXTUAL_BUTTONS)
             val isPhoneNavMode = phoneMode && isThreeButtonNav
             return when {
                 isPhoneNavMode -> {
                     if (!deviceProfile.isLandscape) {
-                        PhonePortraitNavLayoutter(resources, navButtonContainer,
-                                endContextualContainer, startContextualContainer)
+                        PhonePortraitNavLayoutter(
+                            resources,
+                            navButtonContainer,
+                            endContextualContainer,
+                            startContextualContainer
+                        )
                     } else {
-                        PhoneLandscapeNavLayoutter(resources, navButtonContainer,
-                                endContextualContainer, startContextualContainer)
+                        PhoneLandscapeNavLayoutter(
+                            resources,
+                            navButtonContainer,
+                            endContextualContainer,
+                            startContextualContainer
+                        )
                     }
                 }
                 deviceProfile.isTaskbarPresent -> {
                     return when {
                         isInSetup -> {
-                            SetupNavLayoutter(resources, navButtonContainer, endContextualContainer,
-                                    startContextualContainer)
+                            SetupNavLayoutter(
+                                resources,
+                                navButtonContainer,
+                                endContextualContainer,
+                                startContextualContainer
+                            )
                         }
                         isKidsMode -> {
-                            KidsNavLayoutter(resources, navButtonContainer, endContextualContainer,
-                                    startContextualContainer)
+                            KidsNavLayoutter(
+                                resources,
+                                navButtonContainer,
+                                endContextualContainer,
+                                startContextualContainer
+                            )
                         }
                         else ->
-                            TaskbarNavLayoutter(resources, navButtonContainer, endContextualContainer,
-                                    startContextualContainer)
+                            TaskbarNavLayoutter(
+                                resources,
+                                navButtonContainer,
+                                endContextualContainer,
+                                startContextualContainer
+                            )
                     }
                 }
                 else -> error("No layoutter found")
@@ -98,8 +116,8 @@
         }
     }
 
-    /** Lays out and provides access to the home, recents, and back buttons for various mischief  */
+    /** Lays out and provides access to the home, recents, and back buttons for various mischief */
     interface NavButtonLayoutter {
         fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean)
     }
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
index a89476e..201895f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
@@ -28,24 +28,24 @@
 import com.android.launcher3.util.DimensionUtils
 
 class PhoneLandscapeNavLayoutter(
-        resources: Resources,
-        navBarContainer: LinearLayout,
-        endContextualContainer: ViewGroup,
-        startContextualContainer: ViewGroup
-) : AbstractNavButtonLayoutter(
+    resources: Resources,
+    navBarContainer: LinearLayout,
+    endContextualContainer: ViewGroup,
+    startContextualContainer: ViewGroup
+) :
+    AbstractNavButtonLayoutter(
         resources,
         navBarContainer,
         endContextualContainer,
         startContextualContainer
-) {
+    ) {
 
     override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
         // TODO(b/230395757): Polish pending, this is just to make it usable
         val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
-        val endStartMargins =
-                resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
-        val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources,
-                TaskbarManager.isPhoneMode(dp))
+        val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+        val taskbarDimensions =
+            DimensionUtils.getTaskbarPhoneDimensions(dp, resources, TaskbarManager.isPhoneMode(dp))
         navButtonContainer.removeAllViews()
         navButtonContainer.orientation = LinearLayout.VERTICAL
 
@@ -68,7 +68,7 @@
 
         // Add the spaces in between the nav buttons
         val spaceInBetween: Int =
-                resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
+            resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
         navButtonContainer.children.forEachIndexed { i, navButton ->
             val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
             buttonLayoutParams.weight = 1f
@@ -86,4 +86,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
index 275f59f..f7ac974 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
@@ -26,17 +26,24 @@
 import com.android.launcher3.taskbar.TaskbarManager
 import com.android.launcher3.util.DimensionUtils
 
-class PhonePortraitNavLayoutter(resources: Resources, navBarContainer: LinearLayout,
-                                endContextualContainer: ViewGroup,
-                                startContextualContainer: ViewGroup) :
-        AbstractNavButtonLayoutter(resources, navBarContainer, endContextualContainer,
-                startContextualContainer) {
+class PhonePortraitNavLayoutter(
+    resources: Resources,
+    navBarContainer: LinearLayout,
+    endContextualContainer: ViewGroup,
+    startContextualContainer: ViewGroup
+) :
+    AbstractNavButtonLayoutter(
+        resources,
+        navBarContainer,
+        endContextualContainer,
+        startContextualContainer
+    ) {
 
     override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
         // TODO(b/230395757): Polish pending, this is just to make it usable
         val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
-        val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources,
-                TaskbarManager.isPhoneMode(dp))
+        val taskbarDimensions =
+            DimensionUtils.getTaskbarPhoneDimensions(dp, resources, TaskbarManager.isPhoneMode(dp))
         val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
         navContainerParams.width = taskbarDimensions.x
         navContainerParams.height = ViewGroup.LayoutParams.MATCH_PARENT
@@ -58,7 +65,7 @@
 
         // Add the spaces in between the nav buttons
         val spaceInBetween =
-                resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
+            resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
         for (i in 0 until navButtonContainer.childCount) {
             val navButton = navButtonContainer.getChildAt(i)
             val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
@@ -80,4 +87,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
index afe70d6..a24002c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -24,16 +24,17 @@
 import com.android.launcher3.DeviceProfile
 
 class SetupNavLayoutter(
-        resources: Resources,
-        navButtonContainer: LinearLayout,
-        endContextualContainer: ViewGroup,
-        startContextualContainer: ViewGroup
-) : AbstractNavButtonLayoutter(
+    resources: Resources,
+    navButtonContainer: LinearLayout,
+    endContextualContainer: ViewGroup,
+    startContextualContainer: ViewGroup
+) :
+    AbstractNavButtonLayoutter(
         resources,
         navButtonContainer,
         endContextualContainer,
         startContextualContainer
-) {
+    ) {
 
     override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
         // Since setup wizard only has back button enabled, it looks strange to be
@@ -46,4 +47,4 @@
         }
         navButtonContainer.requestLayout()
     }
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
index b2ca2af..5ec7ca0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -24,20 +24,19 @@
 import com.android.launcher3.DeviceProfile
 import com.android.launcher3.R
 
-/**
- * Layoutter for showing 3 button navigation on large screen
- */
+/** Layoutter for showing 3 button navigation on large screen */
 class TaskbarNavLayoutter(
-        resources: Resources,
-        navBarContainer: LinearLayout,
-        endContextualContainer: ViewGroup,
-        startContextualContainer: ViewGroup
-) : AbstractNavButtonLayoutter(
+    resources: Resources,
+    navBarContainer: LinearLayout,
+    endContextualContainer: ViewGroup,
+    startContextualContainer: ViewGroup
+) :
+    AbstractNavButtonLayoutter(
         resources,
         navBarContainer,
         endContextualContainer,
         startContextualContainer
-) {
+    ) {
 
     override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
         // Add spacing after the end of the last nav button
@@ -80,4 +79,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 9be569e..a7651b6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -43,7 +43,6 @@
 import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
-import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 
@@ -63,15 +62,18 @@
 import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.SystemProperties;
-import android.util.Log;
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.RemoteAnimationTarget;
 import android.view.View;
 import android.view.WindowManagerGlobal;
+import android.window.BackEvent;
+import android.window.OnBackAnimationCallback;
+import android.window.OnBackInvokedDispatcher;
 import android.window.SplashScreen;
 
 import androidx.annotation.BinderThread;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.app.viewcapture.ViewCapture;
@@ -105,6 +107,8 @@
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.taskbar.LauncherTaskbarUIController;
 import com.android.launcher3.taskbar.TaskbarManager;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepHolderFactory;
 import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
 import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
@@ -623,6 +627,29 @@
         }
     }
 
+    @Override
+    protected void registerBackDispatcher() {
+        getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+                OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+                new OnBackAnimationCallback() {
+                    @Override
+                    public void onBackInvoked() {
+                        onBackPressed();
+                        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
+                    }
+
+                    @Override
+                    public void onBackProgressed(@NonNull BackEvent backEvent) {
+                        QuickstepLauncher.this.onBackProgressed(backEvent.getProgress());
+                    }
+
+                    @Override
+                    public void onBackCancelled() {
+                        QuickstepLauncher.this.onBackCancelled();
+                    }
+                });
+    }
+
     private void onTaskbarInAppDisplayProgressUpdate(float progress, int flag) {
         if (mTaskbarManager == null
                 || mTaskbarManager.getCurrentActivityContext() == null
@@ -668,7 +695,8 @@
     public void setResumed() {
         if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
             DesktopVisibilityController controller = mDesktopVisibilityController;
-            if (controller != null && controller.areFreeformTasksVisible()) {
+            if (controller != null && controller.areFreeformTasksVisible()
+                    && !controller.isGestureInProgress()) {
                 // Return early to skip setting activity to appear as resumed
                 // TODO(b/255649902): shouldn't be needed when we have a separate launcher state
                 //  for desktop that we can use to control other parts of launcher
@@ -721,7 +749,7 @@
                             getSystemService(SensorManager.class),
                             getMainThreadHandler(),
                             getMainExecutor(),
-                            /* backgroundExecutor= */ THREAD_POOL_EXECUTOR,
+                            /* backgroundExecutor= */ UI_HELPER_EXECUTOR,
                             /* tracingTagPrefix= */ "launcher",
                             WindowManagerGlobal.getWindowManagerService()
                     );
@@ -738,7 +766,6 @@
                     mUnfoldTransitionProgressProvider,
                     mRotationChangeProvider
             );
-            Log.d("b/261320823", "initUnfoldTransitionProgressProvider completed");
         }
     }
 
@@ -1001,6 +1028,14 @@
         mPendingSplitSelectInfo = null;
     }
 
+    @Override
+    public boolean areFreeformTasksVisible() {
+        if (mDesktopVisibilityController != null) {
+            return mDesktopVisibilityController.areFreeformTasksVisible();
+        }
+        return false;
+    }
+
     private static final class LauncherTaskViewController extends
             TaskViewTouchController<Launcher> {
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 733c6a8..95eb128 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -28,6 +28,7 @@
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.views.DesktopTaskView;
 import com.android.quickstep.views.RecentsView;
 
 /**
@@ -91,6 +92,12 @@
 
     @Override
     protected float getDepthUnchecked(Context context) {
+        if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+            if (Launcher.getLauncher(context).areFreeformTasksVisible()) {
+                // Don't blur the background while freeform tasks are visible
+                return 0;
+            }
+        }
         return 1;
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 969abc2..7392469 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -17,10 +17,13 @@
 
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
 
+import android.graphics.Color;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.util.Themes;
+import com.android.quickstep.views.DesktopTaskView;
 
 /**
  * State to indicate we are about to launch a recent task. Note that this state is only used when
@@ -43,6 +46,12 @@
 
     @Override
     public int getWorkspaceScrimColor(Launcher launcher) {
+        if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+            if (launcher.areFreeformTasksVisible()) {
+                // No scrim while freeform tasks are visible
+                return Color.TRANSPARENT;
+            }
+        }
         DeviceProfile dp = launcher.getDeviceProfile();
         if (dp.isTaskbarPresentInApps) {
             return launcher.getColor(R.color.taskbar_background);
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 8409475..a2adbd7 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -323,6 +323,7 @@
     private final boolean mIsTransientTaskbar;
     // May be set to false when mIsTransientTaskbar is true.
     private boolean mCanSlowSwipeGoHome = true;
+    private boolean mHasReachedOverviewThreshold = false;
 
     @Nullable
     private RemoteAnimationTargets.ReleaseCheck mSwipePipToHomeReleaseCheck = null;
@@ -765,6 +766,10 @@
 
     private void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask, boolean animate) {
         if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) {
+            if (isLikelyToStartNewTask && mIsTransientTaskbar) {
+                setDividerShown(false /* shown */, true /* immediate */);
+            }
+
             mIsLikelyToStartNewTask = isLikelyToStartNewTask;
             maybeUpdateRecentsAttachedState(animate);
         }
@@ -1669,7 +1674,9 @@
         mRecentsAnimationController.enableInputConsumer();
 
         // Start hiding the divider
-        setDividerShown(false /* shown */, true /* immediate */);
+        if (!mIsTransientTaskbar || mTaskbarAlreadyOpen || mIsTaskbarAllAppsOpen) {
+            setDividerShown(false /* shown */, true /* immediate */);
+        }
     }
 
     private void computeRecentsScrollIfInvisible() {
@@ -2305,6 +2312,10 @@
 
         // "Catch up" with the displacement at mTaskbarCatchUpThreshold.
         if (displacement < mTaskbarCatchUpThreshold) {
+            if (!mHasReachedOverviewThreshold) {
+                setDividerShown(false /* shown */, true /* immediate */);
+                mHasReachedOverviewThreshold = true;
+            }
             return Utilities.mapToRange(displacement, mTaskbarAppWindowThreshold,
                     mTaskbarCatchUpThreshold, 0, mTaskbarCatchUpThreshold, ACCEL_DEACCEL);
         }
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 2741751..03042c9 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -36,6 +36,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.window.BackEvent;
+import android.window.BackMotionEvent;
 import android.window.BackProgressAnimator;
 import android.window.IOnBackInvokedCallback;
 
@@ -134,14 +135,14 @@
             }
 
             @Override
-            public void onBackProgressed(BackEvent backEvent) {
+            public void onBackProgressed(BackMotionEvent backEvent) {
                 handler.post(() -> {
                     mProgressAnimator.onBackProgressed(backEvent);
                 });
             }
 
             @Override
-            public void onBackStarted(BackEvent backEvent) {
+            public void onBackStarted(BackMotionEvent backEvent) {
                 handler.post(() -> {
                     startBack(backEvent);
                     mProgressAnimator.onBackStarted(backEvent, event -> {
@@ -185,7 +186,7 @@
         mBackCallback = null;
     }
 
-    private void startBack(BackEvent backEvent) {
+    private void startBack(BackMotionEvent backEvent) {
         mBackInProgress = true;
         RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
 
@@ -289,7 +290,8 @@
                     new RemoteAnimationTarget[0],
                     false /* fromUnlock */,
                     mCurrentRect,
-                    cornerRadius);
+                    cornerRadius,
+                    mBackInProgress /* fromPredictiveBack */);
         startTransitionAnimations(pair.first, pair.second);
         mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index dc405ff..6f86bf5 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -240,7 +240,7 @@
 
         mActivityLaunchAnimationRunner = new RemoteAnimationFactory() {
             @Override
-            public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets,
+            public void onAnimationStart(int transit, RemoteAnimationTarget[] appTargets,
                     RemoteAnimationTarget[] wallpaperTargets,
                     RemoteAnimationTarget[] nonAppTargets, AnimationResult result) {
                 mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
@@ -407,28 +407,24 @@
     }
 
     private final RemoteAnimationFactory mAnimationToHomeFactory =
-            new RemoteAnimationFactory() {
-        @Override
-        public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets,
-                RemoteAnimationTarget[] wallpaperTargets,
-                RemoteAnimationTarget[] nonAppTargets, AnimationResult result) {
-            AnimatorPlaybackController controller = getStateManager()
-                    .createAnimationToNewWorkspace(RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION);
-            controller.dispatchOnStart();
+            (transit, appTargets, wallpaperTargets, nonAppTargets, result) -> {
+                AnimatorPlaybackController controller =
+                        getStateManager().createAnimationToNewWorkspace(
+                                RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION);
+                controller.dispatchOnStart();
 
-            RemoteAnimationTargets targets = new RemoteAnimationTargets(
-                    appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING);
-            for (RemoteAnimationTarget app : targets.apps) {
-                new Transaction().setAlpha(app.leash, 1).apply();
-            }
-            AnimatorSet anim = new AnimatorSet();
-            anim.play(controller.getAnimationPlayer());
-            anim.setDuration(HOME_APPEAR_DURATION);
-            result.setAnimation(anim, RecentsActivity.this,
-                    () -> getStateManager().goToState(RecentsState.HOME, false),
-                    true /* skipFirstFrame */);
-        }
-    };
+                RemoteAnimationTargets targets = new RemoteAnimationTargets(
+                        appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING);
+                for (RemoteAnimationTarget app : targets.apps) {
+                    new Transaction().setAlpha(app.leash, 1).apply();
+                }
+                AnimatorSet anim = new AnimatorSet();
+                anim.play(controller.getAnimationPlayer());
+                anim.setDuration(HOME_APPEAR_DURATION);
+                result.setAnimation(anim, RecentsActivity.this,
+                        () -> getStateManager().goToState(RecentsState.HOME, false),
+                        true /* skipFirstFrame */);
+            };
 
     @Override
     protected void collectStateHandlers(List<StateHandler> out) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 9e25555..f1c0f3e 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -17,7 +17,6 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.content.Intent.ACTION_USER_UNLOCKED;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
@@ -50,10 +49,8 @@
 import android.graphics.Region;
 import android.inputmethodservice.InputMethodService;
 import android.net.Uri;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemProperties;
-import android.os.UserManager;
 import android.provider.Settings;
 import android.view.MotionEvent;
 
@@ -63,9 +60,9 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.LockedUserState;
 import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.SettingsCache;
-import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
 import com.android.quickstep.util.NavBarPosition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -109,15 +106,6 @@
     private final boolean mIsOneHandedModeSupported;
     private boolean mPipIsActive;
 
-    private boolean mIsUserUnlocked;
-    private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
-    private final SimpleBroadcastReceiver mUserUnlockedReceiver = new SimpleBroadcastReceiver(i -> {
-        if (ACTION_USER_UNLOCKED.equals(i.getAction())) {
-            mIsUserUnlocked = true;
-            notifyUserUnlocked();
-        }
-    });
-
     private int mGestureBlockingTaskId = -1;
     private @NonNull Region mExclusionRegion = new Region();
     private SystemGestureExclusionListenerCompat mExclusionListener;
@@ -143,14 +131,6 @@
             runOnDestroy(mRotationTouchHelper::destroy);
         }
 
-        // Register for user unlocked if necessary
-        mIsUserUnlocked = context.getSystemService(UserManager.class)
-                .isUserUnlocked(Process.myUserHandle());
-        if (!mIsUserUnlocked) {
-            mUserUnlockedReceiver.register(mContext, ACTION_USER_UNLOCKED);
-        }
-        runOnDestroy(() -> mUserUnlockedReceiver.unregisterReceiverSafely(mContext));
-
         // Register for exclusion updates
         mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
             @Override
@@ -310,39 +290,12 @@
     }
 
     /**
-     * Adds a callback for when a user is unlocked. If the user is already unlocked, this listener
-     * will be called back immediately.
-     */
-    public void runOnUserUnlocked(Runnable action) {
-        if (mIsUserUnlocked) {
-            action.run();
-        } else {
-            mUserUnlockedActions.add(action);
-        }
-    }
-
-    /**
-     * @return whether the user is unlocked.
-     */
-    public boolean isUserUnlocked() {
-        return mIsUserUnlocked;
-    }
-
-    /**
      * @return whether the user has completed setup wizard
      */
     public boolean isUserSetupComplete() {
         return mIsUserSetupComplete;
     }
 
-    private void notifyUserUnlocked() {
-        for (Runnable action : mUserUnlockedActions) {
-            action.run();
-        }
-        mUserUnlockedActions.clear();
-        mUserUnlockedReceiver.unregisterReceiverSafely(mContext);
-    }
-
     /**
      * Sets the task id where gestures should be blocked
      */
@@ -585,7 +538,7 @@
         pw.println("  assistantAvailable=" + mAssistantAvailable);
         pw.println("  assistantDisabled="
                 + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
-        pw.println("  isUserUnlocked=" + mIsUserUnlocked);
+        pw.println("  isUserUnlocked=" + LockedUserState.get(mContext).isUserUnlocked());
         pw.println("  isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
         pw.println("  isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
         pw.println("  deferredGestureRegion=" + mDeferredGestureRegion.getBounds());
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 5d17cc7..d19f124 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -87,6 +87,7 @@
 import com.android.launcher3.tracing.TouchInteractionServiceProto;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.LockedUserState;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
@@ -406,8 +407,8 @@
         mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
 
         // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
-        mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
-        mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
+        LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked);
+        LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
         mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
 
         ProtoTracer.INSTANCE.get(this).add(this);
@@ -477,7 +478,7 @@
     }
 
     private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
-        if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
+        if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) {
             // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
             // mode doesn't have gestures
             return;
@@ -520,7 +521,7 @@
 
     @UiThread
     private void onSystemUiFlagsChanged(int lastSysUIFlags) {
-        if (mDeviceState.isUserUnlocked()) {
+        if (LockedUserState.get(this).isUserUnlocked()) {
             int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
             SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
             mOverviewComponentObserver.onSystemUiStateChanged();
@@ -565,7 +566,7 @@
 
     @UiThread
     private void onAssistantVisibilityChanged() {
-        if (mDeviceState.isUserUnlocked()) {
+        if (LockedUserState.get(this).isUserUnlocked()) {
             mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged(
                     mDeviceState.getAssistantVisibility());
         }
@@ -575,7 +576,7 @@
     public void onDestroy() {
         Log.d(TAG, "Touch service destroyed: user=" + getUserId());
         sIsInitialized = false;
-        if (mDeviceState.isUserUnlocked()) {
+        if (LockedUserState.get(this).isUserUnlocked()) {
             mInputConsumer.unregisterInputConsumer();
             mOverviewComponentObserver.onDestroy();
         }
@@ -609,7 +610,7 @@
         TestLogging.recordMotionEvent(
                 TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
 
-        if (!mDeviceState.isUserUnlocked()) {
+        if (!LockedUserState.get(this).isUserUnlocked()) {
             return;
         }
 
@@ -631,7 +632,8 @@
                 mGestureState = newGestureState;
                 mConsumer = newConsumer(prevGestureState, mGestureState, event);
                 mUncheckedConsumer = mConsumer;
-            } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()
+            } else if (LockedUserState.get(this).isUserUnlocked()
+                    && mDeviceState.isFullyGesturalNavMode()
                     && mDeviceState.canTriggerAssistantAction(event)) {
                 mGestureState = createGestureState(mGestureState);
                 // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
@@ -751,7 +753,7 @@
 
         boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
 
-        if (!mDeviceState.isUserUnlocked()) {
+        if (!LockedUserState.get(this).isUserUnlocked()) {
             CompoundString reasonString = newCompoundString("device locked");
             InputConsumer consumer;
             if (canStartSystemGesture) {
@@ -800,7 +802,7 @@
 
             // If Taskbar is present, we listen for long press to unstash it.
             TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
-            if (tac != null) {
+            if (tac != null && canStartSystemGesture) {
                 reasonString.append(NEWLINE_PREFIX)
                         .append(reasonPrefix)
                         .append(SUBSTRING_PREFIX)
@@ -1098,7 +1100,7 @@
     }
 
     private void preloadOverview(boolean fromInit, boolean forSUWAllSet) {
-        if (!mDeviceState.isUserUnlocked()) {
+        if (!LockedUserState.get(this).isUserUnlocked()) {
             return;
         }
 
@@ -1130,7 +1132,7 @@
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
-        if (!mDeviceState.isUserUnlocked()) {
+        if (!LockedUserState.get(this).isUserUnlocked()) {
             return;
         }
         final BaseActivityInterface activityInterface =
@@ -1171,7 +1173,7 @@
         } else {
             // Dump everything
             FeatureFlags.dump(pw);
-            if (mDeviceState.isUserUnlocked()) {
+            if (LockedUserState.get(this).isUserUnlocked()) {
                 PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
             }
             mDeviceState.dump(pw);
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index 877e28a..cecf58d 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -108,7 +108,10 @@
         float depth = mDepth;
         IBinder windowToken = mLauncher.getRootView().getWindowToken();
         if (windowToken != null) {
-            mWallpaperManager.setWallpaperZoomOut(windowToken, depth);
+            // The API's full zoom-out is three times larger than the zoom-out we apply to the
+            // icons. To keep the two consistent throughout the animation while keeping Launcher's
+            // concept of full depth unchanged, we divide the depth by 3 here.
+            mWallpaperManager.setWallpaperZoomOut(windowToken, depth / 3);
         }
 
         if (!BlurUtils.supportsBlursOnWindows()) {
diff --git a/quickstep/src/com/android/quickstep/util/LogUtils.kt b/quickstep/src/com/android/quickstep/util/LogUtils.kt
index bad8506..300c4a1 100644
--- a/quickstep/src/com/android/quickstep/util/LogUtils.kt
+++ b/quickstep/src/com/android/quickstep/util/LogUtils.kt
@@ -20,15 +20,14 @@
 import com.android.launcher3.logging.InstanceId
 
 object LogUtils {
-  /**
-   * @return a [Pair] of two InstanceIds but with different types, one that can be used by framework
-   * (if needing to pass through an intent or such) and one used in Launcher
-   */
-  @JvmStatic
-  fun getShellShareableInstanceId():
-    Pair<com.android.internal.logging.InstanceId, InstanceId> {
-    val internalInstanceId = InstanceIdSequence(InstanceId.INSTANCE_ID_MAX).newInstanceId()
-    val launcherInstanceId = InstanceId(internalInstanceId.id)
-    return Pair(internalInstanceId, launcherInstanceId)
-  }
+    /**
+     * @return a [Pair] of two InstanceIds but with different types, one that can be used by
+     * framework (if needing to pass through an intent or such) and one used in Launcher
+     */
+    @JvmStatic
+    fun getShellShareableInstanceId(): Pair<com.android.internal.logging.InstanceId, InstanceId> {
+        val internalInstanceId = InstanceIdSequence(InstanceId.INSTANCE_ID_MAX).newInstanceId()
+        val launcherInstanceId = InstanceId(internalInstanceId.id)
+        return Pair(internalInstanceId, launcherInstanceId)
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index c878278..858f6ab 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -81,6 +81,8 @@
 
     private final ArrayList<CancellableTask<?>> mPendingThumbnailRequests = new ArrayList<>();
 
+    private ShapeDrawable mBackground;
+
     public DesktopTaskView(Context context) {
         this(context, null);
     }
@@ -99,10 +101,11 @@
         float[] outerRadii = new float[8];
         Arrays.fill(outerRadii, getTaskCornerRadius());
         RoundRectShape shape = new RoundRectShape(outerRadii, null, null);
-        ShapeDrawable background = new ShapeDrawable(shape);
-        background.setTint(getResources().getColor(android.R.color.system_neutral2_300));
+        mBackground = new ShapeDrawable(shape);
+        mBackground.setTint(getResources().getColor(android.R.color.system_neutral2_300,
+                getContext().getTheme()));
         // TODO(b/244348395): this should be wallpaper
-        setBackground(background);
+        setBackground(mBackground);
 
         mSnapshotViews.add(mSnapshotView);
     }
@@ -427,6 +430,12 @@
         // TODO(b/249371338): this copies parent implementation and makes it work for N thumbs
         progress = Utilities.boundToRange(progress, 0, 1);
         mFullscreenProgress = progress;
+        if (mFullscreenProgress > 0) {
+            // Don't show background while we are transitioning to/from fullscreen
+            setBackground(null);
+        } else {
+            setBackground(mBackground);
+        }
         for (int i = 0; i < mSnapshotViewMap.size(); i++) {
             TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i);
             thumbnailView.getTaskOverlay().setFullscreenProgress(progress);
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 6c27587..cf033a6 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -36,12 +36,15 @@
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.popup.QuickstepSystemShortcut;
 import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.PendingSplitSelectInfo;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.quickstep.LauncherActivityInterface;
+import com.android.quickstep.RotationTouchHelper;
 import com.android.quickstep.util.SplitSelectStateController;
+import com.android.systemui.shared.recents.model.Task;
 
 /**
  * {@link RecentsView} used in Launcher activity
@@ -205,4 +208,25 @@
     protected boolean canLaunchFullscreenTask() {
         return !mActivity.isInState(OVERVIEW_SPLIT_SELECT);
     }
+
+    @Override
+    public void onGestureAnimationStart(Task[] runningTasks,
+            RotationTouchHelper rotationTouchHelper) {
+        super.onGestureAnimationStart(runningTasks, rotationTouchHelper);
+        DesktopVisibilityController desktopVisibilityController =
+                mActivity.getDesktopVisibilityController();
+        if (desktopVisibilityController != null) {
+            desktopVisibilityController.setGestureInProgress(true);
+        }
+    }
+
+    @Override
+    public void onGestureAnimationEnd() {
+        super.onGestureAnimationEnd();
+        DesktopVisibilityController desktopVisibilityController =
+                mActivity.getDesktopVisibilityController();
+        if (desktopVisibilityController != null) {
+            desktopVisibilityController.setGestureInProgress(false);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 5e645ea..c11f7ed 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -3040,7 +3040,7 @@
                     false /* fadeWithThumbnail */, true /* isStagedTask */);
         }
 
-        // TODO (b/257513449): Launch animation not fully complete. OK to remove flag once it is.
+        // Allow user to click staged app to launch into fullscreen
         if (ENABLE_LAUNCH_FROM_STAGED_APP.get()) {
             mFirstFloatingTaskView.setOnClickListener(this::animateToFullscreen);
         }
@@ -3111,7 +3111,9 @@
                 false /* fadeWithThumbnail */,
                 true /* isStagedTask */);
 
-        pendingAnimation.addEndListener(success -> launchStagedTask());
+        pendingAnimation.addEndListener(animationSuccess ->
+                mSplitSelectStateController.launchSplitTasks(launchSuccess ->
+                        resetFromSplitSelectionState()));
 
         pendingAnimation.buildAnim().start();
     }
@@ -3189,8 +3191,6 @@
             }
         }
 
-        announceForAccessibility(getResources().getString(R.string.task_view_closed));
-
         float dismissTranslationInterpolationEnd = 1;
         boolean closeGapBetweenClearAll = false;
         boolean isClearAllHidden = isClearAllHidden();
@@ -3517,6 +3517,8 @@
                             } else {
                                 removeTaskInternal(dismissedTaskViewId);
                             }
+                            announceForAccessibility(
+                                    getResources().getString(R.string.task_view_closed));
                             mActivity.getStatsLogManager().logger()
                                     .withItemInfo(dismissedTaskView.getItemInfo())
                                     .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
@@ -4818,16 +4820,6 @@
         return mPendingAnimation;
     }
 
-    protected void launchStagedTask() {
-        if (mSplitHiddenTaskView != null) {
-            // Split staging was started from an existing running task (in Overview)
-            mSplitHiddenTaskView.launchTask(success -> resetFromSplitSelectionState());
-        } else {
-            // Split staging was started from a new intent (from app menu in Home/AllApps)
-            mActivity.startActivity(mSplitSelectSource.intent);
-        }
-    }
-
     protected void onTaskLaunchAnimationEnd(boolean success) {
         if (success) {
             resetTaskVisuals();
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
index bdc0585..54b58b9 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -48,13 +48,15 @@
             taskContainer: TaskIdAttributeContainer,
             alignSecondRow: Boolean = false
         ): Boolean {
-            val activity = BaseDraggingActivity
-                .fromContext<BaseDraggingActivity>(taskContainer.taskView.context)
-            val taskMenuViewWithArrow = activity.layoutInflater
-                .inflate(
-                        R.layout.task_menu_with_arrow,
-                        activity.dragLayer,
-                        false
+            val activity =
+                BaseDraggingActivity.fromContext<BaseDraggingActivity>(
+                    taskContainer.taskView.context
+                )
+            val taskMenuViewWithArrow =
+                activity.layoutInflater.inflate(
+                    R.layout.task_menu_with_arrow,
+                    activity.dragLayer,
+                    false
                 ) as TaskMenuViewWithArrow<*>
 
             return taskMenuViewWithArrow.populateAndShowForTask(taskContainer, alignSecondRow)
@@ -63,11 +65,11 @@
 
     constructor(context: Context) : super(context)
     constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
-    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
-        context,
-        attrs,
-        defStyleAttr
-    )
+    constructor(
+        context: Context,
+        attrs: AttributeSet,
+        defStyleAttr: Int
+    ) : super(context, attrs, defStyleAttr)
 
     init {
         clipToOutline = true
@@ -91,10 +93,10 @@
 
     private var optionMeasuredHeight = 0
     private val arrowHorizontalPadding: Int
-        get() = if (taskView.isFocusedTask)
-            resources.getDimensionPixelSize(R.dimen.task_menu_horizontal_padding)
-        else
-            0
+        get() =
+            if (taskView.isFocusedTask)
+                resources.getDimensionPixelSize(R.dimen.task_menu_horizontal_padding)
+            else 0
 
     private var iconView: IconView? = null
     private var scrim: View? = null
@@ -139,19 +141,20 @@
     }
 
     private fun addScrim() {
-        scrim = View(context).apply {
-            layoutParams = FrameLayout.LayoutParams(
-                FrameLayout.LayoutParams.MATCH_PARENT,
-                FrameLayout.LayoutParams.MATCH_PARENT
-            )
-            setBackgroundColor(Themes.getAttrColor(context, R.attr.overviewScrimColor))
-            alpha = 0f
-        }
+        scrim =
+            View(context).apply {
+                layoutParams =
+                    FrameLayout.LayoutParams(
+                        FrameLayout.LayoutParams.MATCH_PARENT,
+                        FrameLayout.LayoutParams.MATCH_PARENT
+                    )
+                setBackgroundColor(Themes.getAttrColor(context, R.attr.overviewScrimColor))
+                alpha = 0f
+            }
         popupContainer.addView(scrim)
     }
 
-    /** @return true if successfully able to populate task view menu, false otherwise
-     */
+    /** @return true if successfully able to populate task view menu, false otherwise */
     private fun populateMenu(): Boolean {
         // Icon may not be loaded
         if (taskContainer.task.icon == null) return false
@@ -162,9 +165,9 @@
 
     private fun addMenuOptions() {
         // Add the options
-        TaskOverlayFactory
-            .getEnabledShortcuts(taskView, taskContainer)
-            .forEach { this.addMenuOption(it) }
+        TaskOverlayFactory.getEnabledShortcuts(taskView, taskContainer).forEach {
+            this.addMenuOption(it)
+        }
 
         // Add the spaces between items
         val divider = ShapeDrawable(RectShape())
@@ -185,9 +188,9 @@
     }
 
     private fun addMenuOption(menuOption: SystemShortcut<*>) {
-        val menuOptionView = mActivityContext.layoutInflater.inflate(
-                R.layout.task_view_menu_option, this, false
-        ) as LinearLayout
+        val menuOptionView =
+            mActivityContext.layoutInflater.inflate(R.layout.task_view_menu_option, this, false)
+                as LinearLayout
         menuOption.setIconAndLabelFor(
             menuOptionView.findViewById(R.id.icon),
             menuOptionView.findViewById(R.id.text)
@@ -230,24 +233,25 @@
     }
 
     /**
-     * Copy the iconView from taskView to dragLayer so it can stay on top of the scrim.
-     * It needs to be called after [getTargetObjectLocation] because [mTempRect] needs to be
-     * populated.
+     * Copy the iconView from taskView to dragLayer so it can stay on top of the scrim. It needs to
+     * be called after [getTargetObjectLocation] because [mTempRect] needs to be populated.
      */
     private fun copyIconToDragLayer(insets: Rect) {
-        iconView = IconView(context).apply {
-            layoutParams = FrameLayout.LayoutParams(
-                taskContainer.iconView.width,
-                taskContainer.iconView.height
-            )
-            x = mTempRect.left.toFloat() - insets.left
-            y = mTempRect.top.toFloat() - insets.top
-            drawable = taskContainer.iconView.drawable
-            setDrawableSize(
-                taskContainer.iconView.drawableWidth,
-                taskContainer.iconView.drawableHeight
-            )
-        }
+        iconView =
+            IconView(context).apply {
+                layoutParams =
+                    FrameLayout.LayoutParams(
+                        taskContainer.iconView.width,
+                        taskContainer.iconView.height
+                    )
+                x = mTempRect.left.toFloat() - insets.left
+                y = mTempRect.top.toFloat() - insets.top
+                drawable = taskContainer.iconView.drawable
+                setDrawableSize(
+                    taskContainer.iconView.drawableWidth,
+                    taskContainer.iconView.drawableHeight
+                )
+            }
 
         popupContainer.addView(iconView)
     }
@@ -281,12 +285,13 @@
         // which means the arrow is left aligned with the menu
         val rightAlignedMenuStartX = mTempRect.left - widthWithArrow
         val leftAlignedMenuStartX = mTempRect.right + extraHorizontalSpace
-        mIsLeftAligned = if (mIsRtl) {
-            rightAlignedMenuStartX + insets.left < 0
-        } else {
-            leftAlignedMenuStartX + (widthWithArrow - extraHorizontalSpace) + insets.left <
+        mIsLeftAligned =
+            if (mIsRtl) {
+                rightAlignedMenuStartX + insets.left < 0
+            } else {
+                leftAlignedMenuStartX + (widthWithArrow - extraHorizontalSpace) + insets.left <
                     dragLayer.width - insets.right
-        }
+            }
 
         var menuStartX = if (mIsLeftAligned) leftAlignedMenuStartX else rightAlignedMenuStartX
 
@@ -311,8 +316,8 @@
     override fun addArrow() {
         popupContainer.addView(mArrow)
         mArrow.x = getArrowX()
-        mArrow.y = y + (optionMeasuredHeight / 2) - (mArrowHeight / 2) +
-                extraSpaceForSecondRowAlignment
+        mArrow.y =
+            y + (optionMeasuredHeight / 2) - (mArrowHeight / 2) + extraSpaceForSecondRowAlignment
 
         updateArrowColor()
 
@@ -322,22 +327,19 @@
     }
 
     private fun getArrowX(): Float {
-        return if (mIsLeftAligned)
-            x - mArrowHeight
-        else
-            x + measuredWidth + mArrowOffsetVertical
+        return if (mIsLeftAligned) x - mArrowHeight else x + measuredWidth + mArrowOffsetVertical
     }
 
     override fun updateArrowColor() {
-        mArrow.background = RoundedArrowDrawable(
-            mArrowWidth.toFloat(),
-            mArrowHeight.toFloat(),
-            mArrowPointRadius.toFloat(),
-            mIsLeftAligned,
-            mArrowColor
-        )
+        mArrow.background =
+            RoundedArrowDrawable(
+                mArrowWidth.toFloat(),
+                mArrowHeight.toFloat(),
+                mArrowPointRadius.toFloat(),
+                mIsLeftAligned,
+                mArrowColor
+            )
         elevation = mElevation
         mArrow.elevation = mElevation
     }
-
-}
\ No newline at end of file
+}
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 58f0949..236b5db 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
@@ -8,38 +8,29 @@
 import android.widget.LinearLayout
 import androidx.test.runner.AndroidJUnit4
 import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
 import com.android.launcher3.taskbar.TaskbarManager
+import java.lang.IllegalStateException
+import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import com.android.launcher3.R
-import org.junit.Assume.assumeTrue
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
-import java.lang.IllegalStateException
 
 @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 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
 
     @Before
     fun setup() {
@@ -53,11 +44,11 @@
 
         // Init top level layout
         whenever(mockParentButtonContainer.findViewById<LinearLayout>(R.id.end_nav_buttons))
-                .thenReturn(mockNavLayout)
+            .thenReturn(mockNavLayout)
         whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.end_contextual_buttons))
-                .thenReturn(mockEndContextualLayout)
+            .thenReturn(mockEndContextualLayout)
         whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.start_contextual_buttons))
-                .thenReturn(mockStartContextualLayout)
+            .thenReturn(mockStartContextualLayout)
     }
 
     @Test
@@ -65,8 +56,12 @@
         assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
         mockDeviceProfile.isTaskbarPresent = true
         val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
-                getLayoutter(isKidsMode = true, isInSetup = false, isThreeButtonNav = false,
-                        phoneMode = false)
+            getLayoutter(
+                isKidsMode = true,
+                isInSetup = false,
+                isThreeButtonNav = false,
+                phoneMode = false
+            )
         assert(layoutter is KidsNavLayoutter)
     }
 
@@ -75,8 +70,12 @@
         assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
         mockDeviceProfile.isTaskbarPresent = true
         val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
-                getLayoutter(isKidsMode = false, isInSetup = true, isThreeButtonNav = false,
-                        phoneMode = false)
+            getLayoutter(
+                isKidsMode = false,
+                isInSetup = true,
+                isThreeButtonNav = false,
+                phoneMode = false
+            )
         assert(layoutter is SetupNavLayoutter)
     }
 
@@ -85,8 +84,12 @@
         assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
         mockDeviceProfile.isTaskbarPresent = true
         val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
-                getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false,
-                        phoneMode = false)
+            getLayoutter(
+                isKidsMode = false,
+                isInSetup = false,
+                isThreeButtonNav = false,
+                phoneMode = false
+            )
         assert(layoutter is TaskbarNavLayoutter)
     }
 
@@ -94,8 +97,12 @@
     fun noValidLayoutForLargeScreenTaskbarNotPresent() {
         assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
         mockDeviceProfile.isTaskbarPresent = false
-        getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false,
-                        phoneMode = false)
+        getLayoutter(
+            isKidsMode = false,
+            isInSetup = false,
+            isThreeButtonNav = false,
+            phoneMode = false
+        )
     }
 
     @Test
@@ -103,8 +110,12 @@
         assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
         mockDeviceProfile.isTaskbarPresent = false
         val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
-                getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true,
-                        phoneMode = true)
+            getLayoutter(
+                isKidsMode = false,
+                isInSetup = false,
+                isThreeButtonNav = true,
+                phoneMode = true
+            )
         assert(layoutter is PhonePortraitNavLayoutter)
     }
 
@@ -114,8 +125,12 @@
         mockDeviceProfile.isTaskbarPresent = false
         setDeviceProfileLandscape()
         val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
-                getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true,
-                        phoneMode = true)
+            getLayoutter(
+                isKidsMode = false,
+                isInSetup = false,
+                isThreeButtonNav = true,
+                phoneMode = true
+            )
         assert(layoutter is PhoneLandscapeNavLayoutter)
     }
 
@@ -123,8 +138,12 @@
     fun noValidLayoutForPhoneGestureNav() {
         assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
         mockDeviceProfile.isTaskbarPresent = false
-        getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false,
-                phoneMode = true)
+        getLayoutter(
+            isKidsMode = false,
+            isInSetup = false,
+            isThreeButtonNav = false,
+            phoneMode = true
+        )
     }
 
     private fun setDeviceProfileLandscape() {
@@ -134,15 +153,20 @@
         landscapeField.set(mockDeviceProfile, true)
     }
 
-    private fun getLayoutter(isKidsMode: Boolean, isInSetup: Boolean,
-                             isThreeButtonNav: Boolean, phoneMode: Boolean):
-            NavButtonLayoutFactory.NavButtonLayoutter {
+    private fun getLayoutter(
+        isKidsMode: Boolean,
+        isInSetup: Boolean,
+        isThreeButtonNav: Boolean,
+        phoneMode: Boolean
+    ): NavButtonLayoutFactory.NavButtonLayoutter {
         return NavButtonLayoutFactory.getUiLayoutter(
-                deviceProfile = mockDeviceProfile,
-                navButtonsView = mockParentButtonContainer,
-                resources = mockResources,
-                isKidsMode = isKidsMode, isInSetup = isInSetup,
-                isThreeButtonNav = isThreeButtonNav, phoneMode = phoneMode
+            deviceProfile = mockDeviceProfile,
+            navButtonsView = mockParentButtonContainer,
+            resources = mockResources,
+            isKidsMode = isKidsMode,
+            isInSetup = isInSetup,
+            isThreeButtonNav = isThreeButtonNav,
+            phoneMode = phoneMode
         )
     }
-}
\ No newline at end of file
+}
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index 5c2e14f..1129a33 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -46,7 +46,8 @@
             runWithShellPermission(() ->
                     usageStatsManager.registerAppUsageLimitObserver(observerId, packages,
                             Duration.ofSeconds(600), Duration.ofSeconds(300),
-                            PendingIntent.getActivity(mTargetContext, -1, new Intent(),
+                            PendingIntent.getActivity(mTargetContext, -1, new Intent()
+                                            .setPackage(mTargetContext.getPackageName()),
                                     PendingIntent.FLAG_MUTABLE)));
 
             mLauncher.goHome();
diff --git a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
index 5ec935f..9afd893 100644
--- a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
@@ -27,15 +27,13 @@
 import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
 import com.android.wm.shell.util.SplitBounds
 import com.google.common.truth.Truth.assertThat
+import kotlin.math.roundToInt
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
-import kotlin.math.roundToInt
 
-/**
- * Test for FullscreenDrawParams class.
- */
+/** Test for FullscreenDrawParams class. */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class FullscreenDrawParamsTest : DeviceProfileBaseTest() {
@@ -61,15 +59,29 @@
         val currentRotation = 0
         val isRtl = false
 
-        mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
-                canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation,
-                isRtl)
-        params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
-                /* taskViewScale= */ 1.0f,  /* previewWidth= */ 0, dp, mPreviewPositionHelper)
+        mPreviewPositionHelper.updateThumbnailMatrix(
+            previewRect,
+            mThumbnailData,
+            canvasWidth,
+            canvasHeight,
+            dp.widthPx,
+            dp.heightPx,
+            dp.taskbarSize,
+            dp.isTablet,
+            currentRotation,
+            isRtl
+        )
+        params.setProgress(
+            /* fullscreenProgress= */ 1.0f,
+            /* parentScale= */ 1.0f,
+            /* taskViewScale= */ 1.0f,
+            /* previewWidth= */ 0,
+            dp,
+            mPreviewPositionHelper
+        )
 
         val expectedClippedInsets = RectF(0f, 0f, 0f, dp.taskbarSize * TASK_SCALE)
-        assertThat(params.mCurrentDrawnInsets)
-                .isEqualTo(expectedClippedInsets)
+        assertThat(params.mCurrentDrawnInsets).isEqualTo(expectedClippedInsets)
     }
 
     @Test
@@ -83,25 +95,42 @@
         val isRtl = false
         // portrait/vertical split apps
         val dividerSize = 10
-        val splitBounds = SplitBounds(
+        val splitBounds =
+            SplitBounds(
                 Rect(0, 0, dp.widthPx, (dp.heightPx - dividerSize) / 2),
                 Rect(0, (dp.heightPx + dividerSize) / 2, dp.widthPx, dp.heightPx),
-                0 /*lefTopTaskId*/, 0 /*rightBottomTaskId*/)
+                0 /*lefTopTaskId*/,
+                0 /*rightBottomTaskId*/
+            )
         mPreviewPositionHelper.setSplitBounds(splitBounds, STAGE_POSITION_BOTTOM_OR_RIGHT)
 
-        mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
-                canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation,
-                isRtl)
-        params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
-                /* taskViewScale= */ 1.0f,  /* previewWidth= */ 0, dp, mPreviewPositionHelper)
+        mPreviewPositionHelper.updateThumbnailMatrix(
+            previewRect,
+            mThumbnailData,
+            canvasWidth,
+            canvasHeight,
+            dp.widthPx,
+            dp.heightPx,
+            dp.taskbarSize,
+            dp.isTablet,
+            currentRotation,
+            isRtl
+        )
+        params.setProgress(
+            /* fullscreenProgress= */ 1.0f,
+            /* parentScale= */ 1.0f,
+            /* taskViewScale= */ 1.0f,
+            /* previewWidth= */ 0,
+            dp,
+            mPreviewPositionHelper
+        )
 
         // Probably unhelpful, but also unclear how to test otherwise ¯\_(ツ)_/¯
-        val fullscreenTaskHeight = dp.heightPx *
-                (1 - (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent))
+        val fullscreenTaskHeight =
+            dp.heightPx * (1 - (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent))
         val canvasScreenRatio = canvasHeight / fullscreenTaskHeight
         val expectedBottomHint = dp.taskbarSize * canvasScreenRatio
-        assertThat(params.mCurrentDrawnInsets.bottom)
-                .isWithin(1f).of(expectedBottomHint)
+        assertThat(params.mCurrentDrawnInsets.bottom).isWithin(1f).of(expectedBottomHint)
     }
 
     @Test
@@ -115,20 +144,37 @@
         val isRtl = false
         // portrait/vertical split apps
         val dividerSize = 10
-        val splitBounds = SplitBounds(
+        val splitBounds =
+            SplitBounds(
                 Rect(0, 0, dp.widthPx, (dp.heightPx - dividerSize) / 2),
                 Rect(0, (dp.heightPx + dividerSize) / 2, dp.widthPx, dp.heightPx),
-                0 /*lefTopTaskId*/, 0 /*rightBottomTaskId*/)
+                0 /*lefTopTaskId*/,
+                0 /*rightBottomTaskId*/
+            )
         mPreviewPositionHelper.setSplitBounds(splitBounds, STAGE_POSITION_TOP_OR_LEFT)
 
-        mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
-                canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation,
-                isRtl)
-        params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
-                /* taskViewScale= */ 1.0f,  /* previewWidth= */ 0, dp, mPreviewPositionHelper)
+        mPreviewPositionHelper.updateThumbnailMatrix(
+            previewRect,
+            mThumbnailData,
+            canvasWidth,
+            canvasHeight,
+            dp.widthPx,
+            dp.heightPx,
+            dp.taskbarSize,
+            dp.isTablet,
+            currentRotation,
+            isRtl
+        )
+        params.setProgress(
+            /* fullscreenProgress= */ 1.0f,
+            /* parentScale= */ 1.0f,
+            /* taskViewScale= */ 1.0f,
+            /* previewWidth= */ 0,
+            dp,
+            mPreviewPositionHelper
+        )
 
-        assertThat(params.mCurrentDrawnInsets.bottom)
-                .isWithin(1f).of((0f))
+        assertThat(params.mCurrentDrawnInsets.bottom).isWithin(1f).of((0f))
     }
 
     @Test
@@ -142,20 +188,37 @@
         val isRtl = false
         // portrait/vertical split apps
         val dividerSize = 10
-        val splitBounds = SplitBounds(
+        val splitBounds =
+            SplitBounds(
                 Rect(0, 0, (dp.widthPx - dividerSize) / 2, dp.heightPx),
                 Rect((dp.widthPx + dividerSize) / 2, 0, dp.widthPx, dp.heightPx),
-                0 /*lefTopTaskId*/, 0 /*rightBottomTaskId*/)
+                0 /*lefTopTaskId*/,
+                0 /*rightBottomTaskId*/
+            )
         mPreviewPositionHelper.setSplitBounds(splitBounds, STAGE_POSITION_BOTTOM_OR_RIGHT)
 
-        mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
-                canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation,
-                isRtl)
-        params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
-                /* taskViewScale= */ 1.0f,  /* previewWidth= */ 0, dp, mPreviewPositionHelper)
+        mPreviewPositionHelper.updateThumbnailMatrix(
+            previewRect,
+            mThumbnailData,
+            canvasWidth,
+            canvasHeight,
+            dp.widthPx,
+            dp.heightPx,
+            dp.taskbarSize,
+            dp.isTablet,
+            currentRotation,
+            isRtl
+        )
+        params.setProgress(
+            /* fullscreenProgress= */ 1.0f,
+            /* parentScale= */ 1.0f,
+            /* taskViewScale= */ 1.0f,
+            /* previewWidth= */ 0,
+            dp,
+            mPreviewPositionHelper
+        )
 
-        assertThat(params.mCurrentDrawnInsets.bottom)
-                .isWithin(1f).of((dp.taskbarSize * TASK_SCALE))
+        assertThat(params.mCurrentDrawnInsets.bottom).isWithin(1f).of((dp.taskbarSize * TASK_SCALE))
     }
 
     @Test
@@ -168,14 +231,28 @@
         val currentRotation = 0
         val isRtl = false
 
-        mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
-                canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation,
-                isRtl)
-        params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
-                /* taskViewScale= */ 1.0f,  /* previewWidth= */ 0, dp, mPreviewPositionHelper)
+        mPreviewPositionHelper.updateThumbnailMatrix(
+            previewRect,
+            mThumbnailData,
+            canvasWidth,
+            canvasHeight,
+            dp.widthPx,
+            dp.heightPx,
+            dp.taskbarSize,
+            dp.isTablet,
+            currentRotation,
+            isRtl
+        )
+        params.setProgress(
+            /* fullscreenProgress= */ 1.0f,
+            /* parentScale= */ 1.0f,
+            /* taskViewScale= */ 1.0f,
+            /* previewWidth= */ 0,
+            dp,
+            mPreviewPositionHelper
+        )
 
         val expectedClippedInsets = RectF(0f, 0f, 0f, 0f)
-        assertThat(params.mCurrentDrawnInsets)
-                .isEqualTo(expectedClippedInsets)
+        assertThat(params.mCurrentDrawnInsets).isEqualTo(expectedClippedInsets)
     }
-}
\ No newline at end of file
+}
diff --git a/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt b/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt
index 4837c6c..adbca32 100644
--- a/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt
@@ -29,8 +29,8 @@
 class HotseatWidthCalculationTest : DeviceProfileBaseTest() {
 
     /**
-     * This is a case when after setting the hotseat, the space needs to be recalculated
-     * but it doesn't need to change QSB width or remove icons
+     * This is a case when after setting the hotseat, the space needs to be recalculated but it
+     * doesn't need to change QSB width or remove icons
      */
     @Test
     fun distribute_border_space_when_space_is_enough_portrait() {
@@ -51,8 +51,8 @@
     }
 
     /**
-     * This is a case when after setting the hotseat, and recalculating spaces
-     * it still needs to remove icons for everything to fit
+     * This is a case when after setting the hotseat, and recalculating spaces it still needs to
+     * remove icons for everything to fit
      */
     @Test
     fun decrease_num_of_icons_when_not_enough_space_portrait() {
@@ -73,8 +73,8 @@
     }
 
     /**
-     * This is a case when after setting the hotseat, the space needs to be recalculated
-     * but it doesn't need to change QSB width or remove icons
+     * This is a case when after setting the hotseat, the space needs to be recalculated but it
+     * doesn't need to change QSB width or remove icons
      */
     @Test
     fun distribute_border_space_when_space_is_enough_landscape() {
@@ -94,8 +94,8 @@
     }
 
     /**
-     * This is a case when the hotseat spans a certain amount of columns
-     * and the nav buttons push the hotseat to the side, but not enough to change the border space.
+     * This is a case when the hotseat spans a certain amount of columns and the nav buttons push
+     * the hotseat to the side, but not enough to change the border space.
      */
     @Test
     fun nav_buttons_dont_interfere_with_required_hotseat_width() {
@@ -118,9 +118,7 @@
         assertThat(dp.hotseatQsbWidth).isEqualTo(1233)
     }
 
-    /**
-     * This is a case when after setting the hotseat, the QSB width needs to be changed to fit
-     */
+    /** This is a case when after setting the hotseat, the QSB width needs to be changed to fit */
     @Test
     fun decrease_qsb_when_not_enough_space_landscape() {
         initializeVarsForTablet(isGestureMode = false, isLandscape = true)
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 61707df..e71391f 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -176,14 +176,7 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        if (Utilities.ATLEAST_T) {
-            getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
-                    OnBackInvokedDispatcher.PRIORITY_DEFAULT,
-                    () -> {
-                        onBackPressed();
-                        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
-                    });
-        }
+        registerBackDispatcher();
     }
 
     @Override
@@ -246,6 +239,17 @@
 
     }
 
+    protected void registerBackDispatcher() {
+        if (Utilities.ATLEAST_T) {
+            getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+                    OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+                    () -> {
+                        onBackPressed();
+                        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
+                    });
+        }
+    }
+
     public boolean isStarted() {
         return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0;
     }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 25520e1..f124940 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -910,12 +910,24 @@
                     cellHeightPx = cellContentHeight;
                     cellLayoutBorderSpacePx.y -= extraHeightRequired / numBorders;
                 } else {
-                    // If it still doesn't fit, set borderSpace to 0 and distribute the space for
-                    // cellHeight, and reduce iconSize.
+                    // If it still doesn't fit, set borderSpace to 0 to recover space.
                     cellHeightPx = (cellHeightPx * inv.numRows
                             + cellLayoutBorderSpacePx.y * numBorders) / inv.numRows;
-                    iconSizePx = Math.min(iconSizePx, cellHeightPx - cellTextAndPaddingHeight);
                     cellLayoutBorderSpacePx.y = 0;
+                    // Reduce iconDrawablePaddingPx to make cellContentHeight smaller.
+                    int cellContentWithoutPadding = cellContentHeight - iconDrawablePaddingPx;
+                    if (cellContentWithoutPadding <= cellHeightPx) {
+                        iconDrawablePaddingPx = cellContentHeight - cellHeightPx;
+                    } else {
+                        // If it still doesn't fit, set iconDrawablePaddingPx to 0 to recover space,
+                        // then proportional reduce iconSizePx and iconTextSizePx to fit.
+                        iconDrawablePaddingPx = 0;
+                        float ratio = cellHeightPx / (float) cellContentWithoutPadding;
+                        iconSizePx = (int) (iconSizePx * ratio);
+                        iconTextSizePx = (int) (iconTextSizePx * ratio);
+                    }
+                    cellTextAndPaddingHeight =
+                            iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
                 }
                 cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
             }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 43772e4..e9723a5 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -120,6 +120,7 @@
 import android.widget.Toast;
 
 import androidx.annotation.CallSuper;
+import androidx.annotation.FloatRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
@@ -2065,6 +2066,14 @@
         mStateManager.getState().onBackPressed(this);
     }
 
+    protected void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float backProgress) {
+        mStateManager.getState().onBackProgressed(this, backProgress);
+    }
+
+    protected void onBackCancelled() {
+        mStateManager.getState().onBackCancelled(this);
+    }
+
     protected void onScreenOff() {
         // Reset AllApps to its initial state only if we are not in the middle of
         // processing a multi-step drop
@@ -3317,4 +3326,12 @@
             return false; // Return false to continue iterating through all the items.
         });
     }
+
+    /**
+     * Returns {@code true} if there are visible tasks with windowing mode set to
+     * {@link android.app.WindowConfiguration#WINDOWING_MODE_FREEFORM}
+     */
+    public boolean areFreeformTasksVisible() {
+        return false; // Base launcher does not track freeform tasks
+    }
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 20df897..2c6458b 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -47,7 +47,7 @@
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.CacheDataUpdatedTask;
 import com.android.launcher3.model.ItemInstallQueue;
-import com.android.launcher3.model.LoaderResults;
+import com.android.launcher3.model.LauncherBinder;
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelDelegate;
 import com.android.launcher3.model.ModelWriter;
@@ -405,22 +405,22 @@
                     MAIN_EXECUTOR.execute(cb::clearPendingBinds);
                 }
 
-                LoaderResults loaderResults = new LoaderResults(
+                LauncherBinder launcherBinder = new LauncherBinder(
                         mApp, mBgDataModel, mBgAllAppsList, callbacksList);
                 if (bindDirectly) {
                     // Divide the set of loaded items into those that we are binding synchronously,
                     // and everything else that is to be bound normally (asynchronously).
-                    loaderResults.bindWorkspace(bindAllCallbacks);
+                    launcherBinder.bindWorkspace(bindAllCallbacks);
                     // For now, continue posting the binding of AllApps as there are other
                     // issues that arise from that.
-                    loaderResults.bindAllApps();
-                    loaderResults.bindDeepShortcuts();
-                    loaderResults.bindWidgets();
+                    launcherBinder.bindAllApps();
+                    launcherBinder.bindDeepShortcuts();
+                    launcherBinder.bindWidgets();
                     return true;
                 } else {
                     stopLoader();
                     mLoaderTask = new LoaderTask(
-                            mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
+                            mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, launcherBinder);
 
                     // Always post the loader task, instead of running directly
                     // (even on same thread) so that we exit any nested synchronized blocks
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 23ff10a..0d6ed04 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -9,12 +9,17 @@
     fun getPrefs(context: Context): SharedPreferences {
         // Use application context for shared preferences, so that we use a single cached instance
         return context.applicationContext.getSharedPreferences(
-                LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
+            LauncherFiles.SHARED_PREFERENCES_KEY,
+            Context.MODE_PRIVATE
+        )
     }
 
     @JvmStatic
     fun getDevicePrefs(context: Context): SharedPreferences {
         // Use application context for shared preferences, so that we use a single cached instance
         return context.applicationContext.getSharedPreferences(
-                LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE)
-    }}
\ No newline at end of file
+            LauncherFiles.DEVICE_PREFERENCES_KEY,
+            Context.MODE_PRIVATE
+        )
+    }
+}
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 5dddc6f..b9e4c17 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -34,6 +34,8 @@
 import android.graphics.Color;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.FloatRange;
+
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.HintState;
@@ -342,6 +344,27 @@
         }
     }
 
+    /**
+     * Find {@link StateManager} and target {@link LauncherState} to handle back progress in
+     * predictive back gesture.
+     */
+    public void onBackProgressed(
+            Launcher launcher, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
+        StateManager<LauncherState> lsm = launcher.getStateManager();
+        LauncherState toState = lsm.getLastState();
+        lsm.onBackProgressed(toState, backProgress);
+    }
+
+    /**
+     * Find {@link StateManager} and target {@link LauncherState} to handle backProgress in
+     * predictive back gesture.
+     */
+    public void onBackCancelled(Launcher launcher) {
+        StateManager<LauncherState> lsm = launcher.getStateManager();
+        LauncherState toState = lsm.getLastState();
+        lsm.onBackCancelled(toState);
+    }
+
     public static abstract class PageAlphaProvider {
 
         public final Interpolator interpolator;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 483309d..2b9c135 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -62,6 +62,7 @@
 import android.widget.Toast;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
@@ -161,7 +162,9 @@
 
     protected ShortcutAndWidgetContainer mDragSourceInternal;
 
-    @Thunk final IntSparseArrayMap<CellLayout> mWorkspaceScreens = new IntSparseArrayMap<>();
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    @Thunk public final IntSparseArrayMap<CellLayout> mWorkspaceScreens = new IntSparseArrayMap<>();
+
     @Thunk final IntArray mScreenOrder = new IntArray();
 
     @Thunk boolean mDeferRemoveExtraEmptyScreen = false;
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 9930abe..24bfedb 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.allapps;
 
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
@@ -33,11 +34,15 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.FloatRange;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
@@ -61,6 +66,8 @@
         implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
     // This constant should match the second derivative of the animator interpolator.
     public static final float INTERP_COEFF = 1.7f;
+    private static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f;
+    private static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200;
 
     public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
             new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
@@ -139,6 +146,7 @@
     private ActivityAllAppsContainerView<Launcher> mAppsView;
 
     private final Launcher mLauncher;
+    private final AnimatedFloat mAllAppScale = new AnimatedFloat(this::onScaleProgressChanged);
     private boolean mIsVerticalLayout;
 
     // Whether this class should take care of closing the keyboard.
@@ -232,6 +240,52 @@
         onProgressAnimationEnd();
     }
 
+    @Override
+    public void onBackProgressed(
+            LauncherState toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
+        if (!mLauncher.isInState(ALL_APPS) || !NORMAL.equals(toState)) {
+            return;
+        }
+
+        float deceleratedProgress =
+                Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(backProgress);
+        float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE
+                + (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress);
+
+        mAllAppScale.updateValue(scaleProgress);
+    }
+
+    @Override
+    public void onBackCancelled(LauncherState toState) {
+        if (!mLauncher.isInState(ALL_APPS) || !NORMAL.equals(toState)) {
+            return;
+        }
+
+        // TODO: once ag/20649618 is picked into tm-qpr, we don't need to animate back on cancel
+        // swipe because framework will do that for us in {@link #onBackProgressed}.
+        animateAllAppsToNoScale();
+    }
+
+    private void onScaleProgressChanged() {
+        final float scaleProgress = mAllAppScale.value;
+        SCALE_PROPERTY.set(mLauncher.getAppsView(), scaleProgress);
+        mLauncher.getScrimView().setScrimHeaderScale(scaleProgress);
+
+        AllAppsRecyclerView rv = mLauncher.getAppsView().getActiveRecyclerView();
+        if (rv != null && rv.getScrollbar() != null) {
+            rv.getScrollbar().setVisibility(scaleProgress < 1f ? View.INVISIBLE : View.VISIBLE);
+        }
+
+        // TODO(b/264906511): We need to disable view clipping on all apps' parent views so
+        //  that the extra roll of app icons are displayed.
+    }
+
+    private void animateAllAppsToNoScale() {
+        mAllAppScale.animateToValue(1f)
+                .setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS)
+                .start();
+    }
+
     /**
      * Creates an animation which updates the vertical transition progress and updates all the
      * dependent UI using various animation events
@@ -258,6 +312,8 @@
                 if (config.userControlled && success && mShouldControlKeyboard) {
                     mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
                 }
+
+                mAllAppScale.updateValue(1f);
             });
         }
 
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 00e89ba..4878077 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -808,7 +808,7 @@
     }
 
     @Override
-    public void drawOnScrim(Canvas canvas) {
+    public void drawOnScrimWithScale(Canvas canvas, float scale) {
         boolean isTablet = mActivityContext.getDeviceProfile().isTablet;
 
         // Draw full background panel for tablets.
@@ -833,7 +833,9 @@
         if (mHeaderPaint.getColor() == mScrimColor || mHeaderPaint.getColor() == 0) {
             return;
         }
-        int bottom = getHeaderBottom() + getVisibleContainerView().getPaddingTop();
+        final float offset = (getVisibleContainerView().getHeight() * (1 - scale) / 2);
+        final float bottom =
+                scale * (getHeaderBottom() + getVisibleContainerView().getPaddingTop()) + offset;
         FloatingHeaderView headerView = getFloatingHeaderView();
         if (isTablet) {
             // Start adding header protection if search bar or tabs will attach to the top.
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index b55a1e4..e886543 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -56,6 +56,8 @@
 
     public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f);
     public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f);
+    public static final Interpolator PREDICTIVE_BACK_DECELERATED_EASE =
+            new PathInterpolator(0, 0, 0, 1f);
 
     /**
      * The default emphasized interpolator. Used for hero / emphasized movement of content.
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 082f6a1..daf83d4 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -273,6 +273,10 @@
             "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", false,
             "Enable option to replace decorator-based search result backgrounds with drawables");
 
+    public static final BooleanFlag ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION = new DeviceFlag(
+            "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", false,
+            "Enable option to launch search results using the new standardized transitions");
+
     public static final BooleanFlag TWO_PREDICTED_ROWS_ALL_APPS_SEARCH = new DeviceFlag(
             "TWO_PREDICTED_ROWS_ALL_APPS_SEARCH", false,
             "Use 2 rows of app predictions in All Apps search zero-state");
@@ -364,7 +368,7 @@
             "ENABLE_DEVICE_PROFILE_LOGGING", false, "Allows DeviceProfile logging");
 
     public static final BooleanFlag ENABLE_LAUNCH_FROM_STAGED_APP = getDebugFlag(
-            "ENABLE_LAUNCH_FROM_STAGED_APP", false,
+            "ENABLE_LAUNCH_FROM_STAGED_APP", true,
             "Enable the ability to tap a staged app during split select to launch it in full screen"
     );
 
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
similarity index 95%
rename from src/com/android/launcher3/model/BaseLoaderResults.java
rename to src/com/android/launcher3/model/BaseLauncherBinder.java
index 8c6428b..9f8db51 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -47,12 +47,11 @@
 import java.util.concurrent.Executor;
 
 /**
- * Base Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
  */
-public abstract class BaseLoaderResults {
+public abstract class BaseLauncherBinder {
 
-    protected static final String TAG = "LoaderResults";
-    protected static final int INVALID_SCREEN_ID = -1;
+    protected static final String TAG = "LauncherBinder";
     private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
 
     protected final LooperExecutor mUiExecutor;
@@ -65,7 +64,7 @@
 
     private int mMyBindingId;
 
-    public BaseLoaderResults(LauncherAppState app, BgDataModel dataModel,
+    public BaseLauncherBinder(LauncherAppState app, BgDataModel dataModel,
             AllAppsList allAppsList, Callbacks[] callbacksList, LooperExecutor uiExecutor) {
         mUiExecutor = uiExecutor;
         mApp = app;
@@ -101,8 +100,14 @@
         }
     }
 
+    /**
+     * BindDeepShortcuts is abstract because it is a no-op for the go launcher.
+     */
     public abstract void bindDeepShortcuts();
 
+    /**
+     * Binds the all apps results from LoaderTask to the callbacks UX.
+     */
     public void bindAllApps() {
         // shallow copy
         AppInfo[] apps = mBgAllAppsList.copyData();
@@ -110,6 +115,9 @@
         executeCallbacksTask(c -> c.bindAllApplications(apps, flags), mUiExecutor);
     }
 
+    /**
+     * bindWidgets is abstract because it is a no-op for the go launcher.
+     */
     public abstract void bindWidgets();
 
     /**
@@ -160,6 +168,9 @@
         });
     }
 
+    /**
+     * Only used in LoaderTask.
+     */
     public LooperIdleLock newIdleLock(Object lock) {
         LooperIdleLock idleLock = new LooperIdleLock(lock, mUiExecutor.getLooper());
         // If we are not binding or if the main looper is already idle, there is no reason to wait
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 1d6971e..46a6a66 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -125,7 +125,7 @@
 
     private FirstScreenBroadcast mFirstScreenBroadcast;
 
-    private final LoaderResults mResults;
+    private final LauncherBinder mLauncherBinder;
 
     private final LauncherApps mLauncherApps;
     private final UserManager mUserManager;
@@ -145,12 +145,12 @@
     private String mDbName;
 
     public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
-            ModelDelegate modelDelegate, LoaderResults results) {
+            ModelDelegate modelDelegate, LauncherBinder launcherBinder) {
         mApp = app;
         mBgAllAppsList = bgAllAppsList;
         mBgDataModel = dataModel;
         mModelDelegate = modelDelegate;
-        mResults = results;
+        mLauncherBinder = launcherBinder;
 
         mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
         mUserManager = mApp.getContext().getSystemService(UserManager.class);
@@ -163,7 +163,7 @@
         // Wait until the either we're stopped or the other threads are done.
         // This way we don't start loading all apps until the workspace has settled
         // down.
-        LooperIdleLock idleLock = mResults.newIdleLock(this);
+        LooperIdleLock idleLock = mLauncherBinder.newIdleLock(this);
         // Just in case mFlushingWorkerThread changes but we aren't woken up,
         // wait no longer than 1sec at a time
         while (!mStopped && idleLock.awaitLocked(1000));
@@ -221,7 +221,7 @@
             }
 
             verifyNotStopped();
-            mResults.bindWorkspace(true /* incrementBindId */);
+            mLauncherBinder.bindWorkspace(true /* incrementBindId */);
             logASplit(logger, "bindWorkspace");
 
             mModelDelegate.workspaceLoadComplete();
@@ -245,7 +245,7 @@
             logASplit(logger, "loadAllApps");
 
             verifyNotStopped();
-            mResults.bindAllApps();
+            mLauncherBinder.bindAllApps();
             logASplit(logger, "bindAllApps");
 
             verifyNotStopped();
@@ -271,7 +271,7 @@
             logASplit(logger, "loadDeepShortcuts");
 
             verifyNotStopped();
-            mResults.bindDeepShortcuts();
+            mLauncherBinder.bindDeepShortcuts();
             logASplit(logger, "bindDeepShortcuts");
 
             verifyNotStopped();
@@ -290,7 +290,7 @@
             logASplit(logger, "load widgets");
 
             verifyNotStopped();
-            mResults.bindWidgets();
+            mLauncherBinder.bindWidgets();
             logASplit(logger, "bindWidgets");
             verifyNotStopped();
 
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index ad1e7f0..34ac8c2 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -28,6 +28,8 @@
 import android.os.Handler;
 import android.os.Looper;
 
+import androidx.annotation.FloatRange;
+
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
@@ -195,6 +197,21 @@
         }
     }
 
+    /** Handles backProgress in predictive back gesture by passing it to state handlers. */
+    public void onBackProgressed(
+            STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
+        for (StateHandler handler : getStateHandlers()) {
+            handler.onBackProgressed(toState, backProgress);
+        }
+    }
+
+    /** Handles back cancelled event in predictive back gesture by passing it to state handlers. */
+    public void onBackCancelled(STATE_TYPE toState) {
+        for (StateHandler handler : getStateHandlers()) {
+            handler.onBackCancelled(toState);
+        }
+    }
+
     private void goToState(
             STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
         animated &= areAnimatorsEnabled();
@@ -586,6 +603,13 @@
          */
         void setStateWithAnimation(
                 STATE_TYPE toState, StateAnimationConfig config, PendingAnimation animation);
+
+        /** Handles backProgress in predictive back gesture for target state. */
+        default void onBackProgressed(
+                STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {};
+
+        /** Handles back cancelled event in predictive back gesture for target state.  */
+        default void onBackCancelled(STATE_TYPE toState) {};
     }
 
     public interface StateListener<STATE_TYPE> {
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 8f7a4ec..c499e35 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -32,7 +32,6 @@
 
 import android.animation.Animator.AnimatorListener;
 import android.animation.ValueAnimator;
-import android.util.Log;
 import android.view.MotionEvent;
 
 import com.android.launcher3.Launcher;
@@ -292,11 +291,18 @@
                             ? mToState : mFromState;
             // snap to top or bottom using the release velocity
         } else {
-            float successTransitionProgress =
-                    mLauncher.getDeviceProfile().isTablet
-                            && (mToState == ALL_APPS || mFromState == ALL_APPS)
-                            ? TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS
-                            : SUCCESS_TRANSITION_PROGRESS;
+            float successTransitionProgress = SUCCESS_TRANSITION_PROGRESS;
+            if (mLauncher.getDeviceProfile().isTablet
+                    && (mToState == ALL_APPS || mFromState == ALL_APPS)) {
+                successTransitionProgress = TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS;
+            } else if (!mLauncher.getDeviceProfile().isTablet
+                    && mToState == ALL_APPS && mFromState == NORMAL) {
+                successTransitionProgress = AllAppsSwipeController.ALL_APPS_STATE_TRANSITION_MANUAL;
+            } else if (!mLauncher.getDeviceProfile().isTablet
+                    && mToState == NORMAL && mFromState == ALL_APPS) {
+                successTransitionProgress =
+                        1 - AllAppsSwipeController.ALL_APPS_STATE_TRANSITION_MANUAL;
+            }
             targetState =
                     (interpolatedProgress > successTransitionProgress) ? mToState : mFromState;
         }
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 5279dec..bfd0e1b 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -17,7 +17,6 @@
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE;
 import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
 import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE;
 import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
@@ -64,11 +63,14 @@
 
     // ---- Custom interpolators for NORMAL -> ALL_APPS on phones only. ----
 
-    private static final float WORKSPACE_MOTION_START_ATOMIC = 0.1667f;
-    private static final float ALL_APPS_STATE_TRANSITION_ATOMIC = 0.305f;
-    private static final float ALL_APPS_STATE_TRANSITION_MANUAL = 0.4f;
-    private static final float ALL_APPS_FADE_END_ATOMIC = 0.4717f;
+    public static final float ALL_APPS_STATE_TRANSITION_ATOMIC = 0.3333f;
+    public static final float ALL_APPS_STATE_TRANSITION_MANUAL = 0.4f;
+    private static final float ALL_APPS_FADE_END_ATOMIC = 0.8333f;
+    private static final float ALL_APPS_FADE_END_MANUAL = 0.8f;
     private static final float ALL_APPS_FULL_DEPTH_PROGRESS = 0.5f;
+    private static final float SCRIM_FADE_START_ATOMIC = 0.2642f;
+    private static final float SCRIM_FADE_START_MANUAL = 0.117f;
+    private static final float WORKSPACE_MOTION_START_ATOMIC = 0.1667f;
 
     private static final Interpolator LINEAR_EARLY_MANUAL =
             Interpolators.clampToProgress(LINEAR, 0f, ALL_APPS_STATE_TRANSITION_MANUAL);
@@ -98,27 +100,30 @@
     public static final Interpolator HOTSEAT_FADE_ATOMIC = STEP_TRANSITION_ATOMIC;
     public static final Interpolator HOTSEAT_FADE_MANUAL = STEP_TRANSITION_MANUAL;
 
-    public static final Interpolator HOTSEAT_SCALE_ATOMIC = STEP_TRANSITION_ATOMIC;
-    public static final Interpolator HOTSEAT_SCALE_MANUAL = LINEAR_EARLY_MANUAL;
-
-    public static final Interpolator HOTSEAT_TRANSLATE_ATOMIC =
+    public static final Interpolator HOTSEAT_SCALE_ATOMIC =
             Interpolators.clampToProgress(
                     EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START_ATOMIC,
                     ALL_APPS_STATE_TRANSITION_ATOMIC);
+    public static final Interpolator HOTSEAT_SCALE_MANUAL = LINEAR_EARLY_MANUAL;
+
+    public static final Interpolator HOTSEAT_TRANSLATE_ATOMIC = STEP_TRANSITION_ATOMIC;
     public static final Interpolator HOTSEAT_TRANSLATE_MANUAL = STEP_TRANSITION_MANUAL;
 
     public static final Interpolator SCRIM_FADE_ATOMIC =
             Interpolators.clampToProgress(
                     Interpolators.mapToProgress(LINEAR, 0f, 0.8f),
-                    WORKSPACE_MOTION_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC);
-    public static final Interpolator SCRIM_FADE_MANUAL = LINEAR_EARLY_MANUAL;
+                    SCRIM_FADE_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC);
+    public static final Interpolator SCRIM_FADE_MANUAL =
+            Interpolators.clampToProgress(
+                    LINEAR, SCRIM_FADE_START_MANUAL, ALL_APPS_STATE_TRANSITION_MANUAL);
 
     public static final Interpolator ALL_APPS_FADE_ATOMIC =
             Interpolators.clampToProgress(
-                    Interpolators.mapToProgress(DECELERATED_EASE, 0.2f, 1f),
+                    Interpolators.mapToProgress(EMPHASIZED_DECELERATE, 0.2f, 1f),
                     ALL_APPS_STATE_TRANSITION_ATOMIC, ALL_APPS_FADE_END_ATOMIC);
     public static final Interpolator ALL_APPS_FADE_MANUAL =
-            Interpolators.clampToProgress(LINEAR, ALL_APPS_STATE_TRANSITION_MANUAL, 1f);
+            Interpolators.clampToProgress(
+                    LINEAR, ALL_APPS_STATE_TRANSITION_MANUAL, ALL_APPS_FADE_END_MANUAL);
 
     public static final Interpolator ALL_APPS_VERTICAL_PROGRESS_ATOMIC =
             Interpolators.clampToProgress(
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index b7e0105..64951ca 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -23,14 +23,14 @@
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageInstaller.SessionInfo;
-import android.os.Process;
-import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
@@ -66,6 +66,8 @@
 import com.android.launcher3.widget.WidgetManagerHelper;
 
 import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
 
 /**
  * Class for handling clicks on workspace and all-apps items
@@ -156,32 +158,18 @@
 
     private static void onClickPendingAppItem(View v, Launcher launcher, String packageName,
             boolean downloadStarted) {
-        if (downloadStarted) {
-            // If the download has started, simply direct to the market app.
-            startMarketIntentForPackage(v, launcher, packageName);
-            return;
-        }
-        UserHandle user = v.getTag() instanceof ItemInfo
-                ? ((ItemInfo) v.getTag()).user : Process.myUserHandle();
-        new AlertDialog.Builder(launcher)
-                .setTitle(R.string.abandoned_promises_title)
-                .setMessage(R.string.abandoned_promise_explanation)
-                .setPositiveButton(R.string.abandoned_search,
-                        (d, i) -> startMarketIntentForPackage(v, launcher, packageName))
-                .setNeutralButton(R.string.abandoned_clean_this,
-                        (d, i) -> launcher.getWorkspace()
-                                .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages(
-                                        Collections.singleton(packageName), user),
-                                        "user explicitly removes the promise app icon"))
-                .create().show();
-    }
-
-    private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) {
         ItemInfo item = (ItemInfo) v.getTag();
+        CompletableFuture<SessionInfo> siFuture;
         if (Utilities.ATLEAST_Q) {
-            SessionInfo sessionInfo = InstallSessionHelper.INSTANCE.get(launcher)
-                    .getActiveSessionInfo(item.user, packageName);
-            if (sessionInfo != null) {
+            siFuture = CompletableFuture.supplyAsync(() ->
+                    InstallSessionHelper.INSTANCE.get(launcher)
+                            .getActiveSessionInfo(item.user, packageName),
+                    UI_HELPER_EXECUTOR);
+        } else {
+            siFuture = CompletableFuture.completedFuture(null);
+        }
+        Consumer<SessionInfo> marketLaunchAction = sessionInfo -> {
+            if (sessionInfo != null && Utilities.ATLEAST_Q) {
                 LauncherApps launcherApps = launcher.getSystemService(LauncherApps.class);
                 try {
                     launcherApps.startPackageInstallerSessionDetailsActivity(sessionInfo, null,
@@ -191,11 +179,27 @@
                     Log.e(TAG, "Unable to launch market intent for package=" + packageName, e);
                 }
             }
-        }
+            // Fallback to using custom market intent.
+            Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
+            launcher.startActivitySafely(v, intent, item);
+        };
 
-        // Fallback to using custom market intent.
-        Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
-        launcher.startActivitySafely(v, intent, item);
+        if (downloadStarted) {
+            // If the download has started, simply direct to the market app.
+            siFuture.thenAcceptAsync(marketLaunchAction, MAIN_EXECUTOR);
+            return;
+        }
+        new AlertDialog.Builder(launcher)
+                .setTitle(R.string.abandoned_promises_title)
+                .setMessage(R.string.abandoned_promise_explanation)
+                .setPositiveButton(R.string.abandoned_search,
+                        (d, i) -> siFuture.thenAcceptAsync(marketLaunchAction, MAIN_EXECUTOR))
+                .setNeutralButton(R.string.abandoned_clean_this,
+                        (d, i) -> launcher.getWorkspace()
+                                .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages(
+                                        Collections.singleton(packageName), item.user),
+                                        "user explicitly removes the promise app icon"))
+                .create().show();
     }
 
     /**
diff --git a/src/com/android/launcher3/util/DimensionUtils.kt b/src/com/android/launcher3/util/DimensionUtils.kt
index 758b3a9..1922310 100644
--- a/src/com/android/launcher3/util/DimensionUtils.kt
+++ b/src/com/android/launcher3/util/DimensionUtils.kt
@@ -24,12 +24,15 @@
 
 object DimensionUtils {
     /**
-     * Point where x is width, and y is height of taskbar based on provided [deviceProfile]
-     * x or y could also be -1 to indicate there is no dimension specified
+     * Point where x is width, and y is height of taskbar based on provided [deviceProfile] x or y
+     * could also be -1 to indicate there is no dimension specified
      */
     @JvmStatic
-    fun getTaskbarPhoneDimensions(deviceProfile: DeviceProfile, res: Resources,
-                                  isPhoneMode: Boolean): Point {
+    fun getTaskbarPhoneDimensions(
+        deviceProfile: DeviceProfile,
+        res: Resources,
+        isPhoneMode: Boolean
+    ): Point {
         val p = Point()
         // Taskbar for large screen
         if (!isPhoneMode) {
@@ -57,4 +60,4 @@
         p.y = ViewGroup.LayoutParams.MATCH_PARENT
         return p
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt
new file mode 100644
index 0000000..7b49583
--- /dev/null
+++ b/src/com/android/launcher3/util/LockedUserState.kt
@@ -0,0 +1,57 @@
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.Intent
+import android.os.Process
+import android.os.UserManager
+import androidx.annotation.VisibleForTesting
+
+class LockedUserState(private val mContext: Context) : SafeCloseable {
+    var isUserUnlocked: Boolean
+        private set
+    private val mUserUnlockedActions: RunnableList = RunnableList()
+
+    @VisibleForTesting
+    val mUserUnlockedReceiver = SimpleBroadcastReceiver {
+        if (Intent.ACTION_USER_UNLOCKED == it.action) {
+            isUserUnlocked = true
+            notifyUserUnlocked()
+        }
+    }
+
+    init {
+        isUserUnlocked =
+            mContext
+                .getSystemService(UserManager::class.java)!!
+                .isUserUnlocked(Process.myUserHandle())
+        if (isUserUnlocked) {
+            notifyUserUnlocked()
+        } else {
+            mUserUnlockedReceiver.register(mContext, Intent.ACTION_USER_UNLOCKED)
+        }
+    }
+
+    private fun notifyUserUnlocked() {
+        mUserUnlockedActions.executeAllAndDestroy()
+        mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
+    }
+
+    /** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */
+    override fun close() {
+        mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
+    }
+
+    /**
+     * Adds a `Runnable` to be executed when a user is unlocked. If the user is already unlocked,
+     * this runnable will run immediately because RunnableList will already have been destroyed.
+     */
+    fun runOnUserUnlocked(action: Runnable) {
+        mUserUnlockedActions.add(action)
+    }
+
+    companion object {
+        @VisibleForTesting val INSTANCE = MainThreadInitializedObject { LockedUserState(it) }
+
+        @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context)
+    }
+}
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 4c0bfde..870ff12 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -46,6 +46,7 @@
     private int mBackgroundColor;
     private boolean mIsVisible = true;
     private boolean mLastDispatchedOpaqueness;
+    private float mHeaderScale = 1f;
 
     public ScrimView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -91,7 +92,16 @@
     protected void onDraw(Canvas canvas) {
         super.onDraw(canvas);
         if (mDrawingController != null) {
-            mDrawingController.drawOnScrim(canvas);
+            mDrawingController.drawOnScrimWithScale(canvas, mHeaderScale);
+        }
+    }
+
+    /** Set scrim header's scale and bottom offset. */
+    public void setScrimHeaderScale(float scale) {
+        boolean hasChanged = mHeaderScale != scale;
+        mHeaderScale = scale;
+        if (hasChanged) {
+            invalidate();
         }
     }
 
@@ -176,6 +186,6 @@
         /**
          * Called inside ScrimView#OnDraw
          */
-        void drawOnScrim(Canvas canvas);
+        void drawOnScrimWithScale(Canvas canvas, float scale);
     }
 }
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java
similarity index 88%
rename from src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
rename to src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java
index abce2a2..e1a5f24 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java
@@ -27,11 +27,11 @@
 import java.util.List;
 
 /**
- * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
  */
-public class LoaderResults extends BaseLoaderResults {
+public class LauncherBinder extends BaseLauncherBinder {
 
-    public LoaderResults(LauncherAppState app, BgDataModel dataModel,
+    public LauncherBinder(LauncherAppState app, BgDataModel dataModel,
             AllAppsList allAppsList, Callbacks[] callbacks) {
         super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
     }
diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
index 13e56f3..2da5eee 100644
--- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
+++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
@@ -23,11 +23,11 @@
 import com.android.launcher3.DeviceProfile.DEFAULT_PROVIDER
 import com.android.launcher3.util.DisplayController.Info
 import com.android.launcher3.util.WindowBounds
+import java.io.PrintWriter
+import java.io.StringWriter
 import org.junit.Before
 import org.mockito.ArgumentMatchers.any
 import org.mockito.Mockito.mock
-import java.io.PrintWriter
-import java.io.StringWriter
 import org.mockito.Mockito.`when` as whenever
 
 abstract class DeviceProfileBaseTest {
@@ -49,31 +49,36 @@
         isGestureMode = true
     }
 
-    protected fun newDP(): DeviceProfile = DeviceProfile(
-        context,
-        inv,
-        info,
-        windowBounds,
-        SparseArray(),
-        isMultiWindowMode,
-        transposeLayoutWithOrientation,
-        useTwoPanels,
-        isGestureMode,
-        DEFAULT_PROVIDER
-    )
+    protected fun newDP(): DeviceProfile =
+        DeviceProfile(
+            context,
+            inv,
+            info,
+            windowBounds,
+            SparseArray(),
+            isMultiWindowMode,
+            transposeLayoutWithOrientation,
+            useTwoPanels,
+            isGestureMode,
+            DEFAULT_PROVIDER
+        )
 
-    protected fun initializeVarsForPhone(isGestureMode: Boolean = true,
-                                         isVerticalBar: Boolean = false) {
-        val (x, y) = if (isVerticalBar)
-            Pair(2400, 1080)
-        else
-            Pair(1080, 2400)
+    protected fun initializeVarsForPhone(
+        isGestureMode: Boolean = true,
+        isVerticalBar: Boolean = false
+    ) {
+        val (x, y) = if (isVerticalBar) Pair(2400, 1080) else Pair(1080, 2400)
 
-        windowBounds = WindowBounds(Rect(0, 0, x, y), Rect(
-                if (isVerticalBar) 118 else 0,
-                if (isVerticalBar) 74 else 118,
-                if (!isGestureMode && isVerticalBar) 126 else 0,
-                if (isGestureMode) 63 else if (isVerticalBar) 0 else 126))
+        windowBounds =
+            WindowBounds(
+                Rect(0, 0, x, y),
+                Rect(
+                    if (isVerticalBar) 118 else 0,
+                    if (isVerticalBar) 74 else 118,
+                    if (!isGestureMode && isVerticalBar) 126 else 0,
+                    if (isGestureMode) 63 else if (isVerticalBar) 0 else 126
+                )
+            )
 
         whenever(info.isTablet(any())).thenReturn(false)
         whenever(info.getDensityDpi()).thenReturn(420)
@@ -82,79 +87,76 @@
         this.isGestureMode = isGestureMode
         transposeLayoutWithOrientation = true
 
-        inv = InvariantDeviceProfile().apply {
-            numRows = 5
-            numColumns = 4
-            numSearchContainerColumns = 4
+        inv =
+            InvariantDeviceProfile().apply {
+                numRows = 5
+                numColumns = 4
+                numSearchContainerColumns = 4
 
-            iconSize = floatArrayOf(60f, 54f, 60f, 60f)
-            iconTextSize = FloatArray(4) { 14f }
-            deviceType = InvariantDeviceProfile.TYPE_PHONE
+                iconSize = floatArrayOf(60f, 54f, 60f, 60f)
+                iconTextSize = FloatArray(4) { 14f }
+                deviceType = InvariantDeviceProfile.TYPE_PHONE
 
-            minCellSize = listOf(
-                    PointF(80f, 104f),
-                    PointF(80f, 104f),
-                    PointF(80f, 104f),
-                    PointF(80f, 104f)
-            ).toTypedArray()
+                minCellSize =
+                    listOf(
+                            PointF(80f, 104f),
+                            PointF(80f, 104f),
+                            PointF(80f, 104f),
+                            PointF(80f, 104f)
+                        )
+                        .toTypedArray()
 
-            borderSpaces = listOf(
-                    PointF(16f, 16f),
-                    PointF(16f, 16f),
-                    PointF(16f, 16f),
-                    PointF(16f, 16f)
-            ).toTypedArray()
+                borderSpaces =
+                    listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f))
+                        .toTypedArray()
 
-            numFolderRows = 3
-            numFolderColumns = 3
-            folderStyle = R.style.FolderDefaultStyle
+                numFolderRows = 3
+                numFolderColumns = 3
+                folderStyle = R.style.FolderDefaultStyle
 
-            inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split
+                inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split
 
-            horizontalMargin = FloatArray(4) { 22f }
+                horizontalMargin = FloatArray(4) { 22f }
 
-            allAppsCellSize = listOf(
-                    PointF(80f, 104f),
-                    PointF(80f, 104f),
-                    PointF(80f, 104f),
-                    PointF(80f, 104f)
-            ).toTypedArray()
-            allAppsIconSize = floatArrayOf(60f, 60f, 60f, 60f)
-            allAppsIconTextSize = FloatArray(4) { 14f }
-            allAppsBorderSpaces = listOf(
-                    PointF(16f, 16f),
-                    PointF(16f, 16f),
-                    PointF(16f, 16f),
-                    PointF(16f, 16f)
-            ).toTypedArray()
+                allAppsCellSize =
+                    listOf(
+                            PointF(80f, 104f),
+                            PointF(80f, 104f),
+                            PointF(80f, 104f),
+                            PointF(80f, 104f)
+                        )
+                        .toTypedArray()
+                allAppsIconSize = floatArrayOf(60f, 60f, 60f, 60f)
+                allAppsIconTextSize = FloatArray(4) { 14f }
+                allAppsBorderSpaces =
+                    listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f))
+                        .toTypedArray()
 
-            numShownHotseatIcons = 4
+                numShownHotseatIcons = 4
 
-            numDatabaseHotseatIcons = 4
+                numDatabaseHotseatIcons = 4
 
-            hotseatColumnSpan = IntArray(4) { 4 }
-            hotseatBarBottomSpace = FloatArray(4) { 48f }
-            hotseatQsbSpace = FloatArray(4) { 36f }
+                hotseatColumnSpan = IntArray(4) { 4 }
+                hotseatBarBottomSpace = FloatArray(4) { 48f }
+                hotseatQsbSpace = FloatArray(4) { 36f }
 
-            numAllAppsColumns = 4
+                numAllAppsColumns = 4
 
-            isScalable = true
+                isScalable = true
 
-            inlineQsb = BooleanArray(4) { false }
+                inlineQsb = BooleanArray(4) { false }
 
-            devicePaddingId = R.xml.paddings_handhelds
-        }
+                devicePaddingId = R.xml.paddings_handhelds
+            }
     }
 
-    protected fun initializeVarsForTablet(isLandscape: Boolean = false,
-                                          isGestureMode: Boolean = true) {
-        val (x, y) = if (isLandscape)
-            Pair(2560, 1600)
-        else
-            Pair(1600, 2560)
+    protected fun initializeVarsForTablet(
+        isLandscape: Boolean = false,
+        isGestureMode: Boolean = true
+    ) {
+        val (x, y) = if (isLandscape) Pair(2560, 1600) else Pair(1600, 2560)
 
-        windowBounds =
-                WindowBounds(Rect(0, 0, x, y), Rect(0, 104, 0, 0))
+        windowBounds = WindowBounds(Rect(0, 0, x, y), Rect(0, 104, 0, 0))
 
         whenever(info.isTablet(any())).thenReturn(true)
         whenever(info.getDensityDpi()).thenReturn(320)
@@ -163,85 +165,77 @@
         this.isGestureMode = isGestureMode
         useTwoPanels = false
 
-        inv = InvariantDeviceProfile().apply {
-            numRows = 5
-            numColumns = 6
-            numSearchContainerColumns = 3
+        inv =
+            InvariantDeviceProfile().apply {
+                numRows = 5
+                numColumns = 6
+                numSearchContainerColumns = 3
 
-            iconSize = FloatArray(4) { 60f }
-            iconTextSize = FloatArray(4) { 14f }
-            deviceType = InvariantDeviceProfile.TYPE_TABLET
+                iconSize = FloatArray(4) { 60f }
+                iconTextSize = FloatArray(4) { 14f }
+                deviceType = InvariantDeviceProfile.TYPE_TABLET
 
-            minCellSize = listOf(
-                    PointF(102f, 120f),
-                    PointF(120f, 104f),
-                    PointF(102f, 120f),
-                    PointF(102f, 120f)
-            ).toTypedArray()
+                minCellSize =
+                    listOf(
+                            PointF(102f, 120f),
+                            PointF(120f, 104f),
+                            PointF(102f, 120f),
+                            PointF(102f, 120f)
+                        )
+                        .toTypedArray()
 
-            borderSpaces = listOf(
-                    PointF(16f, 64f),
-                    PointF(64f, 16f),
-                    PointF(16f, 64f),
-                    PointF(16f, 64f)
-            ).toTypedArray()
+                borderSpaces =
+                    listOf(PointF(16f, 64f), PointF(64f, 16f), PointF(16f, 64f), PointF(16f, 64f))
+                        .toTypedArray()
 
-            numFolderRows = 3
-            numFolderColumns = 3
-            folderStyle = R.style.FolderDefaultStyle
+                numFolderRows = 3
+                numFolderColumns = 3
+                folderStyle = R.style.FolderDefaultStyle
 
-            inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_6_5
+                inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_6_5
 
-            horizontalMargin = floatArrayOf(54f, 120f, 54f, 54f)
+                horizontalMargin = floatArrayOf(54f, 120f, 54f, 54f)
 
-            allAppsCellSize = listOf(
-                    PointF(96f, 142f),
-                    PointF(126f, 126f),
-                    PointF(96f, 142f),
-                    PointF(96f, 142f)
-            ).toTypedArray()
-            allAppsIconSize = FloatArray(4) { 60f }
-            allAppsIconTextSize = FloatArray(4) { 14f }
-            allAppsBorderSpaces = listOf(
-                    PointF(8f, 16f),
-                    PointF(16f, 16f),
-                    PointF(8f, 16f),
-                    PointF(8f, 16f)
-            ).toTypedArray()
+                allAppsCellSize =
+                    listOf(
+                            PointF(96f, 142f),
+                            PointF(126f, 126f),
+                            PointF(96f, 142f),
+                            PointF(96f, 142f)
+                        )
+                        .toTypedArray()
+                allAppsIconSize = FloatArray(4) { 60f }
+                allAppsIconTextSize = FloatArray(4) { 14f }
+                allAppsBorderSpaces =
+                    listOf(PointF(8f, 16f), PointF(16f, 16f), PointF(8f, 16f), PointF(8f, 16f))
+                        .toTypedArray()
 
-            numShownHotseatIcons = 6
+                numShownHotseatIcons = 6
 
-            numDatabaseHotseatIcons = 6
+                numDatabaseHotseatIcons = 6
 
-            hotseatColumnSpan = intArrayOf(6, 4, 6, 6)
-            hotseatBarBottomSpace = floatArrayOf(36f, 40f, 36f, 36f)
-            hotseatQsbSpace = FloatArray(4) { 32f }
+                hotseatColumnSpan = intArrayOf(6, 4, 6, 6)
+                hotseatBarBottomSpace = floatArrayOf(36f, 40f, 36f, 36f)
+                hotseatQsbSpace = FloatArray(4) { 32f }
 
-            numAllAppsColumns = 6
+                numAllAppsColumns = 6
 
-            isScalable = true
-            devicePaddingId = R.xml.paddings_6x5
+                isScalable = true
+                devicePaddingId = R.xml.paddings_6x5
 
-            inlineQsb = booleanArrayOf(
-                    false,
-                    true,
-                    false,
-                    false
-            )
+                inlineQsb = booleanArrayOf(false, true, false, false)
 
-            devicePaddingId = R.xml.paddings_handhelds
-        }
+                devicePaddingId = R.xml.paddings_handhelds
+            }
     }
 
-    protected fun initializeVarsForTwoPanel(isLandscape: Boolean = false,
-            isGestureMode: Boolean = true) {
-        val (x, y) = if (isLandscape)
-            Pair(2208, 1840)
-        else
-            Pair(1840, 2208)
+    protected fun initializeVarsForTwoPanel(
+        isLandscape: Boolean = false,
+        isGestureMode: Boolean = true
+    ) {
+        val (x, y) = if (isLandscape) Pair(2208, 1840) else Pair(1840, 2208)
 
-        windowBounds = WindowBounds(Rect(0, 0, x, y),
-                Rect(0, 110, 0, 0))
+        windowBounds = WindowBounds(Rect(0, 0, x, y), Rect(0, 110, 0, 0))
 
         whenever(info.isTablet(any())).thenReturn(true)
         whenever(info.getDensityDpi()).thenReturn(420)
@@ -250,74 +244,63 @@
         this.isGestureMode = isGestureMode
         useTwoPanels = true
 
-        inv = InvariantDeviceProfile().apply {
-            numRows = 4
-            numColumns = 4
-            numSearchContainerColumns = 4
+        inv =
+            InvariantDeviceProfile().apply {
+                numRows = 4
+                numColumns = 4
+                numSearchContainerColumns = 4
 
-            iconSize = floatArrayOf(60f, 52f, 52f, 60f)
-            iconTextSize = floatArrayOf(14f, 14f, 12f, 14f)
-            deviceType = InvariantDeviceProfile.TYPE_MULTI_DISPLAY
+                iconSize = floatArrayOf(60f, 52f, 52f, 60f)
+                iconTextSize = floatArrayOf(14f, 14f, 12f, 14f)
+                deviceType = InvariantDeviceProfile.TYPE_MULTI_DISPLAY
 
-            minCellSize = listOf(
-                    PointF(80f, 104f),
-                    PointF(80f, 104f),
-                    PointF(68f, 116f),
-                    PointF(80f, 102f)
-            ).toTypedArray()
+                minCellSize =
+                    listOf(
+                            PointF(80f, 104f),
+                            PointF(80f, 104f),
+                            PointF(68f, 116f),
+                            PointF(80f, 102f)
+                        )
+                        .toTypedArray()
 
-            borderSpaces = listOf(
-                    PointF(16f, 16f),
-                    PointF(16f, 16f),
-                    PointF(16f, 20f),
-                    PointF(20f, 20f)
-            ).toTypedArray()
+                borderSpaces =
+                    listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 20f), PointF(20f, 20f))
+                        .toTypedArray()
 
-            numFolderRows = 3
-            numFolderColumns = 3
-            folderStyle = R.style.FolderDefaultStyle
+                numFolderRows = 3
+                numFolderColumns = 3
+                folderStyle = R.style.FolderDefaultStyle
 
-            inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split
+                inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split
 
-            horizontalMargin = floatArrayOf(21.5f, 21.5f, 22.5f, 30.5f)
+                horizontalMargin = floatArrayOf(21.5f, 21.5f, 22.5f, 30.5f)
 
-            allAppsCellSize = listOf(
-                    PointF(0f, 0f),
-                    PointF(0f, 0f),
-                    PointF(68f, 104f),
-                    PointF(80f, 104f)
-            ).toTypedArray()
-            allAppsIconSize = floatArrayOf(60f, 60f, 52f, 60f)
-            allAppsIconTextSize = floatArrayOf(14f, 14f, 12f, 14f)
-            allAppsBorderSpaces = listOf(
-                    PointF(16f, 16f),
-                    PointF(16f, 16f),
-                    PointF(16f, 28f),
-                    PointF(20f, 16f)
-            ).toTypedArray()
+                allAppsCellSize =
+                    listOf(PointF(0f, 0f), PointF(0f, 0f), PointF(68f, 104f), PointF(80f, 104f))
+                        .toTypedArray()
+                allAppsIconSize = floatArrayOf(60f, 60f, 52f, 60f)
+                allAppsIconTextSize = floatArrayOf(14f, 14f, 12f, 14f)
+                allAppsBorderSpaces =
+                    listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 28f), PointF(20f, 16f))
+                        .toTypedArray()
 
-            numShownHotseatIcons = 6
+                numShownHotseatIcons = 6
 
-            numDatabaseHotseatIcons = 6
+                numDatabaseHotseatIcons = 6
 
-            hotseatColumnSpan = IntArray(4) { 6 }
-            hotseatBarBottomSpace = floatArrayOf(48f, 48f, 36f, 20f)
-            hotseatQsbSpace = floatArrayOf(36f, 36f, 36f, 28f)
+                hotseatColumnSpan = IntArray(4) { 6 }
+                hotseatBarBottomSpace = floatArrayOf(48f, 48f, 36f, 20f)
+                hotseatQsbSpace = floatArrayOf(36f, 36f, 36f, 28f)
 
-            numAllAppsColumns = 6
-            numDatabaseAllAppsColumns = 6
+                numAllAppsColumns = 6
+                numDatabaseAllAppsColumns = 6
 
-            isScalable = true
+                isScalable = true
 
-            inlineQsb = booleanArrayOf(
-                    false,
-                    false,
-                    false,
-                    false
-            )
+                inlineQsb = booleanArrayOf(false, false, false, false)
 
-            devicePaddingId = R.xml.paddings_handhelds
-        }
+                devicePaddingId = R.xml.paddings_handhelds
+            }
     }
 
     fun dump(dp: DeviceProfile): String {
@@ -327,4 +310,4 @@
         printWriter.flush()
         return stringWriter.toString()
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
index a32ce3c..469d79f 100644
--- a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
+++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
@@ -26,6 +26,7 @@
 import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 
 public class CellLayoutBoard {
@@ -140,6 +141,73 @@
         return mWidgetsMap.get(c);
     }
 
+    private void removeWidgetFromBoard(WidgetRect widget) {
+        for (int xi = widget.mBounds.left; xi < widget.mBounds.right; xi++) {
+            for (int yi = widget.mBounds.top; yi < widget.mBounds.bottom; yi++) {
+                mWidget[xi][yi] = '-';
+            }
+        }
+    }
+
+    private void removeOverlappingItems(Rect rect) {
+        // Remove overlapping widgets and remove them from the board
+        mWidgetsRects = mWidgetsRects.stream().filter(widget -> {
+            if (rect.intersect(widget.mBounds)) {
+                removeWidgetFromBoard(widget);
+                return false;
+            }
+            return true;
+        }).collect(Collectors.toList());
+        // Remove overlapping icons and remove them from the board
+        mIconPoints = mIconPoints.stream().filter(iconPoint -> {
+            int x = iconPoint.coord.x;
+            int y = iconPoint.coord.y;
+            if (rect.contains(x, y)) {
+                mWidget[x][y] = '-';
+                return false;
+            }
+            return true;
+        }).collect(Collectors.toList());
+    }
+
+    private void removeOverlappingItems(Point p) {
+        // Remove overlapping widgets and remove them from the board
+        mWidgetsRects = mWidgetsRects.stream().filter(widget -> {
+            if (widget.mBounds.contains(p.x, p.y)) {
+                removeWidgetFromBoard(widget);
+                return false;
+            }
+            return true;
+        }).collect(Collectors.toList());
+        // Remove overlapping icons and remove them from the board
+        mIconPoints = mIconPoints.stream().filter(iconPoint -> {
+            int x = iconPoint.coord.x;
+            int y = iconPoint.coord.y;
+            if (p.x == x && p.y == y) {
+                mWidget[x][y] = '-';
+                return false;
+            }
+            return true;
+        }).collect(Collectors.toList());
+    }
+
+    public void addWidget(int x, int y, int spanX, int spanY, char type) {
+        Rect rect = new Rect(x, y + spanY - 1, x + spanX - 1, y);
+        removeOverlappingItems(rect);
+        WidgetRect widgetRect = new WidgetRect(type, rect);
+        mWidgetsRects.add(widgetRect);
+        for (int xi = rect.left; xi < rect.right + 1; xi++) {
+            for (int yi = rect.bottom; yi < rect.top + 1; yi++) {
+                mWidget[xi][yi] = type;
+            }
+        }
+    }
+
+    public void addIcon(int x, int y) {
+        removeOverlappingItems(new Point(x, y));
+        mWidget[x][y] = 'i';
+    }
+
     public static WidgetRect getWidgetRect(int x, int y, Set<Point> used, char[][] board) {
         char type = board[x][y];
         Queue<Point> search = new ArrayDeque<Point>();
@@ -227,4 +295,17 @@
         board.mIconPoints = getIconPoints(board.mWidget);
         return board;
     }
+
+    public String toString(int maxX, int maxY) {
+        StringBuilder s = new StringBuilder();
+        maxX = Math.min(maxX, mWidget.length);
+        maxY = Math.min(maxY, mWidget[0].length);
+        for (int y = 0; y < maxY; y++) {
+            for (int x = 0; x < maxX; x++) {
+                s.append(mWidget[x][y]);
+            }
+            s.append('\n');
+        }
+        return s.toString();
+    }
 }
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
index 9da7e0f..843f011 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
@@ -26,6 +26,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.launcher3.CellLayout;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.celllayout.testcases.FullReorderCase;
 import com.android.launcher3.celllayout.testcases.MoveOutReorderCase;
 import com.android.launcher3.celllayout.testcases.PushReorderCase;
@@ -46,6 +47,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
@@ -108,6 +110,42 @@
         return match;
     }
 
+    private void printCurrentWorkspace() {
+        InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
+        ArrayList<CellLayoutBoard> boards = workspaceToBoards();
+        for (int i = 0; i < boards.size(); i++) {
+            Log.d(TAG, "Screen number " + i);
+            Log.d(TAG, ".\n" + boards.get(i).toString(idp.numColumns, idp.numRows));
+        }
+    }
+
+    private ArrayList<CellLayoutBoard> workspaceToBoards() {
+        return getFromLauncher(l -> {
+            ArrayList<CellLayoutBoard> boards = new ArrayList<>();
+            int widgetCount = 0;
+            for (CellLayout cellLayout : l.getWorkspace().mWorkspaceScreens) {
+                CellLayoutBoard board = new CellLayoutBoard();
+                int count = cellLayout.getShortcutsAndWidgets().getChildCount();
+                for (int i = 0; i < count; i++) {
+                    View callView = cellLayout.getShortcutsAndWidgets().getChildAt(i);
+                    CellLayoutLayoutParams params =
+                            (CellLayoutLayoutParams) callView.getLayoutParams();
+                    // is icon
+                    if (callView instanceof DoubleShadowBubbleTextView) {
+                        board.addIcon(params.cellX, params.cellY);
+                    } else {
+                        // is widget
+                        board.addWidget(params.cellX, params.cellY, params.cellHSpan,
+                                params.cellVSpan, (char) ('A' + widgetCount));
+                        widgetCount++;
+                    }
+                }
+                boards.add(board);
+            }
+            return boards;
+        });
+    }
+
     private void runTestCase(ReorderTestCase testCase)
             throws ExecutionException, InterruptedException {
         Point mainWidgetCellPos = testCase.mStart.getMain();
@@ -127,6 +165,7 @@
         for (CellLayoutBoard board : testCase.mEnd) {
             isValid |= validateBoard(board);
         }
+        printCurrentWorkspace();
         assertTrue("Non of the valid boards match with the current state", isValid);
     }
 
diff --git a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt
index d26381d..03352fe 100644
--- a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt
+++ b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt
@@ -30,9 +30,7 @@
 import com.android.launcher3.util.LauncherModelHelper
 import java.util.UUID
 
-/**
- * Base class for workspace related tests.
- */
+/** Base class for workspace related tests. */
 abstract class AbstractWorkspaceModelTest {
     companion object {
         val emptyScreenSpaces = listOf(Rect(0, 0, 5, 5))
@@ -64,10 +62,7 @@
         mModelHelper.destroy()
     }
 
-
-    /**
-     * Sets up workspaces with the given screen IDs with some items and a 2x2 space.
-     */
+    /** Sets up workspaces with the given screen IDs with some items and a 2x2 space. */
     fun setupWorkspaces(screenIdsWithItems: List<Int>) {
         var nextItemId = 1
         screenIdsWithItems.forEach { screenId ->
@@ -83,8 +78,7 @@
         screen1: List<Rect>? = null,
         screen2: List<Rect>? = null,
         screen3: List<Rect>? = null,
-    ) = listOf(screen0, screen1, screen2, screen3)
-        .let(this::setupWithSpaces)
+    ) = listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces)
 
     private fun setupWithSpaces(workspaceSpaces: List<List<Rect>?>) {
         var nextItemId = 1
@@ -110,9 +104,7 @@
         var itemId = itemStartId
         val occupancy = GridOccupancy(mIdp.numColumns, mIdp.numRows)
         occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true)
-        spaces.forEach { spaceRect ->
-            occupancy.markCells(spaceRect, false)
-        }
+        spaces.forEach { spaceRect -> occupancy.markCells(spaceRect, false) }
         mExistingScreens.add(screenId)
         mScreenOccupancy.append(screenId, occupancy)
         for (x in 0 until mIdp.numColumns) {
@@ -139,24 +131,21 @@
         return itemId
     }
 
-    fun getExistingItem() = WorkspaceItemInfo()
-        .apply { intent = Intent().setComponent(ComponentName("a", "b")) }
+    fun getExistingItem() =
+        WorkspaceItemInfo().apply { intent = Intent().setComponent(ComponentName("a", "b")) }
 
     fun getNewItem(): WorkspaceItemInfo {
         val itemPackage = UUID.randomUUID().toString()
-        return WorkspaceItemInfo()
-            .apply { intent = Intent().setComponent(ComponentName(itemPackage, itemPackage)) }
+        return WorkspaceItemInfo().apply {
+            intent = Intent().setComponent(ComponentName(itemPackage, itemPackage))
+        }
     }
 }
 
-data class NewItemSpace(
-    val screenId: Int,
-    val cellX: Int,
-    val cellY: Int
-) {
+data class NewItemSpace(val screenId: Int, val cellX: Int, val cellY: Int) {
     fun toIntArray() = intArrayOf(screenId, cellX, cellY)
 
     companion object {
         fun fromIntArray(array: kotlin.IntArray) = NewItemSpace(array[0], array[1], array[2])
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
index 65d938b..749ffcf 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
@@ -23,9 +23,9 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo
 import com.android.launcher3.util.Executors
 import com.android.launcher3.util.IntArray
-import com.android.launcher3.util.same
-import com.android.launcher3.util.eq
 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
@@ -34,31 +34,24 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.times
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
 
-/**
- * Tests for [AddWorkspaceItemsTask]
- */
+/** Tests for [AddWorkspaceItemsTask] */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
 
-    @Captor
-    private lateinit var mAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
+    @Captor private lateinit var mAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
 
-    @Captor
-    private lateinit var mNotAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
+    @Captor private lateinit var mNotAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
 
-    @Mock
-    private lateinit var mDataModelCallbacks: BgDataModel.Callbacks
+    @Mock private lateinit var mDataModelCallbacks: BgDataModel.Callbacks
 
-    @Mock
-    private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder
-
+    @Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder
 
     @Before
     override fun setup() {
@@ -89,10 +82,7 @@
 
     @Test
     fun givenNewAndExistingItems_whenExecuteTask_thenOnlyAddNewItem() {
-        val itemsToAdd = arrayOf(
-            getNewItem(),
-            getExistingItem()
-        )
+        val itemsToAdd = arrayOf(getNewItem(), getExistingItem())
         givenNewItemSpaces(NewItemSpace(1, 0, 0))
         val nonEmptyScreenIds = listOf(0)
 
@@ -132,13 +122,14 @@
 
     @Test
     fun givenMultipleItems_whenExecuteTask_thenAddThem() {
-        val itemsToAdd = arrayOf(
-            getNewItem(),
-            getExistingItem(),
-            getNewItem(),
-            getNewItem(),
-            getExistingItem(),
-        )
+        val itemsToAdd =
+            arrayOf(
+                getNewItem(),
+                getExistingItem(),
+                getNewItem(),
+                getNewItem(),
+                getExistingItem(),
+            )
         givenNewItemSpaces(
             NewItemSpace(1, 3, 3),
             NewItemSpace(2, 0, 0),
@@ -159,27 +150,16 @@
         // Items that are added to the second screen should be animated
         val itemsAddedToSecondScreen = addedItems.filter { it.itemInfo.screenId == 2 }
         assertThat(itemsAddedToSecondScreen.size).isEqualTo(2)
-        itemsAddedToSecondScreen.forEach {
-            assertThat(it.isAnimated).isTrue()
-        }
+        itemsAddedToSecondScreen.forEach { assertThat(it.isAnimated).isTrue() }
         verifyItemSpaceFinderCall(nonEmptyScreenIds, numberOfExpectedCall = 3)
     }
 
-    /**
-     * Sets up the item space data that will be returned from WorkspaceItemSpaceFinder.
-     */
+    /** Sets up the item space data that will be returned from WorkspaceItemSpaceFinder. */
     private fun givenNewItemSpaces(vararg newItemSpaces: NewItemSpace) {
         val spaceStack = newItemSpaces.toMutableList()
         whenever(
-            mWorkspaceItemSpaceFinder.findSpaceForItem(
-                any(),
-                any(),
-                any(),
-                any(),
-                any(),
-                any()
+                mWorkspaceItemSpaceFinder.findSpaceForItem(any(), any(), any(), any(), any(), any())
             )
-        )
             .then { spaceStack.removeFirst().toIntArray() }
     }
 
@@ -187,20 +167,21 @@
      * Verifies if WorkspaceItemSpaceFinder was called with proper arguments and how many times was
      * it called.
      */
-    private fun verifyItemSpaceFinderCall(
-        nonEmptyScreenIds: List<Int>,
-        numberOfExpectedCall: Int
-    ) {
+    private fun verifyItemSpaceFinderCall(nonEmptyScreenIds: List<Int>, numberOfExpectedCall: Int) {
         verify(mWorkspaceItemSpaceFinder, times(numberOfExpectedCall))
             .findSpaceForItem(
-                same(mAppState), same(mModelHelper.bgDataModel),
-                eq(IntArray.wrap(*nonEmptyScreenIds.toIntArray())), eq(IntArray()), eq(1), eq(1)
+                same(mAppState),
+                same(mModelHelper.bgDataModel),
+                eq(IntArray.wrap(*nonEmptyScreenIds.toIntArray())),
+                eq(IntArray()),
+                eq(1),
+                eq(1)
             )
     }
 
     /**
-     * Sets up the workspaces with items, executes the task, collects the added items from the
-     * model callback then returns it.
+     * Sets up the workspaces with items, executes the task, collects the added items from the model
+     * callback then returns it.
      */
     private fun testAddItems(
         nonEmptyScreenIds: List<Int>,
@@ -209,21 +190,21 @@
         setupWorkspaces(nonEmptyScreenIds)
         val task = newTask(*itemsToAdd)
         var updateCount = 0
-        mModelHelper.executeTaskForTest(task)
-            .forEach {
-                updateCount++
-                it.run()
-            }
+        mModelHelper.executeTaskForTest(task).forEach {
+            updateCount++
+            it.run()
+        }
 
         val addedItems = mutableListOf<AddedItem>()
         if (updateCount > 0) {
-            verify(mDataModelCallbacks).bindAppsAdded(
-                any(),
-                mNotAnimatedItemArgumentCaptor.capture(), mAnimatedItemArgumentCaptor.capture()
-            )
+            verify(mDataModelCallbacks)
+                .bindAppsAdded(
+                    any(),
+                    mNotAnimatedItemArgumentCaptor.capture(),
+                    mAnimatedItemArgumentCaptor.capture()
+                )
             addedItems.addAll(mAnimatedItemArgumentCaptor.value.map { AddedItem(it, true) })
             addedItems.addAll(mNotAnimatedItemArgumentCaptor.value.map { AddedItem(it, false) })
-
         }
 
         return addedItems
@@ -234,12 +215,10 @@
      * with a mock.
      */
     private fun newTask(vararg items: ItemInfo): AddWorkspaceItemsTask =
-        items.map { Pair.create(it, Any()) }
+        items
+            .map { Pair.create(it, Any()) }
             .toMutableList()
             .let { AddWorkspaceItemsTask(it, mWorkspaceItemSpaceFinder) }
 }
 
-private data class AddedItem(
-    val itemInfo: ItemInfo,
-    val isAnimated: Boolean
-)
+private data class AddedItem(val itemInfo: ItemInfo, val isAnimated: Boolean)
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
index 76a186b..dcc8ec7 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
@@ -17,7 +17,7 @@
 
 import android.content.Context
 import android.content.Intent
-import android.database.Cursor;
+import android.database.Cursor
 import android.database.sqlite.SQLiteDatabase
 import android.graphics.Point
 import android.os.Process
@@ -38,7 +38,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-/** Unit tests for [GridSizeMigrationUtil]  */
+/** Unit tests for [GridSizeMigrationUtil] */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class GridSizeMigrationUtilTest {
@@ -64,19 +64,20 @@
         context = modelHelper.sandboxContext
         db = modelHelper.provider.db
 
-        validPackages = setOf(
-            TEST_PACKAGE,
-            testPackage1,
-            testPackage2,
-            testPackage3,
-            testPackage4,
-            testPackage5,
-            testPackage6,
-            testPackage7,
-            testPackage8,
-            testPackage9,
-            testPackage10
-        )
+        validPackages =
+            setOf(
+                TEST_PACKAGE,
+                testPackage1,
+                testPackage2,
+                testPackage3,
+                testPackage4,
+                testPackage5,
+                testPackage6,
+                testPackage7,
+                testPackage8,
+                testPackage9,
+                testPackage10
+            )
 
         idp = InvariantDeviceProfile.INSTANCE[context]
         val userSerial = UserCache.INSTANCE[context].getSerialNumberForUser(Process.myUserHandle())
@@ -90,8 +91,8 @@
     }
 
     /**
-     * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is
-     * not needed anymore
+     * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is not
+     * needed anymore
      */
     @Test
     @Throws(Exception::class)
@@ -124,25 +125,27 @@
         val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
         val destReader = DbReader(db, TABLE_NAME, context, validPackages)
         GridSizeMigrationUtil.migrate(
-                context,
-                db,
-                srcReader,
-                destReader,
-                idp.numDatabaseHotseatIcons,
-                Point(idp.numColumns, idp.numRows),
-                DeviceGridState(context),
-                DeviceGridState(idp)
+            context,
+            db,
+            srcReader,
+            destReader,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows),
+            DeviceGridState(context),
+            DeviceGridState(idp)
         )
 
         // Check hotseat items
-        var c = context.contentResolver.query(
-            CONTENT_URI,
-            arrayOf(SCREEN, INTENT),
-            "container=$CONTAINER_HOTSEAT",
-            null,
-            SCREEN,
-            null
-        ) ?: throw IllegalStateException()
+        var c =
+            context.contentResolver.query(
+                CONTENT_URI,
+                arrayOf(SCREEN, INTENT),
+                "container=$CONTAINER_HOTSEAT",
+                null,
+                SCREEN,
+                null
+            )
+                ?: throw IllegalStateException()
 
         assertThat(c.count).isEqualTo(idp.numDatabaseHotseatIcons)
 
@@ -163,14 +166,16 @@
         c.close()
 
         // Check workspace items
-        c = context.contentResolver.query(
-            CONTENT_URI,
-            arrayOf(CELLX, CELLY, INTENT),
-            "container=$CONTAINER_DESKTOP",
-            null,
-            null,
-            null
-        ) ?: throw IllegalStateException()
+        c =
+            context.contentResolver.query(
+                CONTENT_URI,
+                arrayOf(CELLX, CELLY, INTENT),
+                "container=$CONTAINER_DESKTOP",
+                null,
+                null,
+                null
+            )
+                ?: throw IllegalStateException()
 
         intentIndex = c.getColumnIndex(INTENT)
         val cellXIndex = c.getColumnIndex(CELLX)
@@ -195,8 +200,8 @@
     }
 
     /**
-     * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is
-     * not needed anymore
+     * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is not
+     * needed anymore
      */
     @Test
     @Throws(Exception::class)
@@ -235,39 +240,46 @@
         val readerGridB = DbReader(db, TABLE_NAME, context, validPackages)
         // migrate from A -> B
         GridSizeMigrationUtil.migrate(
-                context,
-                db,
-                readerGridA,
-                readerGridB,
-                idp.numDatabaseHotseatIcons,
-                Point(idp.numColumns, idp.numRows),
-                DeviceGridState(context),
-                DeviceGridState(idp)
+            context,
+            db,
+            readerGridA,
+            readerGridB,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows),
+            DeviceGridState(context),
+            DeviceGridState(idp)
         )
 
         // Check hotseat items in grid B
-        var c = context.contentResolver.query(
+        var c =
+            context.contentResolver.query(
                 CONTENT_URI,
                 arrayOf(SCREEN, INTENT),
                 "container=$CONTAINER_HOTSEAT",
                 null,
                 SCREEN,
                 null
-        ) ?: throw IllegalStateException()
+            )
+                ?: throw IllegalStateException()
         // Expected hotseat items in grid B
         // 2 1 3 4
-        verifyHotseat(c, idp,
-                mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList())
+        verifyHotseat(
+            c,
+            idp,
+            mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList()
+        )
 
         // Check workspace items in grid B
-        c = context.contentResolver.query(
+        c =
+            context.contentResolver.query(
                 CONTENT_URI,
                 arrayOf(SCREEN, CELLX, CELLY, INTENT),
                 "container=$CONTAINER_DESKTOP",
                 null,
                 null,
                 null
-        ) ?: throw IllegalStateException()
+            )
+                ?: throw IllegalStateException()
         var locMap = parseLocMap(context, c)
         // Expected items in grid B
         // _ _ _ _
@@ -285,38 +297,45 @@
 
         // migrate from B -> A
         GridSizeMigrationUtil.migrate(
-                context,
-                db,
-                readerGridB,
-                readerGridA,
-                5,
-                Point(5, 5),
-                DeviceGridState(idp),
-                DeviceGridState(context)
+            context,
+            db,
+            readerGridB,
+            readerGridA,
+            5,
+            Point(5, 5),
+            DeviceGridState(idp),
+            DeviceGridState(context)
         )
         // Check hotseat items in grid A
-        c = context.contentResolver.query(
+        c =
+            context.contentResolver.query(
                 TMP_CONTENT_URI,
                 arrayOf(SCREEN, INTENT),
                 "container=$CONTAINER_HOTSEAT",
                 null,
                 SCREEN,
                 null
-        ) ?: throw IllegalStateException()
+            )
+                ?: throw IllegalStateException()
         // Expected hotseat items in grid A
         // 1 2 _ 3 4
-        verifyHotseat(c, idp, mutableListOf(
-                testPackage1, testPackage2, null, testPackage3, testPackage4).toList())
+        verifyHotseat(
+            c,
+            idp,
+            mutableListOf(testPackage1, testPackage2, null, testPackage3, testPackage4).toList()
+        )
 
         // Check workspace items in grid A
-        c = context.contentResolver.query(
+        c =
+            context.contentResolver.query(
                 TMP_CONTENT_URI,
                 arrayOf(SCREEN, CELLX, CELLY, INTENT),
                 "container=$CONTAINER_DESKTOP",
                 null,
                 null,
                 null
-        ) ?: throw IllegalStateException()
+            )
+                ?: throw IllegalStateException()
         locMap = parseLocMap(context, c)
         // Expected workspace items in grid A
         // _ _ _ _ _
@@ -338,39 +357,46 @@
 
         // migrate from A -> B
         GridSizeMigrationUtil.migrate(
-                context,
-                db,
-                readerGridA,
-                readerGridB,
-                idp.numDatabaseHotseatIcons,
-                Point(idp.numColumns, idp.numRows),
-                DeviceGridState(context),
-                DeviceGridState(idp)
+            context,
+            db,
+            readerGridA,
+            readerGridB,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows),
+            DeviceGridState(context),
+            DeviceGridState(idp)
         )
 
         // Check hotseat items in grid B
-        c = context.contentResolver.query(
+        c =
+            context.contentResolver.query(
                 CONTENT_URI,
                 arrayOf(SCREEN, INTENT),
                 "container=$CONTAINER_HOTSEAT",
                 null,
                 SCREEN,
                 null
-        ) ?: throw IllegalStateException()
+            )
+                ?: throw IllegalStateException()
         // Expected hotseat items in grid B
         // 2 1 3 4
-        verifyHotseat(c, idp,
-                mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList())
+        verifyHotseat(
+            c,
+            idp,
+            mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList()
+        )
 
         // Check workspace items in grid B
-        c = context.contentResolver.query(
+        c =
+            context.contentResolver.query(
                 CONTENT_URI,
                 arrayOf(SCREEN, CELLX, CELLY, INTENT),
                 "container=$CONTAINER_DESKTOP",
                 null,
                 null,
                 null
-        ) ?: throw IllegalStateException()
+            )
+                ?: throw IllegalStateException()
         locMap = parseLocMap(context, c)
         // Expected workspace items in grid B
         // _ _ _ _
@@ -406,7 +432,7 @@
         val locMap = mutableMapOf<String, Triple<Int, Int, Int>>()
         while (c.moveToNext()) {
             locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
-                    Triple(c.getInt(screenIndex), c.getInt(cellXIndex), c.getInt(cellYIndex))
+                Triple(c.getInt(screenIndex), c.getInt(cellXIndex), c.getInt(cellYIndex))
         }
         c.close()
         return locMap.toMap()
@@ -414,12 +440,13 @@
 
     @Test
     fun migrateToLargerHotseat() {
-        val srcHotseatItems = intArrayOf(
-            modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
-            modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
-            modelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
-            modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
-        )
+        val srcHotseatItems =
+            intArrayOf(
+                modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
+                modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
+                modelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
+                modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+            )
         val numSrcDatabaseHotseatIcons = srcHotseatItems.size
         idp.numDatabaseHotseatIcons = 6
         idp.numColumns = 4
@@ -427,25 +454,27 @@
         val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
         val destReader = DbReader(db, TABLE_NAME, context, validPackages)
         GridSizeMigrationUtil.migrate(
-                context,
-                db,
-                srcReader,
-                destReader,
-                idp.numDatabaseHotseatIcons,
-                Point(idp.numColumns, idp.numRows),
-                DeviceGridState(context),
-                DeviceGridState(idp)
+            context,
+            db,
+            srcReader,
+            destReader,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows),
+            DeviceGridState(context),
+            DeviceGridState(idp)
         )
 
         // Check hotseat items
-        val c = context.contentResolver.query(
-            CONTENT_URI,
-            arrayOf(SCREEN, INTENT),
-            "container=$CONTAINER_HOTSEAT",
-            null,
-            SCREEN,
-            null
-        ) ?: throw IllegalStateException()
+        val c =
+            context.contentResolver.query(
+                CONTENT_URI,
+                arrayOf(SCREEN, INTENT),
+                "container=$CONTAINER_HOTSEAT",
+                null,
+                SCREEN,
+                null
+            )
+                ?: throw IllegalStateException()
 
         assertThat(c.count.toLong()).isEqualTo(numSrcDatabaseHotseatIcons.toLong())
         val screenIndex = c.getColumnIndex(SCREEN)
@@ -483,25 +512,27 @@
         val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
         val destReader = DbReader(db, TABLE_NAME, context, validPackages)
         GridSizeMigrationUtil.migrate(
-                context,
-                db,
-                srcReader,
-                destReader,
-                idp.numDatabaseHotseatIcons,
-                Point(idp.numColumns, idp.numRows),
-                DeviceGridState(context),
-                DeviceGridState(idp)
+            context,
+            db,
+            srcReader,
+            destReader,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows),
+            DeviceGridState(context),
+            DeviceGridState(idp)
         )
 
         // Check hotseat items
-        val c = context.contentResolver.query(
-            CONTENT_URI,
-            arrayOf(SCREEN, INTENT),
-            "container=$CONTAINER_HOTSEAT",
-            null,
-            SCREEN,
-            null
-        ) ?: throw IllegalStateException()
+        val c =
+            context.contentResolver.query(
+                CONTENT_URI,
+                arrayOf(SCREEN, INTENT),
+                "container=$CONTAINER_HOTSEAT",
+                null,
+                SCREEN,
+                null
+            )
+                ?: throw IllegalStateException()
 
         assertThat(c.count.toLong()).isEqualTo(idp.numDatabaseHotseatIcons.toLong())
         val screenIndex = c.getColumnIndex(SCREEN)
@@ -527,8 +558,8 @@
     }
 
     /**
-     * Migrating from a smaller grid to a large one should keep the pages
-     * if the column difference is less than 2
+     * Migrating from a smaller grid to a large one should keep the pages if the column difference
+     * is less than 2
      */
     @Test
     @Throws(Exception::class)
@@ -549,25 +580,27 @@
         val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
         val destReader = DbReader(db, TABLE_NAME, context, validPackages)
         GridSizeMigrationUtil.migrate(
-                context,
-                db,
-                srcReader,
-                destReader,
-                idp.numDatabaseHotseatIcons,
-                Point(idp.numColumns, idp.numRows),
-                DeviceGridState(context),
-                DeviceGridState(idp)
+            context,
+            db,
+            srcReader,
+            destReader,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows),
+            DeviceGridState(context),
+            DeviceGridState(idp)
         )
 
         // Get workspace items
-        val c = context.contentResolver.query(
-            CONTENT_URI,
-            arrayOf(INTENT, SCREEN),
-            "container=$CONTAINER_DESKTOP",
-            null,
-            null,
-            null
-        ) ?: throw IllegalStateException()
+        val c =
+            context.contentResolver.query(
+                CONTENT_URI,
+                arrayOf(INTENT, SCREEN),
+                "container=$CONTAINER_DESKTOP",
+                null,
+                null,
+                null
+            )
+                ?: throw IllegalStateException()
         val intentIndex = c.getColumnIndex(INTENT)
         val screenIndex = c.getColumnIndex(SCREEN)
 
@@ -589,8 +622,8 @@
     }
 
     /**
-     * Migrating from a smaller grid to a large one should reflow the pages
-     * if the column difference is more than 2
+     * Migrating from a smaller grid to a large one should reflow the pages if the column difference
+     * is more than 2
      */
     @Test
     @Throws(Exception::class)
@@ -610,25 +643,27 @@
         val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
         val destReader = DbReader(db, TABLE_NAME, context, validPackages)
         GridSizeMigrationUtil.migrate(
-                context,
-                db,
-                srcReader,
-                destReader,
-                idp.numDatabaseHotseatIcons,
-                Point(idp.numColumns, idp.numRows),
-                DeviceGridState(context),
-                DeviceGridState(idp)
+            context,
+            db,
+            srcReader,
+            destReader,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows),
+            DeviceGridState(context),
+            DeviceGridState(idp)
         )
 
         // Get workspace items
-        val c = context.contentResolver.query(
-            CONTENT_URI,
-            arrayOf(INTENT, SCREEN),
-            "container=$CONTAINER_DESKTOP",
-            null,
-            null,
-            null
-        ) ?: throw IllegalStateException()
+        val c =
+            context.contentResolver.query(
+                CONTENT_URI,
+                arrayOf(INTENT, SCREEN),
+                "container=$CONTAINER_DESKTOP",
+                null,
+                null,
+                null
+            )
+                ?: throw IllegalStateException()
 
         val intentIndex = c.getColumnIndex(INTENT)
         val screenIndex = c.getColumnIndex(SCREEN)
@@ -651,9 +686,7 @@
         disableNewMigrationLogic()
     }
 
-    /**
-     * Migrating from a larger grid to a smaller, we reflow from page 0
-     */
+    /** Migrating from a larger grid to a smaller, we reflow from page 0 */
     @Test
     @Throws(Exception::class)
     fun migrateFromLargerGrid() {
@@ -672,25 +705,27 @@
         val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
         val destReader = DbReader(db, TABLE_NAME, context, validPackages)
         GridSizeMigrationUtil.migrate(
-                context,
-                db,
-                srcReader,
-                destReader,
-                idp.numDatabaseHotseatIcons,
-                Point(idp.numColumns, idp.numRows),
-                DeviceGridState(context),
-                DeviceGridState(idp)
+            context,
+            db,
+            srcReader,
+            destReader,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows),
+            DeviceGridState(context),
+            DeviceGridState(idp)
         )
 
         // Get workspace items
-        val c = context.contentResolver.query(
-            CONTENT_URI,
-            arrayOf(INTENT, SCREEN),
-            "container=$CONTAINER_DESKTOP",
-            null,
-            null,
-            null
-        ) ?: throw IllegalStateException()
+        val c =
+            context.contentResolver.query(
+                CONTENT_URI,
+                arrayOf(INTENT, SCREEN),
+                "container=$CONTAINER_DESKTOP",
+                null,
+                null,
+                null
+            )
+                ?: throw IllegalStateException()
         val intentIndex = c.getColumnIndex(INTENT)
         val screenIndex = c.getColumnIndex(SCREEN)
 
@@ -714,11 +749,13 @@
     }
 
     private fun enableNewMigrationLogic(srcGridSize: String) {
-        context.getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE)
+        context
+            .getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE)
             .edit()
             .putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, true)
             .commit()
-        context.getSharedPreferences(LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
+        context
+            .getSharedPreferences(LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
             .edit()
             .putString(DeviceGridState.KEY_WORKSPACE_SIZE, srcGridSize)
             .commit()
@@ -726,9 +763,10 @@
     }
 
     private fun disableNewMigrationLogic() {
-        context.getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE)
+        context
+            .getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE)
             .edit()
             .putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, false)
             .commit()
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
index bfb1ac6..b3d02be 100644
--- a/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
+++ b/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
@@ -24,9 +24,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-/**
- * Tests for [WorkspaceItemSpaceFinder]
- */
+/** Tests for [WorkspaceItemSpaceFinder] */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class WorkspaceItemSpaceFinderTest : AbstractWorkspaceModelTest() {
@@ -44,17 +42,27 @@
     }
 
     private fun findSpace(spanX: Int, spanY: Int): NewItemSpace =
-        mItemSpaceFinder.findSpaceForItem(
-            mAppState, mModelHelper.bgDataModel,
-            mExistingScreens, mNewScreens, spanX, spanY
-        )
+        mItemSpaceFinder
+            .findSpaceForItem(
+                mAppState,
+                mModelHelper.bgDataModel,
+                mExistingScreens,
+                mNewScreens,
+                spanX,
+                spanY
+            )
             .let { NewItemSpace.fromIntArray(it) }
 
     private fun assertRegionVacant(newItemSpace: NewItemSpace, spanX: Int, spanY: Int) {
         assertThat(
-            mScreenOccupancy[newItemSpace.screenId]
-                .isRegionVacant(newItemSpace.cellX, newItemSpace.cellY, spanX, spanY)
-        ).isTrue()
+                mScreenOccupancy[newItemSpace.screenId].isRegionVacant(
+                    newItemSpace.cellX,
+                    newItemSpace.cellY,
+                    spanX,
+                    spanY
+                )
+            )
+            .isTrue()
     }
 
     @Test
diff --git a/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt
index c99ad76..951f5f8 100644
--- a/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt
+++ b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt
@@ -29,8 +29,8 @@
 class HotseatWidthCalculationTest : DeviceProfileBaseTest() {
 
     /**
-     * This is a case when after setting the hotseat, the space needs to be recalculated
-     * but it doesn't need to change QSB width or remove icons
+     * This is a case when after setting the hotseat, the space needs to be recalculated but it
+     * doesn't need to change QSB width or remove icons
      */
     @Test
     fun distribute_border_space_when_space_is_enough_portrait() {
@@ -51,8 +51,8 @@
     }
 
     /**
-     * This is a case when after setting the hotseat, and recalculating spaces
-     * it still needs to remove icons for everything to fit
+     * This is a case when after setting the hotseat, and recalculating spaces it still needs to
+     * remove icons for everything to fit
      */
     @Test
     fun decrease_num_of_icons_when_not_enough_space_portrait() {
@@ -73,8 +73,8 @@
     }
 
     /**
-     * This is a case when after setting the hotseat, the space needs to be recalculated
-     * but it doesn't need to change QSB width or remove icons
+     * This is a case when after setting the hotseat, the space needs to be recalculated but it
+     * doesn't need to change QSB width or remove icons
      */
     @Test
     fun distribute_border_space_when_space_is_enough_landscape() {
@@ -94,8 +94,8 @@
     }
 
     /**
-     * This is a case when the hotseat spans a certain amount of columns
-     * and the nav buttons push the hotseat to the side, but not enough to change the border space.
+     * This is a case when the hotseat spans a certain amount of columns and the nav buttons push
+     * the hotseat to the side, but not enough to change the border space.
      */
     @Test
     fun nav_buttons_dont_interfere_with_required_hotseat_width() {
@@ -118,9 +118,7 @@
         assertThat(dp.hotseatQsbWidth).isEqualTo(1224)
     }
 
-    /**
-     * This is a case when after setting the hotseat, the QSB width needs to be changed to fit
-     */
+    /** This is a case when after setting the hotseat, the QSB width needs to be changed to fit */
     @Test
     fun decrease_qsb_when_not_enough_space_landscape() {
         initializeVarsForTablet(isGestureMode = false, isLandscape = true)
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 0db719e..9669010 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -147,7 +147,8 @@
 
         // Set callback
         PendingIntent callback = PendingIntent.getBroadcast(mTargetContext, 0,
-                new Intent(mCallbackAction), FLAG_ONE_SHOT | FLAG_MUTABLE);
+                new Intent(mCallbackAction).setPackage(mTargetContext.getPackageName()),
+                FLAG_ONE_SHOT | FLAG_MUTABLE);
         mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
                 RequestPinItemActivity.class, "setCallback").putExtra(
                 RequestPinItemActivity.EXTRA_PARAM + "0", callback));
diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
index 9dae00c..7ba0b53 100644
--- a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
@@ -36,7 +36,6 @@
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TaplTestsLauncher3;
 import com.android.launcher3.util.Executors;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 
 import org.junit.Test;
 
@@ -111,7 +110,6 @@
     }
 
     @Test
-    @ScreenRecord // b/260722220
     public void testShortcutIconWithTheme() throws Exception {
         setThemeEnabled(true);
         TaplTestsLauncher3.initialize(this);
diff --git a/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt b/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt
index 57db13a..4303bfb 100644
--- a/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt
+++ b/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt
@@ -22,43 +22,41 @@
  * 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.
+ * 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.
+ * 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.
+ * 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()
- */
+/** 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.
+ * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException when
+ * null is returned.
  *
  * Generic T is nullable because implicitly bounded by Any?.
  */
@@ -82,8 +80,9 @@
 /**
  * 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)
@@ -102,15 +101,15 @@
 
 /**
  * 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 =
diff --git a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
new file mode 100644
index 0000000..84156e7
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
@@ -0,0 +1,88 @@
+/*
+ * 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
+
+import android.content.Context
+import android.content.Intent
+import android.os.Process
+import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+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
+
+/** Unit tests for {@link LockedUserUtil} */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockedUserStateTest {
+
+    @Mock lateinit var userManager: UserManager
+    @Mock lateinit var context: Context
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        `when`(context.getSystemService(UserManager::class.java)).thenReturn(userManager)
+    }
+
+    @Test
+    fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() {
+        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
+        LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+        val action: Runnable = mock()
+
+        LockedUserState.get(context).runOnUserUnlocked(action)
+        verify(action).run()
+    }
+
+    @Test
+    fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() {
+        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
+        LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+        val action: Runnable = mock()
+
+        LockedUserState.get(context).runOnUserUnlocked(action)
+        verifyZeroInteractions(action)
+
+        LockedUserState.get(context)
+            .mUserUnlockedReceiver
+            .onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED))
+
+        verify(action).run()
+    }
+
+    @Test
+    fun isUserUnlocked_returns_true_when_user_is_unlocked() {
+        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
+        LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+        assertThat(LockedUserState.get(context).isUserUnlocked).isTrue()
+    }
+
+    @Test
+    fun isUserUnlocked_returns_false_when_user_is_locked() {
+        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
+        LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+        assertThat(LockedUserState.get(context).isUserUnlocked).isFalse()
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
index bf3a092..a63136e 100644
--- a/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
+++ b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
@@ -30,18 +30,18 @@
 
     private val received = mutableListOf<Float>()
 
-    private val receiveProperty: FloatProperty<Any> = object : FloatProperty<Any>("receive") {
-        override fun setValue(obj: Any?, value: Float) {
-            received.add(value)
+    private val receiveProperty: FloatProperty<Any> =
+        object : FloatProperty<Any>("receive") {
+            override fun setValue(obj: Any?, value: Float) {
+                received.add(value)
+            }
+            override fun get(o: Any): Float {
+                return 0f
+            }
         }
-        override fun get(o: Any): Float {
-            return 0f
-        }
-    }
 
-    private val factory = MultiPropertyFactory(null, receiveProperty, 3) {
-        x: Float, y: Float -> x + y
-    }
+    private val factory =
+        MultiPropertyFactory(null, receiveProperty, 3) { x: Float, y: Float -> x + y }
 
     private val p1 = factory.get(0)
     private val p2 = factory.get(1)
diff --git a/tests/src/com/android/launcher3/util/TouchUtilTest.kt b/tests/src/com/android/launcher3/util/TouchUtilTest.kt
index d6c6e91..235d6ec 100644
--- a/tests/src/com/android/launcher3/util/TouchUtilTest.kt
+++ b/tests/src/com/android/launcher3/util/TouchUtilTest.kt
@@ -18,9 +18,9 @@
 
 import android.view.InputDevice
 import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import org.junit.Test
 import org.junit.runner.RunWith