Merge "Removing Sunny shape option" into main
diff --git a/quickstep/res/layout/icon_app_chip_view.xml b/quickstep/res/layout/icon_app_chip_view.xml
index 00b5392..0972be1 100644
--- a/quickstep/res/layout/icon_app_chip_view.xml
+++ b/quickstep/res/layout/icon_app_chip_view.xml
@@ -51,7 +51,7 @@
     <TextView
         android:id="@+id/icon_text_collapsed"
         android:layout_width="@dimen/task_thumbnail_icon_menu_text_collapsed_max_width"
-        android:layout_height="@dimen/task_thumbnail_icon_menu_app_icon_collapsed_size"
+        android:layout_height="wrap_content"
         android:gravity="start|center_vertical"
         android:maxLines="1"
         android:ellipsize="end"
@@ -62,7 +62,7 @@
     <TextView
         android:id="@+id/icon_text_expanded"
         android:layout_width="@dimen/task_thumbnail_icon_menu_text_expanded_max_width"
-        android:layout_height="@dimen/task_thumbnail_icon_menu_app_icon_collapsed_size"
+        android:layout_height="wrap_content"
         android:gravity="start|center_vertical"
         android:maxLines="1"
         android:ellipsize="end"
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 5f2a63d..f8ca8d9 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -321,6 +321,7 @@
         <item name="android:fontFamily">google-sans-text-medium</item>
         <item name="android:textSize">@dimen/task_thumbnail_icon_menu_text_size</item>
         <item name="android:textColor">@color/materialColorOnSurface</item>
+        <item name="android:includeFontPadding">false</item>
         <item name="android:letterSpacing">0.025</item>
         <item name="android:lineHeight">20sp</item>
     </style>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index c748385..67feb6a 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -226,7 +226,6 @@
     private static final int TASKBAR_TO_HOME_DURATION_FAST = 300;
     private static final int TASKBAR_TO_HOME_DURATION_SLOW = 1000;
     protected static final int CONTENT_SCALE_DURATION = 350;
-    protected static final int CONTENT_SCRIM_DURATION = 350;
 
     private static final int MAX_NUM_TASKS = 5;
 
@@ -244,7 +243,13 @@
 
     private final StartingWindowListener mStartingWindowListener =
             new StartingWindowListener(this);
-    private ContentObserver mAnimationRemovalObserver = new ContentObserver(
+
+    // TODO(b/397690719): Investigate the memory leak from TaskStackChangeListeners#mImpl
+    // This is a temporary fix of memory leak b/397690719. We track registered
+    // {@link TaskRestartedDuringLaunchListener}, and remove them on activity destroy.
+    private final List<TaskRestartedDuringLaunchListener> mRegisteredTaskStackChangeListener =
+            new ArrayList<>();
+    private final ContentObserver mAnimationRemovalObserver = new ContentObserver(
             ORDERED_BG_EXECUTOR.getHandler()) {
         @Override
         public void onChange(boolean selfChange) {
@@ -338,7 +343,14 @@
         TaskRestartedDuringLaunchListener restartedListener =
                 new TaskRestartedDuringLaunchListener();
         restartedListener.register(onEndCallback::executeAllAndDestroy);
-        onEndCallback.add(restartedListener::unregister);
+        mRegisteredTaskStackChangeListener.add(restartedListener);
+        onEndCallback.add(new Runnable() {
+            @Override
+            public void run() {
+                restartedListener.unregister();
+                mRegisteredTaskStackChangeListener.remove(restartedListener);
+            }
+        });
 
         RemoteAnimationRunnerCompat runner = createAppLaunchRunner(v, onEndCallback);
 
@@ -1210,6 +1222,12 @@
         SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null);
         ORDERED_BG_EXECUTOR.execute(() -> mLauncher.getContentResolver()
                 .unregisterContentObserver(mAnimationRemovalObserver));
+        if (BuildConfig.IS_STUDIO_BUILD && !mRegisteredTaskStackChangeListener.isEmpty()) {
+            throw new IllegalStateException("Failed to run onEndCallback created from"
+                    + " getActivityLaunchOptions()");
+        }
+        mRegisteredTaskStackChangeListener.forEach(TaskRestartedDuringLaunchListener::unregister);
+        mRegisteredTaskStackChangeListener.clear();
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
index adbcc75..533a86a 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
@@ -21,6 +21,7 @@
 import android.animation.ValueAnimator
 import android.content.Context
 import android.graphics.Rect
+import android.util.Log
 import android.view.Choreographer
 import android.view.SurfaceControl.Transaction
 import android.view.WindowManager.TRANSIT_CLOSE
@@ -63,7 +64,19 @@
 
     fun createAnimators(info: TransitionInfo, finishCallback: (Animator) -> Unit): List<Animator> {
         val launchChange = getLaunchChange(info)
-        requireNotNull(launchChange) { "expected an app launch Change" }
+        requireNotNull(launchChange) {
+            val changesString =
+                info.changes.joinToString(", ") { change ->
+                    "Change: mode=${change.mode}, " +
+                        "taskId=${change.taskInfo?.id}, " +
+                        "isFreeform=${change.taskInfo?.isFreeform}"
+                }
+            Log.e(
+                TAG,
+                "No launch change found: Transition type=${info.type}, changes=$changesString",
+            )
+            "expected an app launch Change"
+        }
 
         val transaction = transactionSupplier.get()
 
@@ -189,4 +202,8 @@
             }
         }
     }
+
+    private companion object {
+        const val TAG = "DesktopAppLaunchAnimatorHelper"
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
index 578bba5..79072a6 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
@@ -19,6 +19,7 @@
 import android.animation.Animator
 import android.content.Context
 import android.os.IBinder
+import android.util.Log
 import android.view.SurfaceControl.Transaction
 import android.view.WindowManager.TRANSIT_OPEN
 import android.view.WindowManager.TRANSIT_TO_FRONT
@@ -43,7 +44,7 @@
 @JvmOverloads
 constructor(
     context: Context,
-    launchType: AppLaunchType,
+    private val launchType: AppLaunchType,
     @Cuj.CujType private val cujType: Int,
     private val mainExecutor: Executor,
     transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
@@ -66,6 +67,7 @@
         transaction: Transaction,
         transitionFinishedCallback: IRemoteTransitionFinishedCallback,
     ) {
+        Log.v(TAG, "startAnimation: launchType=$launchType, cujType=$cujType")
         val safeTransitionFinishedCallback = RemoteRunnable {
             transitionFinishedCallback.onTransitionFinished(/* wct= */ null, /* sct= */ null)
         }
@@ -86,6 +88,7 @@
     }
 
     companion object {
+        const val TAG = "DesktopAppLaunchTransition"
         /** Change modes that represent a task becoming visible / launching in Desktop mode. */
         val LAUNCH_CHANGE_MODES = intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT)
 
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
index 37e8d4d..6ee43ff 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
@@ -62,8 +62,6 @@
      * (Used only when multiple desks are enabled).
      *
      * @property displayId The ID of the display this object represents.
-     * @property canCreateDesks true if it's possible to create new desks on the display represented
-     *   by this object.
      * @property activeDeskId The ID of the active desk on the associated display (if any). It has a
      *   value of `INACTIVE_DESK_ID` (-1) if there are no active desks. Note that there can only be
      *   at most one active desk on each display.
@@ -71,11 +69,18 @@
      */
     private data class DisplayDeskConfig(
         val displayId: Int,
-        var canCreateDesks: Boolean,
         var activeDeskId: Int = INACTIVE_DESK_ID,
         val deskIds: MutableSet<Int>,
     )
 
+    /** True if it is possible to create new desks on current setup. */
+    var canCreateDesks: Boolean = false
+        private set(value) {
+            if (field == value) return
+            field = value
+            desktopVisibilityListeners.forEach { it.onCanCreateDesksChanged(field) }
+        }
+
     /** Maps each display by its ID to its desks configuration. */
     private val displaysDesksConfigsMap = SparseArray<DisplayDeskConfig>()
 
@@ -393,7 +398,10 @@
         }
     }
 
