Merge "Adding feature flags for Launcher reboot and load improvements" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index fc2be06..98dd4d6 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -85,6 +85,13 @@
 }
 
 flag {
+    name: "enable_shortcut_dont_suggest_app"
+    namespace: "launcher"
+    description: "Enables don't suggest app shortcut for suggested apps"
+    bug: "319250810"
+}
+
+flag {
     name: "enable_support_for_archiving"
     namespace: "launcher"
     description: "Enables support for archived apps in Launcher3, such as empty progress bar etc."
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index 8dded8f..e17fc88 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -38,6 +38,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
 import static com.android.launcher3.model.PredictionHelper.isTrackedForHotseatPrediction;
@@ -137,7 +138,8 @@
                 || event == LAUNCHER_QUICKSWITCH_LEFT
                 || event == LAUNCHER_APP_LAUNCH_DRAGDROP) {
             sendEvent(atomInfo, ACTION_LAUNCH, CONTAINER_PREDICTION);
-        } else if (event == LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST) {
+        } else if (event == LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST
+                || event == LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP) {
             sendEvent(atomInfo, ACTION_DISMISS, CONTAINER_PREDICTION);
         } else if (event == LAUNCHER_ITEM_DRAG_STARTED) {
             mLastDragItem = atomInfo;
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 382276a..fd0d655 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -22,6 +22,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.graphics.Outline;
@@ -39,6 +40,7 @@
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.MultiValueAlpha;
+import com.android.quickstep.NavHandle;
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 
 import java.io.PrintWriter;
@@ -46,7 +48,8 @@
 /**
  * Handles properties/data collection, then passes the results to our stashed handle View to render.
  */
-public class StashedHandleViewController implements TaskbarControllers.LoggableTaskbarController {
+public class StashedHandleViewController implements TaskbarControllers.LoggableTaskbarController,
+        NavHandle {
 
     public static final int ALPHA_INDEX_STASHED = 0;
     public static final int ALPHA_INDEX_HOME_DISABLED = 1;
@@ -318,4 +321,24 @@
         pw.println(prefix + "\tmStashedHandleHeight=" + mStashedHandleHeight);
         mRegionSamplingHelper.dump(prefix, pw);
     }
+
+    @Override
+    public void animateNavBarLongPress(boolean isTouchDown, boolean shrink, long durationMs) {
+        // TODO(b/308693847): Animate similarly to NavigationHandle.java (SysUI).
+    }
+
+    @Override
+    public boolean isNavHandleStashedTaskbar() {
+        return true;
+    }
+
+    @Override
+    public boolean canNavHandleBeLongPressed() {
+        return isStashedHandleVisible();
+    }
+
+    @Override
+    public int getNavHandleWidth(Context context) {
+        return mStashedHandleWidth;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 7ad2c68..4b95d7b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -123,6 +123,7 @@
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.util.ViewCache;
 import com.android.launcher3.views.ActivityContext;
+import com.android.quickstep.NavHandle;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
@@ -611,6 +612,11 @@
         return mControllers.bubbleControllers.orElse(null);
     }
 
+    @NonNull
+    public NavHandle getNavHandle() {
+        return mControllers.stashedHandleViewController;
+    }
+
     @Override
     public ViewCache getViewCache() {
         return mViewCache;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 0d4a7f0..f0ab08c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -39,6 +39,7 @@
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
 import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
 import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
+import static com.android.launcher3.popup.SystemShortcut.DONT_SUGGEST_APP;
 import static com.android.launcher3.popup.SystemShortcut.INSTALL;
 import static com.android.launcher3.popup.SystemShortcut.PRIVATE_PROFILE_INSTALL;
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -417,10 +418,12 @@
         shortcuts.addAll(getSplitShortcuts());
         shortcuts.add(WIDGETS);
         shortcuts.add(INSTALL);
-
         if (Flags.enablePrivateSpaceInstallShortcut()) {
             shortcuts.add(PRIVATE_PROFILE_INSTALL);
         }
+        if (Flags.enableShortcutDontSuggestApp()) {
+            shortcuts.add(DONT_SUGGEST_APP);
+        }
         return shortcuts.stream();
     }
 
diff --git a/quickstep/src/com/android/quickstep/NavHandle.java b/quickstep/src/com/android/quickstep/NavHandle.java
new file mode 100644
index 0000000..da3311f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/NavHandle.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import android.content.Context;
+
+import com.android.launcher3.R;
+
+/**
+ * Control and get information about the gesture nav bar at the bottom of the screen, which has
+ * historically been drawn by SysUI, but is also emulated by the stashed Taskbar on large screens.
+ */
+public interface NavHandle {
+
+    /**
+     * Animate the nav bar being long-pressed.
+     *
+     * @param isTouchDown {@code true} if the button is starting to be pressed ({@code false} if
+     *                                released or canceled)
+     * @param shrink {@code true} if the handle should shrink, {@code false} if it should grow
+     * @param durationMs how long the animation should take (for the {@code isTouchDown} case, this
+     *                   should be the same as the amount of time to trigger a long-press)
+     */
+    void animateNavBarLongPress(boolean isTouchDown, boolean shrink, long durationMs);
+
+    /** @return {@code true} if this nav handle is actually the stashed taskbar */
+    default boolean isNavHandleStashedTaskbar() {
+        return false;
+    }
+
+    /** @return {@code true} if this nav handle can currently accept long presses */
+    default boolean canNavHandleBeLongPressed() {
+        return true;
+    }
+
+    /** @return the width of this nav handle, in pixels */
+    default int getNavHandleWidth(Context context) {
+        return context.getResources().getDimensionPixelSize(R.dimen.navigation_home_handle_width);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index a8c6809..723af43 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -106,7 +106,7 @@
 /**
  * Holds the reference to SystemUI.
  */
-public class SystemUiProxy implements ISystemUiProxy {
+public class SystemUiProxy implements ISystemUiProxy, NavHandle {
     private static final String TAG = SystemUiProxy.class.getSimpleName();
 
     public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 7982606..647ff90 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -1005,14 +1005,22 @@
                     base = new TaskbarUnstashInputConsumer(this, base, mInputMonitorCompat, tac,
                             mOverviewCommandHelper);
                 }
-            } else if (canStartSystemGesture && !previousGestureState.isRecentsAnimationRunning()) {
+            }
+
+            NavHandle navHandle = tac != null ? tac.getNavHandle()
+                    : SystemUiProxy.INSTANCE.get(this);
+            if (canStartSystemGesture && !previousGestureState.isRecentsAnimationRunning()
+                    && navHandle.canNavHandleBeLongPressed()) {
                 reasonString.append(NEWLINE_PREFIX)
                         .append(reasonPrefix)
                         .append(SUBSTRING_PREFIX)
-                        .append("Not running recents animation, ")
-                        .append("using NavHandleLongPressInputConsumer");
+                        .append("Not running recents animation, ");
+                if (tac != null && tac.getNavHandle().canNavHandleBeLongPressed()) {
+                    reasonString.append("stashed handle is long-pressable, ");
+                }
+                reasonString.append("using NavHandleLongPressInputConsumer");
                 base = new NavHandleLongPressInputConsumer(this, base, mInputMonitorCompat,
-                        mDeviceState);
+                        mDeviceState, navHandle);
             }
 
             if (mDeviceState.isBubblesExpanded()) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
index 4d47f07..1d00e53 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
@@ -22,6 +22,7 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.quickstep.NavHandle;
 
 /**
  * Class for extending nav handle long press behavior
@@ -42,19 +43,26 @@
      * A Runnable is returned here to ensure the InputConsumer can call
      * {@link android.view.InputMonitor#pilferPointers()} before invoking the long press behavior
      * since pilfering can break the long press behavior.
+     *
+     * @param navHandle to handle this long press
      */
-    public @Nullable Runnable getLongPressRunnable() {
+    public @Nullable Runnable getLongPressRunnable(NavHandle navHandle) {
         return null;
     }
 
     /**
      * Called when nav handle gesture starts.
+     *
+     * @param navHandle to handle the animation for this touch
      */
-    public void onTouchStarted() {}
+    public void onTouchStarted(NavHandle navHandle) {}
 
     /**
      * Called when nav handle gesture is finished by the user lifting their finger or the system
      * cancelling the touch for some other reason.
+     *
+     * @param navHandle to handle the animation for this touch
+     * @param reason why the touch ended
      */
-    public void onTouchFinished(String reason) {}
+    public void onTouchFinished(NavHandle navHandle, String reason) {}
 }
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index 0a558e2..4c0b550 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -17,7 +17,9 @@
 
 import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_TIMEOUT_MS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DEEP_PRESS_NAVBAR;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DEEP_PRESS_STASHED_TASKBAR;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LONG_PRESS_NAVBAR;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LONG_PRESS_STASHED_TASKBAR;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.content.Context;
@@ -25,11 +27,11 @@
 import android.view.ViewConfiguration;
 
 import com.android.launcher3.LauncherPrefs;
-import com.android.launcher3.R;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.InputConsumer;
+import com.android.quickstep.NavHandle;
 import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.TopTaskTracker;
 import com.android.systemui.shared.system.InputMonitorCompat;
@@ -47,6 +49,7 @@
     private final float mTouchSlopSquared;
     private final int mLongPressTimeout;
     private final boolean mDeepPressEnabled;
+    private final NavHandle mNavHandle;
     private final StatsLogManager mStatsLogManager;
     private final TopTaskTracker mTopTaskTracker;
 
@@ -54,10 +57,9 @@
     private boolean mDeepPressLogged;  // Whether deep press has been logged for the current touch.
 
     public NavHandleLongPressInputConsumer(Context context, InputConsumer delegate,
-            InputMonitorCompat inputMonitor, RecentsAnimationDeviceState deviceState) {
+            InputMonitorCompat inputMonitor, RecentsAnimationDeviceState deviceState,
+            NavHandle navHandle) {
         super(delegate, inputMonitor);
-        mNavHandleWidth = context.getResources().getDimensionPixelSize(
-                R.dimen.navigation_home_handle_width);
         mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x;
         mDeepPressEnabled = FeatureFlags.ENABLE_LPNH_DEEP_PRESS.get();
         if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) {
@@ -66,6 +68,8 @@
             mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
         }
         mTouchSlopSquared = deviceState.getSquaredTouchSlop();
+        mNavHandle = navHandle;
+        mNavHandleWidth = navHandle.getNavHandleWidth(context);
         mNavHandleLongPressHandler = NavHandleLongPressHandler.newInstance(context);
         mStatsLogManager = StatsLogManager.newInstance(context);
         mTopTaskTracker = TopTaskTracker.INSTANCE.get(context);
@@ -89,6 +93,11 @@
         }
     }
 
+    @Override
+    public void onHoverEvent(MotionEvent ev) {
+        mDelegate.onHoverEvent(ev);
+    }
+
     private void handleMotionEvent(MotionEvent ev) {
         switch (ev.getAction()) {
             case MotionEvent.ACTION_DOWN -> {
@@ -98,9 +107,8 @@
                 mCurrentDownEvent = MotionEvent.obtain(ev);
                 mDeepPressLogged = false;
                 if (isInNavBarHorizontalArea(ev.getRawX())) {
-                    mNavHandleLongPressHandler.onTouchStarted();
-                    MAIN_EXECUTOR.getHandler().postDelayed(mTriggerLongPress,
-                            mLongPressTimeout);
+                    mNavHandleLongPressHandler.onTouchStarted(mNavHandle);
+                    MAIN_EXECUTOR.getHandler().postDelayed(mTriggerLongPress, mLongPressTimeout);
                 }
             }
             case MotionEvent.ACTION_MOVE -> {
@@ -127,8 +135,9 @@
             // Log deep press even if feature is disabled.
             String runningPackage = mTopTaskTracker.getCachedTopTask(
                     /* filterOnlyVisibleRecents */ true).getPackageName();
-            mStatsLogManager.logger().withPackageName(runningPackage)
-                    .log(LAUNCHER_DEEP_PRESS_NAVBAR);
+            mStatsLogManager.logger().withPackageName(runningPackage).log(
+                    mNavHandle.isNavHandleStashedTaskbar() ? LAUNCHER_DEEP_PRESS_STASHED_TASKBAR
+                            : LAUNCHER_DEEP_PRESS_NAVBAR);
             mDeepPressLogged = true;
 
             // But only trigger if the feature is enabled.
@@ -142,9 +151,11 @@
     private void triggerLongPress() {
         String runningPackage = mTopTaskTracker.getCachedTopTask(
                 /* filterOnlyVisibleRecents */ true).getPackageName();
-        mStatsLogManager.logger().withPackageName(runningPackage).log(LAUNCHER_LONG_PRESS_NAVBAR);
+        mStatsLogManager.logger().withPackageName(runningPackage).log(
+                mNavHandle.isNavHandleStashedTaskbar() ? LAUNCHER_LONG_PRESS_STASHED_TASKBAR
+                        : LAUNCHER_LONG_PRESS_NAVBAR);
 
-        Runnable longPressRunnable = mNavHandleLongPressHandler.getLongPressRunnable();
+        Runnable longPressRunnable = mNavHandleLongPressHandler.getLongPressRunnable(mNavHandle);
         if (longPressRunnable == null) {
             return;
         }
@@ -161,7 +172,7 @@
 
     private void cancelLongPress(String reason) {
         MAIN_EXECUTOR.getHandler().removeCallbacks(mTriggerLongPress);
-        mNavHandleLongPressHandler.onTouchFinished(reason);
+        mNavHandleLongPressHandler.onTouchFinished(mNavHandle, reason);
     }
 
     private boolean isInNavBarHorizontalArea(float x) {
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 34677f6..fb55c75 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -175,7 +175,7 @@
     <string name="install_private_system_shortcut_label">Install in private</string>
     <!-- Label for install drop target. [CHAR_LIMIT=20] -->
     <string name="install_drop_target_label">Install</string>
-    <!-- Label for install dismiss prediction. -->
+    <!-- Label for dismiss prediction. -->
     <string name="dismiss_prediction_label">Don\'t suggest app</string>
     <!-- Label for pinning predicted app. -->
     <string name="pin_prediction">Pin Prediction</string>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index a2f6bf9..826eeb2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1512,7 +1512,6 @@
 
     private void prepareAppWidget(AppWidgetHostView hostView, LauncherAppWidgetInfo item) {
         hostView.setTag(item);
-        item.onBindAppWidget(this, hostView);
         hostView.setFocusable(true);
         hostView.setOnFocusChangeListener(mFocusHandler);
     }
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 2dd610cb..0b92c28 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -155,6 +155,9 @@
             }
             return INVALID;
         } else if (info.isPredictedItem()) {
+            if (Flags.enableShortcutDontSuggestApp()) {
+                return INVALID;
+            }
             return DISMISS_PREDICTION;
         }
 
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 7cc33cf..45ff33b 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -211,6 +211,9 @@
         @UiEvent(doc = "User tapped on pin system shortcut.")
         LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP(522),
 
+        @UiEvent(doc = "User tapped on don't suggest app system shortcut.")
+        LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP(1603),
+
         @UiEvent(doc = "User is shown All Apps education view.")
         LAUNCHER_ALL_APPS_EDU_SHOWN(523),
 
@@ -284,6 +287,12 @@
         @UiEvent(doc = "User long presses on the bottom bezel area.")
         LAUNCHER_LONG_PRESS_NAVBAR(1544),
 
+        @UiEvent(doc = "User deep presses on the stashed taskbar")
+        LAUNCHER_DEEP_PRESS_STASHED_TASKBAR(1602),
+
+        @UiEvent(doc = "User long presses on the stashed taskbar")
+        LAUNCHER_LONG_PRESS_STASHED_TASKBAR(1592),
+
         @UiEvent(doc = "User swipes or fling in UP direction from bottom bazel area.")
         LAUNCHER_HOME_GESTURE(574),
 
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index 697fbc8..f98cab6 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -45,6 +45,7 @@
 import com.android.launcher3.util.PackageUserKey
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo
 import com.android.launcher3.widget.WidgetInflater
+import com.android.launcher3.widget.util.WidgetSizes
 
 /**
  * This items is used by LoaderTask to process items that have been loaded from the Launcher's DB.
@@ -423,44 +424,56 @@
         }
         val inflationResult = widgetInflater.inflateAppWidget(appWidgetInfo)
         var shouldUpdate = inflationResult.isUpdate
-        if (inflationResult.type == WidgetInflater.TYPE_DELETE) {
-            c.markDeleted(inflationResult.reason, inflationResult.restoreErrorType)
-            return
-        }
-
         val lapi = inflationResult.widgetInfo
-        if (inflationResult.type == WidgetInflater.TYPE_PENDING) {
-            tempPackageKey.update(component.packageName, c.user)
-            val si = installingPkgs[tempPackageKey]
 
-            if (
-                !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) &&
-                    !isSafeMode &&
-                    (si == null) &&
-                    (lapi == null)
-            ) {
-                // Restore never started
-                c.markDeleted(
-                    "Unrestored widget removed: $component",
-                    RestoreError.APP_NOT_INSTALLED
-                )
+        when (inflationResult.type) {
+            WidgetInflater.TYPE_DELETE -> {
+                c.markDeleted(inflationResult.reason, inflationResult.restoreErrorType)
                 return
-            } else if (
-                !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) && si != null
-            ) {
-                shouldUpdate = true
-                appWidgetInfo.restoreStatus =
-                    appWidgetInfo.restoreStatus or LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
             }
-            appWidgetInfo.installProgress = if (si == null) 0 else (si.getProgress() * 100).toInt()
-            appWidgetInfo.pendingItemInfo =
-                WidgetsModel.newPendingItemInfo(
+            WidgetInflater.TYPE_PENDING -> {
+                tempPackageKey.update(component.packageName, c.user)
+                val si = installingPkgs[tempPackageKey]
+
+                if (
+                    !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) &&
+                        !isSafeMode &&
+                        (si == null) &&
+                        (lapi == null)
+                ) {
+                    // Restore never started
+                    c.markDeleted(
+                        "Unrestored widget removed: $component",
+                        RestoreError.APP_NOT_INSTALLED
+                    )
+                    return
+                } else if (
+                    !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) && si != null
+                ) {
+                    shouldUpdate = true
+                    appWidgetInfo.restoreStatus =
+                        appWidgetInfo.restoreStatus or LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
+                }
+                appWidgetInfo.installProgress =
+                    if (si == null) 0 else (si.getProgress() * 100).toInt()
+                appWidgetInfo.pendingItemInfo =
+                    WidgetsModel.newPendingItemInfo(
+                        app.context,
+                        appWidgetInfo.providerName,
+                        appWidgetInfo.user
+                    )
+                iconCache.getTitleAndIconForApp(appWidgetInfo.pendingItemInfo, false)
+            }
+            WidgetInflater.TYPE_REAL ->
+                WidgetSizes.updateWidgetSizeRangesAsync(
+                    appWidgetInfo.appWidgetId,
+                    lapi,
                     app.context,
-                    appWidgetInfo.providerName,
-                    appWidgetInfo.user
+                    appWidgetInfo.spanX,
+                    appWidgetInfo.spanY
                 )
-            iconCache.getTitleAndIconForApp(appWidgetInfo.pendingItemInfo, false)
         }
+
         if (shouldUpdate) {
             c.updater()
                 .put(Favorites.APPWIDGET_PROVIDER, component.flattenToString())
diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index 1fbe04f..6fa8c54 100644
--- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -32,13 +32,11 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.widget.util.WidgetSizes;
 
 /**
  * Represents a widget (either instantiated or about to be) in the Launcher.
@@ -143,8 +141,6 @@
      */
     private int widgetFeatures;
 
-    private boolean mHasNotifiedInitialWidgetSizeChanged;
-
     /**
      * The container from which this widget was added (e.g. widgets tray, pin widget, search)
      */
@@ -202,17 +198,6 @@
                 .put(LauncherSettings.Favorites.APPWIDGET_SOURCE, sourceContainer);
     }
 
-    /**
-     * When we bind the widget, we should notify the widget that the size has changed if we have not
-     * done so already (only really for default workspace widgets).
-     */
-    public void onBindAppWidget(Launcher launcher, AppWidgetHostView hostView) {
-        if (!mHasNotifiedInitialWidgetSizeChanged) {
-            WidgetSizes.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
-            mHasNotifiedInitialWidgetSizeChanged = true;
-        }
-    }
-
     @Override
     protected String dumpProperties() {
         return super.dumpProperties()
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index d4ab4ab..fa7700b 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -2,6 +2,7 @@
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_INSTALL_SYSTEM_SHORTCUT_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP;
 
 import android.app.ActivityOptions;
@@ -324,6 +325,34 @@
         }
     }
 
+    public static final Factory<Launcher> DONT_SUGGEST_APP = new Factory<Launcher>() {
+        @Nullable
+        @Override
+        public SystemShortcut<Launcher> getShortcut(Launcher activity, ItemInfo itemInfo,
+                View originalView) {
+            if (!itemInfo.isPredictedItem()) {
+                return null;
+            }
+            return new DontSuggestApp(activity, itemInfo, originalView);
+        }
+    };
+
+    private static class DontSuggestApp extends SystemShortcut<Launcher> {
+        DontSuggestApp(Launcher target, ItemInfo itemInfo,
+                View originalView) {
+            super(R.drawable.ic_block_no_shadow, R.string.dismiss_prediction_label, target,
+                    itemInfo, originalView);
+        }
+
+        @Override
+        public void onClick(View view) {
+            dismissTaskMenuView(mTarget);
+            mTarget.getStatsLogManager().logger()
+                    .withItemInfo(mItemInfo)
+                    .log(LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP);
+        }
+    }
+
     public static <T extends Context & ActivityContext> void dismissTaskMenuView(T activity) {
         AbstractFloatingView.closeOpenViews(activity, true,
             AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
diff --git a/src/com/android/launcher3/widget/util/WidgetSizes.java b/src/com/android/launcher3/widget/util/WidgetSizes.java
index 7049509..4688359 100644
--- a/src/com/android/launcher3/widget/util/WidgetSizes.java
+++ b/src/com/android/launcher3/widget/util/WidgetSizes.java
@@ -15,8 +15,11 @@
  */
 package com.android.launcher3.widget.util;
 
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Point;
@@ -91,20 +94,33 @@
      */
     public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Context context,
             int spanX, int spanY) {
-        AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
-        int widgetId = widgetView.getAppWidgetId();
-        if (widgetId <= 0) {
+        updateWidgetSizeRangesAsync(
+                widgetView.getAppWidgetId(), widgetView.getAppWidgetInfo(), context, spanX, spanY);
+    }
+
+    /**
+     * Updates a given {@code widgetId} with size, {@code spanX}, {@code spanY} asynchronously.
+     *
+     * <p>On Android S+, it also updates the given {@code widgetView} with a list of sizes derived
+     * from {@code spanX}, {@code spanY} in all supported device profiles.
+     */
+    public static void updateWidgetSizeRangesAsync(int widgetId,
+            AppWidgetProviderInfo info, Context context, int spanX, int spanY) {
+        if (widgetId <= 0 || info == null) {
             return;
         }
-        Bundle sizeOptions = getWidgetSizeOptions(context, widgetView.getAppWidgetInfo().provider,
-                spanX, spanY);
-        if (sizeOptions.<SizeF>getParcelableArrayList(
-                AppWidgetManager.OPTION_APPWIDGET_SIZES).equals(
-                widgetManager.getAppWidgetOptions(widgetId).<SizeF>getParcelableArrayList(
-                        AppWidgetManager.OPTION_APPWIDGET_SIZES))) {
-            return;
-        }
-        widgetManager.updateAppWidgetOptions(widgetId, sizeOptions);
+
+        UI_HELPER_EXECUTOR.execute(() -> {
+            AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
+            Bundle sizeOptions = getWidgetSizeOptions(context, info.provider, spanX, spanY);
+            if (sizeOptions.<SizeF>getParcelableArrayList(
+                    AppWidgetManager.OPTION_APPWIDGET_SIZES).equals(
+                    widgetManager.getAppWidgetOptions(widgetId).<SizeF>getParcelableArrayList(
+                            AppWidgetManager.OPTION_APPWIDGET_SIZES))) {
+                return;
+            }
+            widgetManager.updateAppWidgetOptions(widgetId, sizeOptions);
+        });
     }
 
     /**