-    private fun onListenerConnected(displayDeskStates: Array<DisplayDeskState>) {
+    private fun onListenerConnected(
+        displayDeskStates: Array<DisplayDeskState>,
+        canCreateDesks: Boolean,
+    ) {
         if (!DesktopModeStatus.enableMultipleDesktops(context)) {
             return
         }
@@ -404,23 +412,24 @@
             displaysDesksConfigsMap[displayDeskState.displayId] =
                 DisplayDeskConfig(
                     displayId = displayDeskState.displayId,
-                    canCreateDesks = displayDeskState.canCreateDesk,
                     activeDeskId = displayDeskState.activeDeskId,
                     deskIds = displayDeskState.deskIds.toMutableSet(),
                 )
         }
+
+        this.canCreateDesks = canCreateDesks
     }
 
     private fun getDisplayDeskConfig(displayId: Int) =
         displaysDesksConfigsMap[displayId]
             ?: null.also { Slog.e(TAG, "Expected non-null desk config for display: $displayId") }
 
-    private fun onCanCreateDesksChanged(displayId: Int, canCreateDesks: Boolean) {
+    private fun onCanCreateDesksChanged(canCreateDesks: Boolean) {
         if (!DesktopModeStatus.enableMultipleDesktops(context)) {
             return
         }
 
-        getDisplayDeskConfig(displayId)?.canCreateDesks = canCreateDesks
+        this.canCreateDesks = canCreateDesks
     }
 
     private fun onDeskAdded(displayId: Int, deskId: Int) {
@@ -525,9 +534,12 @@
     ) : Stub() {
         private val controller = WeakReference(controller)
 
-        override fun onListenerConnected(displayDeskStates: Array<DisplayDeskState>) {
+        override fun onListenerConnected(
+            displayDeskStates: Array<DisplayDeskState>,
+            canCreateDesks: Boolean,
+        ) {
             Executors.MAIN_EXECUTOR.execute {
-                controller.get()?.onListenerConnected(displayDeskStates)
+                controller.get()?.onListenerConnected(displayDeskStates, canCreateDesks)
             }
         }
 
@@ -565,9 +577,9 @@
 
         override fun onExitDesktopModeTransitionStarted(transitionDuration: Int) {}
 
-        override fun onCanCreateDesksChanged(displayId: Int, canCreateDesks: Boolean) {
+        override fun onCanCreateDesksChanged(canCreateDesks: Boolean) {
             Executors.MAIN_EXECUTOR.execute {
-                controller.get()?.onCanCreateDesksChanged(displayId, canCreateDesks)
+                controller.get()?.onCanCreateDesksChanged(canCreateDesks)
             }
         }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index d1e63f3..2eb4412 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -18,6 +18,7 @@
 import static android.os.Trace.TRACE_TAG_APP;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
@@ -876,7 +877,7 @@
 
     @Override
     public void onPopupVisibilityChanged(boolean isVisible) {
-        setTaskbarWindowFocusable(isVisible);
+        setTaskbarWindowFocusable(isVisible /* focusable */, false /* imeFocusable */);
     }
 
     @Override
@@ -1235,17 +1236,29 @@
     }
 
     /**
-     * Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar
-     * window.
+     * Sets whether the taskbar window should be focusable and IME focusable. This won't be IME
+     * focusable unless it is also focusable.
+     *
+     * @param focusable    whether it should be focusable.
+     * @param imeFocusable whether it should be IME focusable.
+     *
+     * @see WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE
+     * @see WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
      */
-    public void setTaskbarWindowFocusable(boolean focusable) {
+    public void setTaskbarWindowFocusable(boolean focusable, boolean imeFocusable) {
         if (isPhoneMode()) {
             return;
         }
         if (focusable) {
             mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
+            if (imeFocusable) {
+                mWindowLayoutParams.flags &= ~FLAG_ALT_FOCUSABLE_IM;
+            } else {
+                mWindowLayoutParams.flags |= FLAG_ALT_FOCUSABLE_IM;
+            }
         } else {
             mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
+            mWindowLayoutParams.flags &= ~FLAG_ALT_FOCUSABLE_IM;
         }
         notifyUpdateLayoutParams();
     }
@@ -1266,8 +1279,12 @@
     }
 
     /**
-     * Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar
-     * window. If we're now focusable, also move nav buttons to a separate window above IME.
+     * Sets whether the taskbar window should be focusable, as well as IME focusable. If we're now
+     * focusable, also move nav buttons to a separate window above IME.
+     *
+     * @param focusable whether it should be focusable.
+     *
+     * @see WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE
      */
     public void setTaskbarWindowFocusableForIme(boolean focusable) {
         if (focusable) {
@@ -1275,7 +1292,7 @@
         } else {
             mControllers.navbarButtonsViewController.moveNavButtonsBackToTaskbarWindow();
         }
-        setTaskbarWindowFocusable(focusable);
+        setTaskbarWindowFocusable(focusable, true /* imeFocusable */);
     }
 
     /** Adds the given view to WindowManager with the provided LayoutParams (creates new window). */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 6cd2979..34bb6e0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -45,6 +45,7 @@
 import android.os.Handler;
 import android.os.Trace;
 import android.provider.Settings;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -75,14 +76,20 @@
 import com.android.quickstep.fallback.window.RecentsDisplayModel;
 import com.android.quickstep.fallback.window.RecentsWindowManager;
 import com.android.quickstep.util.ContextualSearchInvoker;
+import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.views.RecentsViewContainer;
+import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.statusbar.phone.BarTransitions;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
 import java.io.PrintWriter;
+import java.util.Set;
 import java.util.StringJoiner;
 
 /**
@@ -194,6 +201,65 @@
         recreateTaskbar();
     };
 
+    private final PerceptibleTaskListener mTaskStackListener;
+
+    private class PerceptibleTaskListener implements TaskStackChangeListener {
+        private ArraySet<Integer> mPerceptibleTasks = new ArraySet<Integer>();
+
+        @Override
+        public void onTaskMovedToFront(int taskId) {
+            if (mPerceptibleTasks.contains(taskId)) {
+                return;
+            }
+
+            // This listens to any Task, so we filter them by the ones shown in the launcher.
+            // For Tasks restored after startup, they will by default not be Perceptible, and no
+            // need to until user interacts with it by bringing it to the foreground.
+            for (int i = 0; i < mTaskbars.size(); i++) {
+                // get pinned tasks
+                Set<Integer> taskbarPinnedTasks =
+                        mTaskbars.valueAt(i).getControllers().taskbarViewController
+                                .getTaskIdsForPinnedApps();
+
+                // mark as perceptible if the foregrounded task is in the list of apps shown in
+                // the launcher.
+                if (taskbarPinnedTasks.contains(taskId)
+                        && ActivityManagerWrapper.getInstance()
+                        .setTaskIsPerceptible(taskId, true)
+                ) {
+                    mPerceptibleTasks.add(taskId);
+                }
+            }
+        }
+
+        /**
+         * Launcher also can display recently launched tasks that are not pinned. Also add
+         * these as perceptible
+         */
+        @Override
+        public void onRecentTaskListUpdated() {
+            for (int i = 0; i < mTaskbars.size(); i++) {
+                for (GroupTask gTask : mTaskbars.valueAt(i).getControllers()
+                        .taskbarRecentAppsController.getShownTasks()) {
+                    for (Task task : gTask.getTasks()) {
+                        int taskId = task.key.id;
+
+                        if (!mPerceptibleTasks.contains(taskId)) {
+                            ActivityManagerWrapper.getInstance()
+                                    .setTaskIsPerceptible(taskId, true);
+                            mPerceptibleTasks.add(taskId);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onTaskRemoved(int taskId) {
+            mPerceptibleTasks.remove(taskId);
+        }
+    };
+
     private boolean mUserUnlocked = false;
 
     private final SimpleBroadcastReceiver mTaskbarBroadcastReceiver;
@@ -296,6 +362,12 @@
             mTaskbarBroadcastReceiver.register(RECEIVER_NOT_EXPORTED, ACTION_SHOW_TASKBAR);
         });
 
+        if (ActivityManagerWrapper.usePerceptibleTasks(getPrimaryWindowContext())) {
+            mTaskStackListener = new PerceptibleTaskListener();
+            TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
+        } else {
+            mTaskStackListener = null;
+        }
         debugTaskbarManager("TaskbarManager created");
         recreateTaskbar();
     }
@@ -709,15 +781,14 @@
             return;
         }
 
-        // TODO (b/391965805): remove once onDisplayAddSystemDecorations is working.
-        WindowManager wm = getWindowManager(displayId);
-        if (wm == null || !wm.shouldShowSystemDecors(displayId)) {
-            return;
-        }
-
         Context newWindowContext = createWindowContext(displayId);
         if (newWindowContext != null) {
             addWindowContextToMap(displayId, newWindowContext);
+            // TODO (b/391965805): remove once onDisplayAddSystemDecorations is working.
+            WindowManager wm = getWindowManager(displayId);
+            if (wm == null || !wm.shouldShowSystemDecors(displayId)) {
+                return;
+            }
             createTaskbarRootLayout(displayId);
             createNavButtonController(displayId);
             createAndRegisterComponentCallbacks(displayId);
@@ -788,6 +859,12 @@
         Log.d(TASKBAR_NOT_DESTROYED_TAG, "unregistering component callbacks from destroy().");
         removeAndUnregisterComponentCallbacks(getDefaultDisplayId());
         mShutdownReceiver.unregisterReceiverSafely();
+        if (ActivityManagerWrapper.usePerceptibleTasks(getPrimaryWindowContext())) {
+            for (Integer taskId: mTaskStackListener.mPerceptibleTasks) {
+                ActivityManagerWrapper.getInstance().setTaskIsPerceptible(taskId, false);
+            }
+        }
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
         destroyAllTaskbars();
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 37b8dc7..15a27d1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -17,6 +17,7 @@
 
 import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
 import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter;
+import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -53,7 +54,6 @@
 import com.android.launcher3.celllayout.DelegatedCellDrawing;
 import com.android.launcher3.graphics.ThemeManager;
 import com.android.launcher3.icons.FastBitmapDrawable;
-import com.android.launcher3.icons.IconNormalizer;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -138,7 +138,7 @@
     public PredictedAppIcon(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         mDeviceProfile = ActivityContext.lookupContext(context).getDeviceProfile();
-        mNormalizedIconSize = IconNormalizer.getNormalizedCircleSize(getIconSize());
+        mNormalizedIconSize = Math.round(getIconSize() * ICON_VISIBLE_AREA_FACTOR);
         int shadowSize = context.getResources().getDimensionPixelSize(
                 R.dimen.blur_size_thin_outline);
         mShadowFilter = new BlurMaskFilter(shadowSize, BlurMaskFilter.Blur.OUTER);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
index 6e901ee..1f34969 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
@@ -144,7 +144,7 @@
     override fun isNonResizeableActivity(lai: LauncherActivityInfo) =
         lai.activityInfo.resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE
 
-    override fun supportsMultiInstance(lai: LauncherActivityInfo) : Boolean {
+    override fun supportsMultiInstance(lai: LauncherActivityInfo): Boolean {
         return try {
             super.supportsMultiInstance(lai) || lai.supportsMultiInstance()
         } catch (e: Exception) {
@@ -202,4 +202,7 @@
         (appInfo.sourceDir?.hashCode() ?: 0).toString() + " " + appInfo.longVersionCode
 
     override fun getRoundIconRes(appInfo: ApplicationInfo) = appInfo.roundIconRes
+
+    override fun isFileDrawable(shortcutInfo: ShortcutInfo) =
+        shortcutInfo.hasIconFile() || shortcutInfo.hasIconUri()
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 10513c0..b27c6e8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -153,7 +153,7 @@
         return new PageAlphaProvider(DECELERATE_2) {
             @Override
             public float getPageAlpha(int pageIndex) {
-                return launcher.getDeviceProfile().shouldShowAllAppsOnSheet()
+                return launcher.getDeviceProfile().isTablet
                         ? superPageAlphaProvider.getPageAlpha(pageIndex)
                         : 0;
             }
@@ -164,7 +164,7 @@
     public int getVisibleElements(Launcher launcher) {
         int elements = ALL_APPS_CONTENT | FLOATING_SEARCH_BAR;
         // When All Apps is presented on a bottom sheet, HOTSEAT_ICONS are visible.
-        if (launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
+        if (launcher.getDeviceProfile().isTablet) {
             elements |= HOTSEAT_ICONS;
         }
         return elements;
diff --git a/quickstep/src/com/android/quickstep/DesktopFullscreenDrawParams.kt b/quickstep/src/com/android/quickstep/DesktopFullscreenDrawParams.kt
index bafb0b2..444e77d 100644
--- a/quickstep/src/com/android/quickstep/DesktopFullscreenDrawParams.kt
+++ b/quickstep/src/com/android/quickstep/DesktopFullscreenDrawParams.kt
@@ -17,7 +17,7 @@
 package com.android.quickstep
 
 import android.content.Context
-import com.android.systemui.shared.system.QuickStepContract
+import com.android.launcher3.R
 
 // DesktopTaskView thumbnail's corner radius is independent of fullscreenProgress.
 open class DesktopFullscreenDrawParams
@@ -28,6 +28,6 @@
         // computeCornerRadius is used as cornerRadiusProvider, so
         // QuickStepContract::getWindowCornerRadius can be mocked properly.
         private fun computeCornerRadius(context: Context): Float =
-            QuickStepContract.getWindowCornerRadius(context)
+            context.resources.getDimension(R.dimen.desktop_windowing_freeform_rounded_corner_radius)
     }
 }
diff --git a/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
index 608fafd..12616a8 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
@@ -17,6 +17,7 @@
 package com.android.quickstep.recents.data
 
 import android.os.UserHandle
+import android.util.Log
 import com.android.quickstep.HighResLoadingState.HighResLoadingStateChangedCallback
 import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskIconChangedCallback
 import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskThumbnailChangedCallback
@@ -58,7 +59,7 @@
         fun onTaskThumbnailChanged(thumbnailData: ThumbnailData?)
 
         /** Informs the listener that the default resolution for loading thumbnails has changed */
-        fun onHighResLoadingStateChanged()
+        fun onHighResLoadingStateChanged(highResEnabled: Boolean)
     }
 }
 
@@ -91,8 +92,9 @@
     }
 
     override fun onHighResLoadingStateChanged(enabled: Boolean) {
+        Log.d(TAG, "onHighResLoadingStateChanged(enabled = $enabled)")
         taskThumbnailChangedCallbacks.values.forEach { (_, callback) ->
-            callback.onHighResLoadingStateChanged()
+            callback.onHighResLoadingStateChanged(enabled)
         }
     }
 
@@ -142,4 +144,8 @@
             }
         }
     }
+
+    companion object {
+        const val TAG = "TaskVisualsChangedDelegateImpl"
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index b1a5920..5274ef3 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -160,7 +160,17 @@
                     updateThumbnail(task.key.id, thumbnailData)
                 }
 
-                override fun onHighResLoadingStateChanged() {
+                override fun onHighResLoadingStateChanged(highResEnabled: Boolean) {
+                    val isTaskVisible = taskRequests.containsKey(task.key.id)
+                    if (!isTaskVisible) return
+
+                    val isCurrentThumbnailLowRes =
+                        tasks.value[task.key.id]?.thumbnail?.reducedResolution
+                    val isRequestedResHigherThanCurrent =
+                        isCurrentThumbnailLowRes == null ||
+                            (isCurrentThumbnailLowRes && highResEnabled)
+                    if (!isRequestedResHigherThanCurrent) return
+
                     recentsCoroutineScope.launch(dispatcherProvider.background) {
                         updateThumbnail(task.key.id, getThumbnailFromDataSource(task))
                     }
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index e672ec4..e91073a 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -20,6 +20,7 @@
 import android.graphics.Color
 import android.graphics.Matrix
 import android.graphics.Outline
+import android.graphics.Path
 import android.graphics.Rect
 import android.graphics.drawable.ShapeDrawable
 import android.util.AttributeSet
@@ -52,6 +53,7 @@
     private val dimAlpha: MultiPropertyFactory<View> by lazy {
         MultiPropertyFactory(scrimView, VIEW_ALPHA, ScrimViewAlpha.entries.size, ::maxOf)
     }
+    private val outlinePath = Path()
     private var onSizeChanged: ((width: Int, height: Int) -> Unit)? = null
 
     private var taskThumbnailViewHeader: TaskThumbnailViewHeader? = null
@@ -96,7 +98,20 @@
         outlineProvider =
             object : ViewOutlineProvider() {
                 override fun getOutline(view: View, outline: Outline) {
-                    outline.setRoundRect(outlineBounds ?: bounds, cornerRadius)
+                    val outlineRect = outlineBounds ?: bounds
+                    outlinePath.apply {
+                        rewind()
+                        addRoundRect(
+                            outlineRect.left.toFloat(),
+                            outlineRect.top.toFloat(),
+                            outlineRect.right.toFloat(),
+                            outlineRect.bottom.toFloat(),
+                            cornerRadius / scaleX,
+                            cornerRadius / scaleY,
+                            Path.Direction.CW,
+                        )
+                    }
+                    outline.setPath(outlinePath)
                 }
             }
     }
diff --git a/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java b/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java
index 91e8376..6e2d469 100644
--- a/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java
+++ b/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java
@@ -16,16 +16,11 @@
 
 package com.android.quickstep.util;
 
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-
-import android.app.Activity;
 import android.app.ActivityManager;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
 
-import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
-import com.android.quickstep.RecentsModel;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index a29d302..c5a76cb 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -157,6 +157,7 @@
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.statemanager.StatefulContainer;
@@ -179,6 +180,7 @@
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.util.ViewPool;
 import com.android.launcher3.util.coroutines.DispatcherProvider;
+import com.android.launcher3.util.window.WindowManagerProxy.DesktopVisibilityListener;
 import com.android.quickstep.BaseContainerInterface;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.HighResLoadingState;
@@ -265,7 +267,7 @@
         CONTAINER_TYPE extends Context & RecentsViewContainer & StatefulContainer<STATE_TYPE>,
         STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
         HighResLoadingState.HighResLoadingStateChangedCallback,
-        TaskVisualsChangeListener {
+        TaskVisualsChangeListener, DesktopVisibilityListener {
 
     private static final String TAG = "RecentsView";
     private static final boolean DEBUG = false;
@@ -556,6 +558,10 @@
     private final Rect mTaskViewDeadZoneRect = new Rect();
     private final Rect mTopRowDeadZoneRect = new Rect();
     private final Rect mBottomRowDeadZoneRect = new Rect();
+
+    @Nullable
+    private DesktopVisibilityController mDesktopVisibilityController = null;
+
     /**
      * Reflects if Recents is currently in the middle of a gesture, and if so, which tasks are
      * running. If a gesture is not in progress, this will be null.
@@ -913,6 +919,8 @@
             mAddDesktopButton = (AddDesktopButton) LayoutInflater.from(context).inflate(
                     R.layout.overview_add_desktop_button, this, false);
             mAddDesktopButton.setOnClickListener(this::createDesk);
+
+            mDesktopVisibilityController = DesktopVisibilityController.INSTANCE.get(mContext);
         }
 
         mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
@@ -1227,13 +1235,16 @@
         mSyncTransactionApplier = new SurfaceTransactionApplier(this);
         runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
                 .setSyncTransactionApplier(mSyncTransactionApplier));
-        RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
+        RecentsModel.INSTANCE.get(mContext).addThumbnailChangeListener(this);
         mIPipAnimationListener.setActivityAndRecentsView(mContainer, this);
-        SystemUiProxy.INSTANCE.get(getContext()).setPipAnimationListener(
+        SystemUiProxy.INSTANCE.get(mContext).setPipAnimationListener(
                 mIPipAnimationListener);
         mOrientationState.initListeners();
         mTaskOverlayFactory.initListeners();
         mSplitSelectStateController.registerSplitListener(mSplitSelectionListener);
+        if (mDesktopVisibilityController != null) {
+            mDesktopVisibilityController.registerDesktopVisibilityListener(this);
+        }
     }
 
     @Override
@@ -1248,12 +1259,15 @@
         runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
                 .setSyncTransactionApplier(null));
         executeSideTaskLaunchCallback();
-        RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
-        SystemUiProxy.INSTANCE.get(getContext()).setPipAnimationListener(null);
+        RecentsModel.INSTANCE.get(mContext).removeThumbnailChangeListener(this);
+        SystemUiProxy.INSTANCE.get(mContext).setPipAnimationListener(null);
         mIPipAnimationListener.setActivityAndRecentsView(null, null);
         mOrientationState.destroyListeners();
         mTaskOverlayFactory.removeListeners();
         mSplitSelectStateController.unregisterSplitListener(mSplitSelectionListener);
+        if (mDesktopVisibilityController != null) {
+            mDesktopVisibilityController.unregisterDesktopVisibilityListener(this);
+        }
         reset();
     }
 
@@ -6859,6 +6873,11 @@
         }
     }
 
+    @Override
+    public void onCanCreateDesksChanged(boolean canCreateDesks) {
+        // TODO: b/389209338 - update the AddDesktopButton's visibility on this.
+    }
+
     /** Get the color used for foreground scrimming the RecentsView for sharing. */
     public static int getForegroundScrimDimColor(Context context) {
         return context.getColor(R.color.overview_foreground_scrim_color);
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
index 6f0aaeb..80b2c16 100644
--- a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
@@ -213,6 +213,18 @@
         }
     }
 
+    @Test
+    fun taskThumbnailView_scaled_roundRoundedCorners() {
+        screenshotRule.screenshotTest("taskThumbnailView_scaledRoundedCorners") { activity ->
+            activity.actionBar?.hide()
+            createTaskThumbnailView(activity).apply {
+                scaleX = 0.75f
+                scaleY = 0.3f
+                setState(BackgroundOnly(Color.YELLOW))
+            }
+        }
+    }
+
     private fun createTaskThumbnailView(context: Context): TaskThumbnailView {
         val taskThumbnailView =
             LayoutInflater.from(context).inflate(R.layout.task_thumbnail, null, false)
diff --git a/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchTransitionManagerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManagerTest.kt
similarity index 90%
rename from quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchTransitionManagerTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManagerTest.kt
index 9ca9fe4..7ebef45 100644
--- a/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchTransitionManagerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManagerTest.kt
@@ -27,12 +27,10 @@
 import android.window.TransitionFilter
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.quickstep.SystemUiProxy
 import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.google.common.truth.Truth.assertThat
-import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -43,7 +41,6 @@
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
-import org.mockito.quality.Strictness
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -51,12 +48,6 @@
 
     @get:Rule val mSetFlagsRule = SetFlagsRule()
 
-    private val mockitoSession =
-        mockitoSession()
-            .strictness(Strictness.LENIENT)
-            .mockStatic(DesktopModeStatus::class.java)
-            .startMocking()
-
     private val context = mock<Context>()
     private val systemUiProxy = mock<SystemUiProxy>()
     private lateinit var transitionManager: DesktopAppLaunchTransitionManager
@@ -68,11 +59,6 @@
         transitionManager = DesktopAppLaunchTransitionManager(context, systemUiProxy)
     }
 
-    @After
-    fun tearDown() {
-        mockitoSession.finishMocking()
-    }
-
     @Test
     @EnableFlags(FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
     fun registerTransitions_appLaunchFlagEnabled_registersTransition() {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
index e10afc4..40d5e02 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.shared.recents.model.ThumbnailData
 import kotlinx.coroutines.yield
 import org.mockito.kotlin.mock
-import org.mockito.kotlin.whenever
 
 class FakeTaskThumbnailDataSource : TaskThumbnailDataSource {
 
@@ -31,6 +30,8 @@
     private val completionPrevented: MutableSet<Int> = mutableSetOf()
     private val getThumbnailCalls = mutableMapOf<Int, Int>()
 
+    var highResEnabled = true
+
     /** Retrieves and sets a thumbnail on [task] from [taskIdToBitmap]. */
     override suspend fun getThumbnail(task: Task): ThumbnailData {
         getThumbnailCalls[task.key.id] = (getThumbnailCalls[task.key.id] ?: 0) + 1
@@ -38,9 +39,10 @@
         while (task.key.id in completionPrevented) {
             yield()
         }
-        return mock<ThumbnailData>().also {
-            whenever(it.thumbnail).thenReturn(taskIdToBitmap[task.key.id])
-        }
+        return ThumbnailData(
+            thumbnail = taskIdToBitmap[task.key.id],
+            reducedResolution = !highResEnabled,
+        )
     }
 
     fun getNumberOfGetThumbnailCalls(taskId: Int): Int = getThumbnailCalls[taskId] ?: 0
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegateTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegateTest.kt
index 41f6bfd..b91f8bd 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegateTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegateTest.kt
@@ -19,16 +19,19 @@
 import android.content.ComponentName
 import android.content.Intent
 import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskIconChangedCallback
 import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskThumbnailChangedCallback
 import com.android.systemui.shared.recents.model.Task.TaskKey
 import com.android.systemui.shared.recents.model.ThumbnailData
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.verifyNoMoreInteractions
 
+@RunWith(AndroidJUnit4::class)
 class TaskVisualsChangedDelegateTest {
     private val taskVisualsChangeNotifier = FakeTaskVisualsChangeNotifier()
     private val highResLoadingStateNotifier = FakeHighResLoadingStateNotifier()
@@ -83,21 +86,21 @@
         // Correct match
         systemUnderTest.registerTaskIconChangedCallback(
             createTaskKey(id = 1, pkg = ALTERNATIVE_PACKAGE_NAME, userId = 1),
-            expectedListener
+            expectedListener,
         )
         // 1 out of 2 match
         systemUnderTest.registerTaskIconChangedCallback(
             createTaskKey(id = 2, pkg = PACKAGE_NAME, userId = 1),
-            listener
+            listener,
         )
         systemUnderTest.registerTaskIconChangedCallback(
             createTaskKey(id = 3, pkg = ALTERNATIVE_PACKAGE_NAME, userId = 2),
-            listener
+            listener,
         )
         // 0 out of 2 match
         systemUnderTest.registerTaskIconChangedCallback(
             createTaskKey(id = 4, pkg = PACKAGE_NAME, userId = 2),
-            listener
+            listener,
         )
 
         systemUnderTest.onTaskIconChanged(ALTERNATIVE_PACKAGE_NAME, UserHandle(1))
@@ -112,11 +115,11 @@
         val newListener = mock<TaskIconChangedCallback>()
         systemUnderTest.registerTaskIconChangedCallback(
             createTaskKey(id = 1, pkg = ALTERNATIVE_PACKAGE_NAME, userId = 1),
-            replacedListener
+            replacedListener,
         )
         systemUnderTest.registerTaskIconChangedCallback(
             createTaskKey(id = 1, pkg = ALTERNATIVE_PACKAGE_NAME, userId = 1),
-            newListener
+            newListener,
         )
 
         systemUnderTest.onTaskIconChanged(ALTERNATIVE_PACKAGE_NAME, UserHandle(1))
@@ -132,11 +135,11 @@
         val expectedThumbnailData = ThumbnailData(snapshotId = 12345)
         systemUnderTest.registerTaskThumbnailChangedCallback(
             createTaskKey(id = 1),
-            expectedListener
+            expectedListener,
         )
         systemUnderTest.registerTaskThumbnailChangedCallback(
             createTaskKey(id = 2),
-            additionalListener
+            additionalListener,
         )
 
         systemUnderTest.onTaskThumbnailChanged(1, expectedThumbnailData)
@@ -146,22 +149,41 @@
     }
 
     @Test
-    fun onHighResLoadingStateChanged_notifiesAllListeners() {
+    fun onHighResLoadingStateChanged_toEnabled_notifiesAllListeners() {
         val expectedListener = mock<TaskThumbnailChangedCallback>()
         val additionalListener = mock<TaskThumbnailChangedCallback>()
         systemUnderTest.registerTaskThumbnailChangedCallback(
             createTaskKey(id = 1),
-            expectedListener
+            expectedListener,
         )
         systemUnderTest.registerTaskThumbnailChangedCallback(
             createTaskKey(id = 2),
-            additionalListener
+            additionalListener,
         )
 
         systemUnderTest.onHighResLoadingStateChanged(true)
 
-        verify(expectedListener).onHighResLoadingStateChanged()
-        verify(additionalListener).onHighResLoadingStateChanged()
+        verify(expectedListener).onHighResLoadingStateChanged(true)
+        verify(additionalListener).onHighResLoadingStateChanged(true)
+    }
+
+    @Test
+    fun onHighResLoadingStateChanged_toDisabled_notifiesAllListeners() {
+        val expectedListener = mock<TaskThumbnailChangedCallback>()
+        val additionalListener = mock<TaskThumbnailChangedCallback>()
+        systemUnderTest.registerTaskThumbnailChangedCallback(
+            createTaskKey(id = 1),
+            expectedListener,
+        )
+        systemUnderTest.registerTaskThumbnailChangedCallback(
+            createTaskKey(id = 2),
+            additionalListener,
+        )
+
+        systemUnderTest.onHighResLoadingStateChanged(false)
+
+        verify(expectedListener).onHighResLoadingStateChanged(false)
+        verify(additionalListener).onHighResLoadingStateChanged(false)
     }
 
     @Test
@@ -171,7 +193,7 @@
         val expectedThumbnailData = ThumbnailData(snapshotId = 12345)
         systemUnderTest.registerTaskThumbnailChangedCallback(
             createTaskKey(id = 1),
-            replacedListener1
+            replacedListener1,
         )
         systemUnderTest.registerTaskThumbnailChangedCallback(createTaskKey(id = 1), newListener1)
 
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
index 823f808..10be6fd 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -326,8 +326,9 @@
         }
 
     @Test
-    fun onHighResLoadingStateChanged_setsNewThumbnailDataOnTask() =
+    fun onHighResLoadingStateChanged_highResReplacesLowResThumbnail() =
         testScope.runTest {
+            taskThumbnailDataSource.highResEnabled = false
             recentsModel.seedTasks(defaultTaskList)
             systemUnderTest.getAllTaskData(forceRefresh = true)
 
@@ -337,16 +338,77 @@
             val expectedPreviousBitmap = taskThumbnailDataSource.taskIdToBitmap[1]
             val taskDataFlow = systemUnderTest.getTaskDataById(1)
 
-            val task1ThumbnailValues = mutableListOf<Bitmap?>()
+            val task1ThumbnailValues = mutableListOf<ThumbnailData?>()
             testScope.backgroundScope.launch {
-                taskDataFlow.map { it?.thumbnail?.thumbnail }.toList(task1ThumbnailValues)
+                taskDataFlow.map { it?.thumbnail }.toList(task1ThumbnailValues)
             }
 
             taskThumbnailDataSource.taskIdToBitmap[1] = expectedBitmap
+            taskThumbnailDataSource.highResEnabled = true
             taskVisualsChangedDelegate.onHighResLoadingStateChanged(true)
 
-            assertThat(task1ThumbnailValues.first()).isEqualTo(expectedPreviousBitmap)
-            assertThat(task1ThumbnailValues.last()).isEqualTo(expectedBitmap)
+            val firstThumbnailValue = task1ThumbnailValues.first()!!
+            assertThat(firstThumbnailValue.thumbnail).isEqualTo(expectedPreviousBitmap)
+            assertThat(firstThumbnailValue.reducedResolution).isTrue()
+
+            val lastThumbnailValue = task1ThumbnailValues.last()!!
+            assertThat(lastThumbnailValue.thumbnail).isEqualTo(expectedBitmap)
+            assertThat(lastThumbnailValue.reducedResolution).isFalse()
+        }
+
+    @Test
+    fun onHighResLoadingStateChanged_invisibleTaskIgnored() =
+        testScope.runTest {
+            taskThumbnailDataSource.highResEnabled = false
+            recentsModel.seedTasks(defaultTaskList)
+            systemUnderTest.getAllTaskData(forceRefresh = true)
+
+            systemUnderTest.setVisibleTasks(setOf(1))
+
+            val invisibleTaskId = 2
+            val taskDataFlow = systemUnderTest.getTaskDataById(invisibleTaskId)
+
+            val task2ThumbnailValues = mutableListOf<ThumbnailData?>()
+            testScope.backgroundScope.launch {
+                taskDataFlow.map { it?.thumbnail }.toList(task2ThumbnailValues)
+            }
+
+            taskThumbnailDataSource.highResEnabled = true
+            taskVisualsChangedDelegate.onHighResLoadingStateChanged(true)
+
+            assertThat(task2ThumbnailValues.filterNotNull()).isEmpty()
+            assertThat(taskThumbnailDataSource.getNumberOfGetThumbnailCalls(2)).isEqualTo(0)
+        }
+
+    @Test
+    fun onHighResLoadingStateChanged_lowResDoesNotReplaceHighResThumbnail() =
+        testScope.runTest {
+            taskThumbnailDataSource.highResEnabled = true
+            recentsModel.seedTasks(defaultTaskList)
+            systemUnderTest.getAllTaskData(forceRefresh = true)
+
+            systemUnderTest.setVisibleTasks(setOf(1))
+
+            val expectedBitmap = mock<Bitmap>()
+            val expectedPreviousBitmap = taskThumbnailDataSource.taskIdToBitmap[1]
+            val taskDataFlow = systemUnderTest.getTaskDataById(1)
+
+            val task1ThumbnailValues = mutableListOf<ThumbnailData?>()
+            testScope.backgroundScope.launch {
+                taskDataFlow.map { it?.thumbnail }.toList(task1ThumbnailValues)
+            }
+
+            taskThumbnailDataSource.taskIdToBitmap[1] = expectedBitmap
+            taskThumbnailDataSource.highResEnabled = false
+            taskVisualsChangedDelegate.onHighResLoadingStateChanged(false)
+
+            val firstThumbnailValue = task1ThumbnailValues.first()!!
+            assertThat(firstThumbnailValue.thumbnail).isEqualTo(expectedPreviousBitmap)
+            assertThat(firstThumbnailValue.reducedResolution).isFalse()
+
+            val lastThumbnailValue = task1ThumbnailValues.last()!!
+            assertThat(lastThumbnailValue.thumbnail).isEqualTo(expectedPreviousBitmap)
+            assertThat(lastThumbnailValue.reducedResolution).isFalse()
         }
 
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
index 76aab39..a523e02 100644
--- a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.view.Display.DEFAULT_DISPLAY
 import androidx.test.platform.app.InstrumentationRegistry
@@ -106,6 +107,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+    @DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
     fun createDesktopTaskShortcutFactory_transparentTask() {
         val baseComponent = ComponentName("", /* class */ "")
         val taskKey =
diff --git a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
index 818841a..2db94f6 100644
--- a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
 import android.view.Display.DEFAULT_DISPLAY
@@ -113,6 +114,7 @@
         Flags.FLAG_MOVE_TO_EXTERNAL_DISPLAY_SHORTCUT,
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
     )
+    @DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
     fun createExternalDisplayTaskShortcut_transparentTask() {
         val baseComponent = ComponentName("", /* class */ "")
         val taskKey =
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 03dd943..bd42b2b 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -27,6 +27,7 @@
 import static com.android.launcher3.icons.BitmapInfo.FLAG_SKIP_USER_BADGE;
 import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK;
@@ -76,7 +77,6 @@
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.PreloadIconDrawable;
-import com.android.launcher3.graphics.ShapeDelegate;
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
@@ -723,8 +723,7 @@
     protected void drawDotIfNecessary(Canvas canvas) {
         if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
             getIconBounds(mDotParams.iconBounds);
-            Utilities.scaleRectAboutCenter(mDotParams.iconBounds,
-                    ShapeDelegate.getNormalizationScale());
+            Utilities.scaleRectAboutCenter(mDotParams.iconBounds, ICON_VISIBLE_AREA_FACTOR);
             final int scrollX = getScrollX();
             final int scrollY = getScrollY();
             canvas.translate(scrollX, scrollY);
@@ -773,9 +772,7 @@
             return;
         }
         getIconBounds(mRunningAppIconBounds);
-        Utilities.scaleRectAboutCenter(
-                mRunningAppIconBounds,
-                ShapeDelegate.getNormalizationScale());
+        Utilities.scaleRectAboutCenter(mRunningAppIconBounds, ICON_VISIBLE_AREA_FACTOR);
 
         final boolean isMinimized = mRunningAppState == RunningAppState.MINIMIZED;
         final int indicatorTop = mRunningAppIconBounds.bottom + mRunningAppIndicatorTopMargin;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 4561506..c85ca49 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -55,7 +55,6 @@
 import com.android.launcher3.folder.ClippedFolderIconLayoutRule;
 import com.android.launcher3.graphics.ThemeManager;
 import com.android.launcher3.icons.DotRenderer;
-import com.android.launcher3.icons.IconNormalizer;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.responsive.CalculatedCellSpec;
 import com.android.launcher3.responsive.CalculatedHotseatSpec;
@@ -1374,7 +1373,7 @@
         updateHotseatSizes(iconSizePx);
 
         // Folder icon
-        folderIconSizePx = IconNormalizer.getNormalizedCircleSize(iconSizePx);
+        folderIconSizePx = Math.round(iconSizePx * ICON_VISIBLE_AREA_FACTOR);
         folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2;
 
         // Update widget padding:
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 6109131..981b78b 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -62,7 +62,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.ThemeManager;
 import com.android.launcher3.icons.FastBitmapDrawable;
-import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.IconNormalizer;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.views.ActivityContext;
@@ -263,12 +263,7 @@
                 // be scaled down due to icon normalization.
                 mBadge = fullDrawable.second;
                 FastBitmapDrawable.setBadgeBounds(mBadge, bounds);
-
-                try (LauncherIcons li = LauncherIcons.obtain(mActivity)) {
-                    // Since we just want the scale, avoid heavy drawing operations
-                    Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(
-                            new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null)));
-                }
+                Utilities.scaleRectAboutCenter(bounds, IconNormalizer.ICON_VISIBLE_AREA_FACTOR);
 
                 // Shrink very tiny bit so that the clip path is smaller than the original bitmap
                 // that has anti aliased edges and shadows.
@@ -568,7 +563,7 @@
         return mContentViewParent;
     }
 
-    /** Return true if {@link mContent} is a {@link AppWidgetHostView}. */
+    /** Return true if {@link #mContent} is a {@link AppWidgetHostView}. */
     public boolean containsAppWidgetHostView() {
         return mContent instanceof AppWidgetHostView;
     }
diff --git a/src/com/android/launcher3/graphics/ShapeDelegate.kt b/src/com/android/launcher3/graphics/ShapeDelegate.kt
index df0c8f9..9033eac 100644
--- a/src/com/android/launcher3/graphics/ShapeDelegate.kt
+++ b/src/com/android/launcher3/graphics/ShapeDelegate.kt
@@ -43,7 +43,6 @@
 import androidx.graphics.shapes.transformed
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider
 import com.android.launcher3.icons.GraphicsUtils
-import com.android.launcher3.icons.IconNormalizer.normalizeAdaptiveIcon
 import com.android.launcher3.views.ClipPathView
 
 /** Abstract representation of the shape of an icon shape */
@@ -342,12 +341,5 @@
                 centerY = (bottom - top) / 2,
                 rounding = CornerRounding(cornerR),
             )
-
-        @JvmStatic
-        val normalizationScale =
-            normalizeAdaptiveIcon(
-                AdaptiveIconDrawable(null, ColorDrawable(Color.BLACK)),
-                AREA_CALC_SIZE,
-            )
     }
 }
diff --git a/src/com/android/launcher3/icons/CacheableShortcutInfo.kt b/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
index 225e12f..50dd146 100644
--- a/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
+++ b/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
@@ -30,6 +30,7 @@
 import com.android.launcher3.icons.cache.BaseIconCache
 import com.android.launcher3.icons.cache.CachingLogic
 import com.android.launcher3.shortcuts.ShortcutKey
+import com.android.launcher3.util.ApiWrapper
 import com.android.launcher3.util.ApplicationInfoWrapper
 import com.android.launcher3.util.PackageUserKey
 import com.android.launcher3.util.Themes
@@ -114,7 +115,15 @@
                         d,
                         IconOptions()
                             .setExtractedColor(Themes.getColorAccent(context))
-                            .setSourceHint(getSourceHint(info, cache)),
+                            .setSourceHint(
+                                getSourceHint(info, cache)
+                                    .copy(
+                                        isFileDrawable =
+                                            ApiWrapper.INSTANCE[context].isFileDrawable(
+                                                info.shortcutInfo
+                                            )
+                                    )
+                            ),
                     )
                 } ?: BitmapInfo.LOW_RES_INFO
         }
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 107bcc1..2cc4909 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -207,6 +207,11 @@
             }
             config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATED_EASE);
             config.setInterpolator(ANIM_DEPTH, DECELERATED_EASE);
+            if (launcher.getDeviceProfile().isPhone) {
+                config.setInterpolator(ANIM_WORKSPACE_FADE, INSTANT);
+                config.setInterpolator(ANIM_HOTSEAT_FADE, INSTANT);
+                config.animFlags |= StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
+            }
         } else {
             if (config.isUserControlled()) {
                 config.setInterpolator(ANIM_DEPTH, Interpolators.reverse(BLUR_MANUAL));
@@ -248,6 +253,11 @@
             }
             config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATED_EASE);
             config.setInterpolator(ANIM_DEPTH, DECELERATED_EASE);
+            if (launcher.getDeviceProfile().isPhone) {
+                config.setInterpolator(ANIM_WORKSPACE_FADE, FINAL_FRAME);
+                config.setInterpolator(ANIM_HOTSEAT_FADE, FINAL_FRAME);
+                config.animFlags |= StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
+            }
         } else {
             config.setInterpolator(ANIM_DEPTH,
                     config.isUserControlled() ? BLUR_MANUAL : BLUR_ATOMIC);
diff --git a/src/com/android/launcher3/util/ApiWrapper.java b/src/com/android/launcher3/util/ApiWrapper.java
index 56337b0..0510d59 100644
--- a/src/com/android/launcher3/util/ApiWrapper.java
+++ b/src/com/android/launcher3/util/ApiWrapper.java
@@ -216,6 +216,13 @@
         return 0;
     }
 
+    /**
+     * Checks if the shortcut is using an icon with file or URI source
+     */
+    public boolean isFileDrawable(@NonNull ShortcutInfo shortcutInfo) {
+        return false;
+    }
+
     private static class NoopDrawable extends ColorDrawable {
         @Override
         public int getIntrinsicHeight() {
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 647d170..11f0bc2 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -509,7 +509,17 @@
          * @param isInDesktopModeAndNotInOverview True if a desktop is currently active on the given
          *                                        display, and Overview is currently inactive.
          */
-        void onIsInDesktopModeChanged(int displayId, boolean isInDesktopModeAndNotInOverview);
+        default void onIsInDesktopModeChanged(int displayId,
+                boolean isInDesktopModeAndNotInOverview) {
+        }
+
+        /**
+         * Called whenever the conditions that allow the creation of desks change.
+         *
+         * @param canCreateDesks whether it is possible to create new desks.
+         */
+        default void onCanCreateDesksChanged(boolean canCreateDesks) {
+        }
     }
 
 }
diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java
index ddf18df..a295d6b 100644
--- a/src/com/android/launcher3/views/ClipIconView.java
+++ b/src/com/android/launcher3/views/ClipIconView.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.Utilities.boundToRange;
 import static com.android.launcher3.Utilities.mapToRange;
 import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 
 import static java.lang.Math.max;
@@ -44,10 +45,10 @@
 import androidx.core.util.Consumer;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
-import com.android.launcher3.graphics.ShapeDelegate;
 import com.android.launcher3.graphics.ThemeManager;
 
 /**
@@ -68,6 +69,7 @@
     private boolean mIsAdaptiveIcon = false;
 
     private ValueAnimator mRevealAnimator;
+    private float mIconScale;
 
     private final Rect mStartRevealRect = new Rect();
     private final Rect mEndRevealRect = new Rect();
@@ -173,9 +175,12 @@
 
         mTaskCornerRadius = cornerRadius / scale;
         if (mIsAdaptiveIcon) {
-            if (!isOpening && progress >= shapeProgressStart) {
+            final ThemeManager themeManager = ThemeManager.INSTANCE.get(getContext());
+            mIconScale = themeManager.getIconState().getIconScale();
+            if ((!isOpening || Flags.enableLauncherIconShapes())
+                    && progress >= shapeProgressStart) {
                 if (mRevealAnimator == null) {
-                    mRevealAnimator = ThemeManager.INSTANCE.get(getContext()).getIconShape()
+                    mRevealAnimator = themeManager.getIconShape()
                             .createRevealAnimator(this, mStartRevealRect,
                                     mOutline, mTaskCornerRadius, !isOpening);
                     mRevealAnimator.addListener(forEndCallback(() -> mRevealAnimator = null));
@@ -259,8 +264,7 @@
             mStartRevealRect.set(0, 0, originalWidth, originalHeight);
 
             if (!isFolderIcon) {
-                Utilities.scaleRectAboutCenter(mStartRevealRect,
-                        ShapeDelegate.getNormalizationScale());
+                Utilities.scaleRectAboutCenter(mStartRevealRect, ICON_VISIBLE_AREA_FACTOR);
             }
 
             if (dp.isLandscape) {
@@ -310,17 +314,24 @@
 
     @Override
     public void draw(Canvas canvas) {
-        int count = canvas.save();
+        int count1 = canvas.save();
         if (mClipPath != null) {
             canvas.clipPath(mClipPath);
         }
-        super.draw(canvas);
+        int count2 = canvas.save();
+        float iconCenterX =
+                (mFinalDrawableBounds.right - mFinalDrawableBounds.left) / 2f * mIconScale;
+        float iconCenterY =
+                (mFinalDrawableBounds.bottom - mFinalDrawableBounds.top) / 2f * mIconScale;
+        canvas.scale(mIconScale, mIconScale, iconCenterX, iconCenterY);
         if (mBackground != null) {
             mBackground.draw(canvas);
         }
         if (mForeground != null) {
             mForeground.draw(canvas);
         }
+        canvas.restoreToCount(count2);
+        super.draw(canvas);
         if (mTaskViewArtist != null) {
             canvas.saveLayerAlpha(
                     0,
@@ -334,7 +345,7 @@
             canvas.scale(drawScale, drawScale);
             mTaskViewArtist.taskViewDrawCallback.accept(canvas);
         }
-        canvas.restoreToCount(count);
+        canvas.restoreToCount(count1);
     }
 
     void recycle() {
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 22857b1..5b3abc3 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -55,7 +55,7 @@
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.icons.FastBitmapDrawable;
-import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.IconNormalizer;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.popup.SystemShortcut;
@@ -463,10 +463,7 @@
         Rect bounds = new Rect(0, 0, (int) position.width() + blurSizeOutline,
                 (int) position.height() + blurSizeOutline);
         bounds.inset(blurSizeOutline / 2, blurSizeOutline / 2);
-
-        try (LauncherIcons li = LauncherIcons.obtain(l)) {
-            Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable));
-        }
+        Utilities.scaleRectAboutCenter(bounds, IconNormalizer.ICON_VISIBLE_AREA_FACTOR);
 
         bounds.inset(
                 (int) (-bounds.width() * AdaptiveIconDrawable.getExtraInsetFraction()),
diff --git a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index 8cdf380..3658989 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -364,7 +364,13 @@
         context.assets.open("dumpTests/$fileName").bufferedReader().use(BufferedReader::readText)
 
     private fun writeToDevice(context: Context, fileName: String, content: String) {
-        File(context.getDir("dumpTests", Context.MODE_PRIVATE), fileName).writeText(content)
+        val dir =
+            File(context.filesDir, "dumpTests").also {
+                if (!it.exists()) {
+                    it.mkdirs()
+                }
+            }
+        File(dir, fileName).writeText(content)
     }
 
     protected fun Float.dpToPx(): Float {