Merge "Fix FastScroller jumping in recyclerView" into main
diff --git a/Android.bp b/Android.bp
index 61042f6..4354b66 100644
--- a/Android.bp
+++ b/Android.bp
@@ -203,6 +203,7 @@
         "animationlib",
         "com_android_launcher3_flags_lib",
         "com_android_wm_shell_flags_lib",
+        "android.appwidget.flags-aconfig-java",
     ],
     sdk_version: "current",
     min_sdk_version: min_launcher3_sdk_version,
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 6d899d9..d379132 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -150,6 +150,13 @@
 }
 
 flag {
+    name: "enable_generated_previews"
+    namespace: "launcher"
+    description: "Enables support for RemoteViews previews in the widget picker."
+    bug: "306546610"
+}
+
+flag {
   name: "enable_categorized_widget_suggestions"
   namespace: "launcher"
   description: "Enables widget suggestions in widget picker to be displayed in categories"
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index 29b24b7..6e7a82a 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.icons.ClockDrawableWrapper;
 import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.util.ArrayList;
@@ -136,10 +137,12 @@
 
             case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING:
                 TestProtocol.sDebugTracing = true;
+                ClockDrawableWrapper.sRunningInTest = true;
                 return response;
 
             case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING:
                 TestProtocol.sDebugTracing = false;
+                ClockDrawableWrapper.sRunningInTest = false;
                 return response;
 
             case TestProtocol.REQUEST_PID: {
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index db46508..2953c8e 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -43,6 +43,12 @@
 
     <uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY" />
 
+    <!--
+    Permission required to access profiles which are otherwise hidden
+    from being visible via APIs, e.g. private profile.
+    -->
+    <uses-permission android:name="android.permission.ACCESS_HIDDEN_PROFILES_FULL" />
+
     <!-- Permission required to start a WidgetPickerActivity. -->
     <permission android:name="${packageName}.permission.START_WIDGET_PICKER_ACTIVITY"
         android:protectionLevel="signature|privileged" />
diff --git a/quickstep/res/layout/keyboard_quick_switch_view.xml b/quickstep/res/layout/keyboard_quick_switch_view.xml
index 5af8d51..3256b0b 100644
--- a/quickstep/res/layout/keyboard_quick_switch_view.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_view.xml
@@ -43,7 +43,7 @@
             android:layout_height="@dimen/keyboard_quick_switch_no_recent_items_icon_size"
             android:layout_marginBottom="@dimen/keyboard_quick_switch_no_recent_items_icon_margin"
             android:src="@drawable/ic_empty_recents"
-            android:tint="?androidprv:attr/materialColorOnSurfaceInverse"
+            android:tint="?androidprv:attr/materialColorOnSurface"
             android:importantForAccessibility="no"
 
             app:layout_constraintVertical_chainStyle="packed"
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 325c255..14f615e 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -105,6 +105,8 @@
     <string name="back_gesture_feedback_cancelled">Make sure you swipe from the right or left edge to the middle of the screen and let go</string>
     <!-- Feedback shown after completing the back gesture step if the user is following the full gesture tutorial flow. [CHAR LIMIT=100] -->
     <string name="back_gesture_feedback_complete_with_overview_follow_up">You learned how to swipe from the right to go back. Next up, learn how to switch apps.</string>
+    <!-- Feedback shown after completing the back gesture step if the user is following the full gesture tutorial flow. [CHAR LIMIT=100] -->
+    <string name="back_gesture_feedback_complete_with_follow_up">You completed the go back gesture. Next up, learn how to switch apps.</string>
     <!-- Feedback shown after completing the back gesture step if the user started this tutorial individually. [CHAR LIMIT=100] -->
     <string name="back_gesture_feedback_complete_without_follow_up">You completed the go back gesture</string>
     <!-- Feedback shown during interactive parts of Back gesture tutorial when the gesture is within the nav bar region. [CHAR LIMIT=100] -->
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index bdc86b2..350c752 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -273,7 +273,7 @@
     </style>
 
     <style name="KeyboardQuickSwitchText.OnBackground" parent="KeyboardQuickSwitchText">
-        <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceInverse</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
     </style>
 
     <style name="GestureTutorialActivity" parent="@style/AppTheme">
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index 436fe3b..68e7824 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -23,6 +23,7 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ClipData;
 import android.content.ClipDescription;
@@ -50,7 +51,6 @@
 /** An Activity that can host Launcher's widget picker. */
 public class WidgetPickerActivity extends BaseActivity {
     private static final String TAG = "WidgetPickerActivity";
-    private static final boolean DEBUG = false;
 
     /**
      * Name of the extra that indicates that a widget being dragged.
@@ -65,13 +65,13 @@
     private static final String EXTRA_DESIRED_WIDGET_WIDTH = "desired_widget_width";
     private static final String EXTRA_DESIRED_WIDGET_HEIGHT = "desired_widget_height";
 
-
     private SimpleDragLayer<WidgetPickerActivity> mDragLayer;
     private WidgetsModel mModel;
     private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> {});
 
     private int mDesiredWidgetWidth;
     private int mDesiredWidgetHeight;
+    private int mWidgetCategoryFilter;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -104,6 +104,10 @@
         mDesiredWidgetHeight =
                 getIntent().getIntExtra(EXTRA_DESIRED_WIDGET_HEIGHT, 0);
 
+        // Defaults to '0' to indicate that there isn't a category filter.
+        mWidgetCategoryFilter =
+                getIntent().getIntExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER, 0);
+
         refreshAndBindWidgets();
     }
 
@@ -199,6 +203,14 @@
             return rejectWidget(widget, "shortcut");
         }
 
+        if (mWidgetCategoryFilter > 0 && (info.widgetCategory & mWidgetCategoryFilter) == 0) {
+            return rejectWidget(
+                    widget,
+                    "doesn't match category filter [filter=%d, widget=%d]",
+                    mWidgetCategoryFilter,
+                    info.widgetCategory);
+        }
+
         if (mDesiredWidgetWidth == 0 && mDesiredWidgetHeight == 0) {
             // Accept the widget if the desired dimensions are unspecified.
             return acceptWidget(widget);
@@ -210,22 +222,18 @@
             if (info.maxResizeWidth > 0 && info.maxResizeWidth < mDesiredWidgetWidth) {
                 return rejectWidget(
                         widget,
-                        String.format(
-                                Locale.ENGLISH,
-                                "maxResizeWidth[%d] < mDesiredWidgetWidth[%d]",
-                                info.maxResizeWidth,
-                                mDesiredWidgetWidth));
+                        "maxResizeWidth[%d] < mDesiredWidgetWidth[%d]",
+                        info.maxResizeWidth,
+                        mDesiredWidgetWidth);
             }
 
             final int minWidth = info.minResizeWidth > 0 ? info.minResizeWidth : info.minWidth;
             if (minWidth > mDesiredWidgetWidth) {
                 return rejectWidget(
                         widget,
-                        String.format(
-                                Locale.ENGLISH,
-                                "minWidth[%d] > mDesiredWidgetWidth[%d]",
-                                minWidth,
-                                mDesiredWidgetWidth));
+                        "minWidth[%d] > mDesiredWidgetWidth[%d]",
+                        minWidth,
+                        mDesiredWidgetWidth);
             }
         }
 
@@ -235,22 +243,18 @@
             if (info.maxResizeHeight > 0 && info.maxResizeHeight < mDesiredWidgetHeight) {
                 return rejectWidget(
                         widget,
-                        String.format(
-                                Locale.ENGLISH,
-                                "maxResizeHeight[%d] < mDesiredWidgetHeight[%d]",
-                                info.maxResizeHeight,
-                                mDesiredWidgetHeight));
+                        "maxResizeHeight[%d] < mDesiredWidgetHeight[%d]",
+                        info.maxResizeHeight,
+                        mDesiredWidgetHeight);
             }
 
             final int minHeight = info.minResizeHeight > 0 ? info.minResizeHeight : info.minHeight;
             if (minHeight > mDesiredWidgetHeight) {
                 return rejectWidget(
                         widget,
-                        String.format(
-                                Locale.ENGLISH,
-                                "minHeight[%d] > mDesiredWidgetHeight[%d]",
-                                minHeight,
-                                mDesiredWidgetHeight));
+                        "minHeight[%d] > mDesiredWidgetHeight[%d]",
+                        minHeight,
+                        mDesiredWidgetHeight);
             }
         }
 
@@ -264,8 +268,11 @@
     }
 
     private static WidgetAcceptabilityVerdict rejectWidget(
-            WidgetItem widget, String rejectionReason) {
-        return new WidgetAcceptabilityVerdict(false, widget.label, rejectionReason);
+            WidgetItem widget, String rejectionReason, Object... args) {
+        return new WidgetAcceptabilityVerdict(
+                false,
+                widget.label,
+                String.format(Locale.ENGLISH, rejectionReason, args));
     }
 
     private static WidgetAcceptabilityVerdict acceptWidget(WidgetItem widget) {
@@ -276,7 +283,7 @@
             boolean isAcceptable, String widgetLabel, String reason) {
         void maybeLogVerdict() {
             // Only log a verdict if a reason is specified.
-            if (DEBUG && !reason.isEmpty()) {
+            if (Log.isLoggable(TAG, Log.DEBUG) && !reason.isEmpty()) {
                 Log.i(TAG, String.format(
                         Locale.ENGLISH,
                         "%s: %s because %s",
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index 037f7a8..694475a 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -77,19 +77,15 @@
     public AppsDividerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        boolean isMainColorDark = Themes.getAttrBoolean(context, R.attr.isMainColorDark);
         mDividerSize = new int[]{
                 getResources().getDimensionPixelSize(R.dimen.all_apps_divider_width),
                 getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height)
         };
 
-        mStrokeColor = ContextCompat.getColor(context, isMainColorDark
-                ? R.color.all_apps_prediction_row_separator_dark
-                : R.color.all_apps_prediction_row_separator);
+        mStrokeColor = ContextCompat.getColor(context, R.color.material_color_outline_variant);
 
-        mAllAppsLabelTextColor = ContextCompat.getColor(context, isMainColorDark
-                ? R.color.all_apps_label_text_dark
-                : R.color.all_apps_label_text);
+        mAllAppsLabelTextColor = ContextCompat.getColor(context,
+                R.color.material_color_on_surface_variant);
 
         mShowAllAppsLabel = !ALL_APPS_VISITED_COUNT.hasReachedMax(context);
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 55deca8..b69f657 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -36,12 +36,14 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
+import static com.android.launcher3.taskbar.TaskbarDragLayerController.TASKBAR_REAPPEAR_DELAY_MS;
 import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
 import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
 
 import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
@@ -77,6 +79,7 @@
 import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.apppairs.AppPairIcon;
 import com.android.launcher3.config.FeatureFlags;
@@ -1377,6 +1380,23 @@
         });
     }
 
+    public void hideTaskbarWhenFolding() {
+        AnimatedFloat alphaAnim = mControllers.taskbarDragLayerController.getTaskbarAlpha();
+        alphaAnim.cancelAnimation();
+        alphaAnim.updateValue(0);
+        ObjectAnimator animator = alphaAnim.animateToValue(1).setDuration(0);
+        animator.setStartDelay(TASKBAR_REAPPEAR_DELAY_MS);
+        animator.start();
+    }
+
+    public void cancelHideTaskbarWhenFolding() {
+        mControllers.taskbarDragLayerController.getTaskbarAlpha().cancelAnimation();
+    }
+
+    public void resetHideTaskbarWhenUnfolding() {
+        mControllers.taskbarDragLayerController.getTaskbarAlpha().updateValue(1);
+    }
+
     protected boolean isUserSetupComplete() {
         return mIsUserSetupComplete;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 3f5402f..74eda24 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -44,6 +44,12 @@
     private static final boolean DEBUG = SystemProperties.getBoolean(
             "persist.debug.draw_taskbar_debug_ui", false);
 
+    // Delay to reset the task bar alpha back to 1 after fading it for transition from unfold to
+    // fold. Normally this is not needed since the new task bar is recreated after fading, but in
+    // case something goes wrong this provides a fallback mechanism to make sure the task bar is
+    // visible after the transition finishes.
+    public static final long TASKBAR_REAPPEAR_DELAY_MS = 2000;
+
     private final TaskbarActivityContext mActivity;
     private final TaskbarDragLayer mTaskbarDragLayer;
     private final int mFolderMargin;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 7c7c426..4dd2f44 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -30,6 +30,7 @@
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING;
 import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
 import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR;
@@ -43,6 +44,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
+import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.DisplayManager;
 import android.net.Uri;
 import android.os.Handler;
@@ -109,6 +111,7 @@
 
     private final Context mContext;
     private final @Nullable Context mNavigationBarPanelContext;
+    private final DeviceStateManager mDeviceStateManager;
     private WindowManager mWindowManager;
     private FrameLayout mTaskbarRootLayout;
     private boolean mAddedWindow;
@@ -175,7 +178,8 @@
         }
     };
 
-    UnfoldTransitionProgressProvider.TransitionProgressListener mUnfoldTransitionProgressListener =
+    private final UnfoldTransitionProgressProvider.TransitionProgressListener
+            mUnfoldTransitionProgressListener =
             new UnfoldTransitionProgressProvider.TransitionProgressListener() {
                 @Override
                 public void onTransitionStarted() {
@@ -204,6 +208,9 @@
                 }
             };
 
+    private final DeviceStateManager.FoldStateListener mFoldStateListener;
+    private Boolean mFolded;
+
     @SuppressLint("WrongConstant")
     public TaskbarManager(TouchInteractionService service) {
         Display display =
@@ -229,6 +236,29 @@
                 }
             };
         }
+        // Temporary solution to mitigate the visual jump from folding the device. Currently, the
+        // screen turns on much earlier than we receive the onConfigurationChanged callback or
+        // receiving the correct device profile. While the ideal the solution is to align turning
+        // the screen on after onConfigurationChanged (by either delaying turning on the screen or
+        // figuring out what is causing the delay in getting onConfigurationChanged callback), one
+        // easy temporary mitigation is to dimming the bar so that the visual jump isn't as glaring.
+        mFoldStateListener = new DeviceStateManager.FoldStateListener(mContext, folded -> {
+            boolean firstTime = mFolded == null;
+            if (mTaskbarActivityContext == null) {
+                return;
+            }
+            if (!firstTime && mFolded.booleanValue() != folded) {
+                mTaskbarActivityContext.cancelHideTaskbarWhenFolding();
+            }
+            mFolded = folded;
+            if (folded && !firstTime) {
+                mTaskbarActivityContext.hideTaskbarWhenFolding();
+            } else {
+                mTaskbarActivityContext.resetHideTaskbarWhenUnfolding();
+            }
+        });
+        mDeviceStateManager = mContext.getSystemService(DeviceStateManager.class);
+        mDeviceStateManager.registerCallback(MAIN_EXECUTOR, mFoldStateListener);
         mNavButtonController = new TaskbarNavButtonController(service,
                 SystemUiProxy.INSTANCE.get(mContext), new Handler(),
                 AssistUtils.newInstance(mContext));
@@ -588,6 +618,7 @@
         Log.d(TASKBAR_NOT_DESTROYED_TAG, "unregistering component callbacks from destroy().");
         mContext.unregisterComponentCallbacks(mComponentCallbacks);
         mContext.unregisterReceiver(mShutdownReceiver);
+        mDeviceStateManager.unregisterCallback(mFoldStateListener);
     }
 
     public @Nullable TaskbarActivityContext getCurrentActivityContext() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
index 41c3dec..73c71c8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
@@ -185,20 +185,23 @@
         }
         mMagnetizedObject.setMagnetListener(new MagnetizedObject.MagnetListener() {
             @Override
-            public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+            public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target,
+                    @NonNull MagnetizedObject<?> draggedObject) {
                 if (mAnimator == null) return;
                 mAnimator.animateDismissCaptured();
             }
 
             @Override
             public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
+                    @NonNull MagnetizedObject<?> draggedObject,
                     float velX, float velY, boolean wasFlungOut) {
                 if (mAnimator == null) return;
                 mAnimator.animateDismissReleased();
             }
 
             @Override
-            public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+            public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target,
+                    @NonNull MagnetizedObject<?> draggedObject) {
                 dismissMagnetizedObject();
             }
         });
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index ca598c8..fc3eeba 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -38,6 +38,7 @@
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 
+import com.android.internal.jank.Cuj;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
@@ -53,6 +54,7 @@
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.OverviewToHomeAnim;
 import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
 import java.util.function.Consumer;
 
@@ -148,6 +150,8 @@
         mMotionPauseDetector.clear();
 
         if (handlingOverviewAnim()) {
+            InteractionJankMonitorWrapper.begin(mRecentsView, Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS,
+                    "Home");
             mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected);
         }
 
@@ -182,6 +186,7 @@
         if (mStartedOverview) {
             goToOverviewOrHomeOnDragEnd(velocity);
         } else {
+            InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
             super.onDragEnd(velocity);
         }
 
@@ -237,6 +242,7 @@
 
     private void maybeSwipeInteractionToOverviewComplete() {
         if (mReachedOverview && !mDetector.isDraggingState()) {
+            InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
             onSwipeInteractionCompleted(OVERVIEW);
         }
     }
@@ -280,6 +286,7 @@
         if (goToHomeInsteadOfOverview) {
             new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(NORMAL), null)
                     .animateWithVelocity(velocity);
+            InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
         }
         if (mReachedOverview) {
             float distanceDp = dpiFromPx(Math.max(
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 6d3b60a..b7a907f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -193,6 +193,8 @@
         mMotionPauseDetector.clear();
         if (start) {
             InteractionJankMonitorWrapper.begin(mRecentsView, Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
+            InteractionJankMonitorWrapper.begin(mRecentsView, Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS,
+                    "Home");
 
             mStartState = mLauncher.getStateManager().getState();
 
@@ -350,6 +352,7 @@
                     .dispatchOnStart();
             return;
         }
+        InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
 
         final LauncherState targetState;
         if (horizontalFling && verticalFling) {
@@ -471,6 +474,8 @@
 
         if (targetState == QUICK_SWITCH_FROM_HOME) {
             InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
+        } else if (targetState == OVERVIEW) {
+            InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
         }
 
         mLauncher.getStateManager().goToState(targetState, false, forEndCallback(this::clearState));
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 404bca9..6757cd8 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -85,7 +85,9 @@
     public int getSuccessFeedbackSubtitle() {
         return mTutorialFragment.isAtFinalStep()
                 ? R.string.back_gesture_feedback_complete_without_follow_up
-                : R.string.back_gesture_feedback_complete_with_overview_follow_up;
+                : ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
+                        ? R.string.back_gesture_feedback_complete_with_follow_up
+                        : R.string.back_gesture_feedback_complete_with_overview_follow_up;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
index 278ca56..1e05a69 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
@@ -67,15 +67,15 @@
     /**
      * Adds a log to be printed at log-dump-time.
      */
-    public void addLog(String event) {
+    public void addLog(@NonNull String event) {
         addLog(event, null);
     }
 
-    public void addLog(String event, int extras) {
+    public void addLog(@NonNull String event, int extras) {
         addLog(event, extras, null);
     }
 
-    public void addLog(String event, boolean extras) {
+    public void addLog(@NonNull String event, boolean extras) {
         addLog(event, extras, null);
     }
 
@@ -85,30 +85,30 @@
      * @param gestureEvent GestureEvent representing the event being logged.
      */
     public void addLog(
-            String event, @Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
+            @NonNull String event, @Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
         addLog(new CompoundString(event), gestureEvent);
     }
 
     public void addLog(
-            String event,
+            @NonNull String event,
             int extras,
             @Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
         addLog(new CompoundString(event).append(": ").append(extras), gestureEvent);
     }
 
     public void addLog(
-            String event,
+            @NonNull String event,
             boolean extras,
             @Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
         addLog(new CompoundString(event).append(": ").append(extras), gestureEvent);
     }
 
-    public void addLog(CompoundString compoundString) {
+    public void addLog(@NonNull CompoundString compoundString) {
         addLog(compoundString, null);
     }
 
     public void addLog(
-            CompoundString compoundString,
+            @NonNull CompoundString compoundString,
             @Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
         EventLog lastEventLog = logs[(nextIndex + logs.length - 1) % logs.length];
         if (lastEventLog == null || mCurrentLogId != lastEventLog.logId) {
@@ -259,21 +259,20 @@
 
         public CompoundString(String substring) {
             mIsNoOp = substring == null;
-            if (mIsNoOp) {
-                mSubstrings = null;
-                mArgs = null;
-                return;
+            mSubstrings = mIsNoOp ? null : new ArrayList<>();
+            mArgs = mIsNoOp ? null : new ArrayList<>();
+
+            if (!mIsNoOp) {
+                mSubstrings.add(substring);
             }
-            mSubstrings = new ArrayList<>();
-            mSubstrings.add(substring);
-            mArgs = new ArrayList<>();
         }
 
         public CompoundString append(CompoundString substring) {
-            if (mIsNoOp) {
+            if (mIsNoOp || substring.mIsNoOp) {
                 return this;
             }
             mSubstrings.addAll(substring.mSubstrings);
+            mArgs.addAll(substring.mArgs);
 
             return this;
         }
@@ -288,30 +287,53 @@
         }
 
         public CompoundString append(int num) {
+            if (mIsNoOp) {
+                return this;
+            }
+            mArgs.add(num);
+
+            return append("%d");
+        }
+
+        public CompoundString append(long num) {
+            if (mIsNoOp) {
+                return this;
+            }
             mArgs.add(num);
 
             return append("%d");
         }
 
         public CompoundString append(float num) {
+            if (mIsNoOp) {
+                return this;
+            }
             mArgs.add(num);
 
             return append("%.2f");
         }
 
         public CompoundString append(double num) {
+            if (mIsNoOp) {
+                return this;
+            }
             mArgs.add(num);
 
             return append("%.2f");
         }
 
         public CompoundString append(boolean bool) {
+            if (mIsNoOp) {
+                return this;
+            }
             mArgs.add(bool);
 
             return append("%b");
         }
 
-        public Object[] getArgs() {
+        private Object[] getArgs() {
+            Preconditions.assertTrue(!mIsNoOp);
+
             return mArgs.toArray();
         }
 
@@ -320,7 +342,7 @@
             return String.format(toUnformattedString(), getArgs());
         }
 
-        public String toUnformattedString() {
+        private String toUnformattedString() {
             Preconditions.assertTrue(!mIsNoOp);
 
             StringBuilder sb = new StringBuilder();
@@ -333,7 +355,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(mIsNoOp, mSubstrings);
+            return Objects.hash(mIsNoOp, mSubstrings, mArgs);
         }
 
         @Override
@@ -342,7 +364,9 @@
                 return false;
             }
             CompoundString other = (CompoundString) obj;
-            return (mIsNoOp == other.mIsNoOp) && Objects.equals(mSubstrings, other.mSubstrings);
+            return (mIsNoOp == other.mIsNoOp)
+                    && Objects.equals(mSubstrings, other.mSubstrings)
+                    && Objects.equals(mArgs, other.mArgs);
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index a8a96ce..b8bc828 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -96,8 +96,14 @@
         mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
         mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
         mForcePauseTimeout = new Alarm();
-        mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */,
-                "Force pause timeout after " +  alarm.getLastSetTimeout() + "ms" /* reason */));
+        mForcePauseTimeout.setOnAlarmListener(alarm -> {
+            ActiveGestureLog.CompoundString log =
+                    new ActiveGestureLog.CompoundString("Force pause timeout after ")
+                            .append(alarm.getLastSetTimeout())
+                            .append("ms");
+            addLogs(log);
+            updatePaused(true /* isPaused */, log);
+        });
         mMakePauseHarderToTrigger = makePauseHarderToTrigger;
         mVelocityProvider = new SystemVelocityProvider(axis);
     }
@@ -113,8 +119,14 @@
      * @param disallowPause If true, we will not detect any pauses until this is set to false again.
      */
     public void setDisallowPause(boolean disallowPause) {
+        ActiveGestureLog.CompoundString log =
+                new ActiveGestureLog.CompoundString("Set disallowPause=")
+                        .append(disallowPause);
+        if (mDisallowPause != disallowPause) {
+            addLogs(log);
+        }
         mDisallowPause = disallowPause;
-        updatePaused(mIsPaused, "Set disallowPause=" + disallowPause);
+        updatePaused(mIsPaused, log);
     }
 
     /**
@@ -148,27 +160,30 @@
         float speed = Math.abs(velocity);
         float previousSpeed = Math.abs(prevVelocity);
         boolean isPaused;
-        String isPausedReason = "";
+        ActiveGestureLog.CompoundString isPausedReason;
         if (mIsPaused) {
             // Continue to be paused until moving at a fast speed.
             isPaused = speed < mSpeedFast || previousSpeed < mSpeedFast;
-            isPausedReason = "Was paused, but started moving at a fast speed";
+            isPausedReason = new ActiveGestureLog.CompoundString(
+                    "Was paused, but started moving at a fast speed");
         } else {
             if (velocity < 0 != prevVelocity < 0) {
                 // We're just changing directions, not necessarily stopping.
                 isPaused = false;
-                isPausedReason = "Velocity changed directions";
+                isPausedReason = new ActiveGestureLog.CompoundString("Velocity changed directions");
             } else {
                 isPaused = speed < mSpeedVerySlow && previousSpeed < mSpeedVerySlow;
-                isPausedReason = "Pause requires back to back slow speeds";
+                isPausedReason = new ActiveGestureLog.CompoundString(
+                        "Pause requires back to back slow speeds");
                 if (!isPaused && !mHasEverBeenPaused) {
                     // We want to be more aggressive about detecting the first pause to ensure it
                     // feels as responsive as possible; getting two very slow speeds back to back
                     // takes too long, so also check for a rapid deceleration.
                     boolean isRapidDeceleration = speed < previousSpeed * RAPID_DECELERATION_FACTOR;
                     isPaused = isRapidDeceleration && speed < mSpeedSomewhatFast;
-                    isPausedReason = "Didn't have back to back slow speeds, checking for rapid"
-                            + " deceleration on first pause only";
+                    isPausedReason = new ActiveGestureLog.CompoundString(
+                            "Didn't have back to back slow speeds, checking for rapid ")
+                            .append(" deceleration on first pause only");
                 }
                 if (mMakePauseHarderToTrigger) {
                     if (speed < mSpeedSlow) {
@@ -176,12 +191,14 @@
                             mSlowStartTime = time;
                         }
                         isPaused = time - mSlowStartTime >= HARDER_TRIGGER_TIMEOUT;
-                        isPausedReason = "Maintained slow speed for sufficient duration when making"
-                                + " pause harder to trigger";
+                        isPausedReason = new ActiveGestureLog.CompoundString(
+                                "Maintained slow speed for sufficient duration when making")
+                                .append(" pause harder to trigger");
                     } else {
                         mSlowStartTime = 0;
                         isPaused = false;
-                        isPausedReason = "Intentionally making pause harder to trigger";
+                        isPausedReason = new ActiveGestureLog.CompoundString(
+                                "Intentionally making pause harder to trigger");
                     }
                 }
             }
@@ -189,18 +206,21 @@
         updatePaused(isPaused, isPausedReason);
     }
 
-    private void updatePaused(boolean isPaused, String reason) {
+    private void updatePaused(boolean isPaused, ActiveGestureLog.CompoundString reason) {
         if (mDisallowPause) {
-            reason = "Disallow pause; otherwise, would have been " + isPaused + " due to " + reason;
+            reason = new ActiveGestureLog.CompoundString(
+                    "Disallow pause; otherwise, would have been ")
+                    .append(isPaused)
+                    .append(" due to reason:")
+                    .append(reason);
             isPaused = false;
         }
         if (mIsPaused != isPaused) {
             mIsPaused = isPaused;
-            String logString = "onMotionPauseChanged, paused=" + mIsPaused + " reason=" + reason;
-            if (Utilities.isRunningInTestHarness()) {
-                Log.d(TAG, logString);
-            }
-            ActiveGestureLog.INSTANCE.addLog(logString);
+            addLogs(new ActiveGestureLog.CompoundString("onMotionPauseChanged triggered; paused=")
+                    .append(mIsPaused)
+                    .append(", reason=")
+                    .append(reason));
             boolean isFirstDetectedPause = !mHasEverBeenPaused && mIsPaused;
             if (mIsPaused) {
                 AccessibilityManagerCompat.sendTestProtocolEventToTest(mContext,
@@ -219,6 +239,16 @@
         }
     }
 
+    private void addLogs(ActiveGestureLog.CompoundString compoundString) {
+        ActiveGestureLog.CompoundString logString =
+                new ActiveGestureLog.CompoundString("MotionPauseDetector: ")
+                        .append(compoundString);
+        if (Utilities.isRunningInTestHarness()) {
+            Log.d(TAG, logString.toString());
+        }
+        ActiveGestureLog.INSTANCE.addLog(logString);
+    }
+
     public void clear() {
         mVelocityProvider.clear();
         mPreviousVelocity = null;
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index b549058..8281ad7 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -20,6 +20,7 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
@@ -41,6 +42,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.stream.Collectors;
 
 /**
  * View for showing action buttons in Overview
@@ -220,6 +223,17 @@
         boolean shouldBeVisible = mSplitButtonHiddenFlags == 0;
         mSplitButton.setVisibility(shouldBeVisible ? VISIBLE : GONE);
         findViewById(R.id.action_split_space).setVisibility(shouldBeVisible ? VISIBLE : GONE);
+
+        String callStack = Arrays.stream(
+                        Log.getStackTraceString(new Exception("thread stacktrace"))
+                                .split("\\n"))
+                .limit(5)
+                .skip(1) // Removes the line "java.lang.Exception: thread stacktrace"
+                .collect(Collectors.joining("\n"));
+        Log.d("b/321291049", "updateSplitButtonHiddenFlags called with flag: " + flag
+                + " enabled: " + enable
+                + " shouldBeVisible: " + shouldBeVisible
+                + " partial trace: \n" + callStack);
     }
 
     /**
diff --git a/res/drawable/ic_install_to_private.xml b/res/drawable/ic_install_to_private.xml
index 7f00f8d..0e9833c 100644
--- a/res/drawable/ic_install_to_private.xml
+++ b/res/drawable/ic_install_to_private.xml
@@ -18,11 +18,14 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
-    android:viewportWidth="960"
-    android:viewportHeight="960"
-    android:tint="?attr/colorControlNormal">
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?android:attr/textColorPrimary">
 
     <path
         android:fillColor="@android:color/white"
-        android:pathData="M420,600L540,600L517,471Q537,461 548.5,442Q560,423 560,400Q560,367 536.5,343.5Q513,320 480,320Q447,320 423.5,343.5Q400,367 400,400Q400,423 411.5,442Q423,461 443,471L420,600ZM480,880Q341,845 250.5,720.5Q160,596 160,444L160,200L480,80L800,200L800,444Q800,596 709.5,720.5Q619,845 480,880ZM480,796Q584,763 652,664Q720,565 720,444L720,255L480,165L240,255L240,444Q240,565 308,664Q376,763 480,796ZM480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Z"/>
+        android:pathData="M19,5V19H5V5H19ZM19,3H5C3.9,3 3,3.9 3,5V19C3,20.1 3.9,21 5,21H19C20.1,21 21,20.1 21,19V5C21,3.9 20.1,3 19,3Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12.93,12.27L13.5,15.5H10.5L11.07,12.27C10.43,11.94 10,11.27 10,10.5C10,9.4 10.9,8.5 12,8.5C13.1,8.5 14,9.4 14,10.5C14,11.27 13.57,11.94 12.93,12.27Z" />
 </vector>
diff --git a/res/layout/widgets_two_pane_sheet.xml b/res/layout/widgets_two_pane_sheet.xml
index cd7f2e1..f692e24 100644
--- a/res/layout/widgets_two_pane_sheet.xml
+++ b/res/layout/widgets_two_pane_sheet.xml
@@ -116,6 +116,7 @@
                         android:clipToOutline="true"
                         android:paddingBottom="36dp"
                         android:background="@drawable/widgets_surface_background"
+                        android:importantForAccessibility="yes"
                         android:id="@+id/right_pane">
                         <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
                             android:id="@+id/recommended_widget_table"
diff --git a/res/values-ldrtl/strings.xml b/res/values-ldrtl/strings.xml
new file mode 100644
index 0000000..118b499
--- /dev/null
+++ b/res/values-ldrtl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+/*
+* Copyright (C) 2008 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- General -->
+    <skip />
+
+    <!-- accessibilityPaneTitle for the right pane when showing suggested widgets. -->
+    <string name="widget_picker_right_pane_accessibility_title"><xliff:g id="selected_header" example="Calendar">%1$s</xliff:g> widgets on left, search and options on right</string>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5cc4616..a4e7ec4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -77,6 +77,8 @@
     <string name="fitness_widget_recommendation_category_label">Reach Your Fitness Goals</string>
     <string name="weather_widget_recommendation_category_label">Stay Ahead of the Weather</string>
     <string name="others_widget_recommendation_category_label">You Might Also Like</string>
+    <!-- accessibilityPaneTitle for the right pane when showing suggested widgets. -->
+    <string name="widget_picker_right_pane_accessibility_title"><xliff:g id="selected_header" example="Calendar">%1$s</xliff:g> widgets on right, search and options on left</string>
     <!-- Label for showing the number of widgets an app has in the full widgets picker.
          [CHAR_LIMIT=25][ICU SYNTAX] -->
     <string name="widgets_count">
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index d124746..99fca62 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -441,17 +441,35 @@
             @Override
             public void execute(@NonNull final LauncherAppState app,
                     @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
+                IconCache iconCache = app.getIconCache();
                 final IntSet removedIds = new IntSet();
+                HashSet<WorkspaceItemInfo> archivedItemsToCacheRefresh = new HashSet<>();
+                HashSet<String> archivedPackagesToCacheRefresh = new HashSet<>();
                 synchronized (dataModel) {
                     for (ItemInfo info : dataModel.itemsIdMap) {
                         if (info instanceof WorkspaceItemInfo
                                 && ((WorkspaceItemInfo) info).hasPromiseIconUi()
                                 && user.equals(info.user)
-                                && info.getIntent() != null
-                                && TextUtils.equals(packageName, info.getIntent().getPackage())) {
-                            removedIds.add(info.id);
+                                && info.getIntent() != null) {
+                            if (TextUtils.equals(packageName, info.getIntent().getPackage())) {
+                                removedIds.add(info.id);
+                            }
+                            if (((WorkspaceItemInfo) info).isArchived()) {
+                                WorkspaceItemInfo workspaceItem = (WorkspaceItemInfo) info;
+                                // Remove package cache icon for archived app in case of a session
+                                // failure.
+                                mApp.getIconCache().removeIconsForPkg(packageName, user);
+                                // Refresh icons on the workspace for archived apps.
+                                iconCache.getTitleAndIcon(workspaceItem,
+                                        workspaceItem.usingLowResIcon());
+                                archivedPackagesToCacheRefresh.add(packageName);
+                                archivedItemsToCacheRefresh.add(workspaceItem);
+                            }
                         }
                     }
+                    if (!archivedPackagesToCacheRefresh.isEmpty()) {
+                        apps.updateIconsAndLabels(archivedPackagesToCacheRefresh, user);
+                    }
                 }
 
                 if (!removedIds.isEmpty()) {
@@ -459,6 +477,10 @@
                             ItemInfoMatcher.ofItemIds(removedIds),
                             "removed because install session failed");
                 }
+                if (!archivedItemsToCacheRefresh.isEmpty()) {
+                    bindUpdatedWorkspaceItems(archivedItemsToCacheRefresh.stream().toList());
+                    bindApplicationsIfNeeded();
+                }
             }
         });
     }
diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt
index 5172999..f6bc1f1 100644
--- a/src/com/android/launcher3/ModelCallbacks.kt
+++ b/src/com/android/launcher3/ModelCallbacks.kt
@@ -178,7 +178,10 @@
         val hadWorkApps = launcher.appsView.shouldShowTabs()
         launcher.appsView.appsStore.setApps(apps, flags, packageUserKeytoUidMap)
         PopupContainerWithArrow.dismissInvalidPopup(launcher)
-        if (hadWorkApps != launcher.appsView.shouldShowTabs()) {
+        if (
+            hadWorkApps != launcher.appsView.shouldShowTabs() &&
+                launcher.stateManager.state == LauncherState.ALL_APPS
+        ) {
             launcher.stateManager.goToState(LauncherState.NORMAL)
         }
     }
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 051cf50..009a2aa6 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -93,11 +93,14 @@
      * Sets the current set of apps and sets mapping for {@link PackageUserKey} to Uid for
      * the current set of apps.
      *
-     * <p> Note that shouldPreinflate param should be set to {@code false} for taskbar, because this
-     * method is too late to preinflate all apps, as user will open all apps in the same frame.
+     * <p> Note that shouldPreinflate param should be set to {@code false} for taskbar, because
+     * this method is too late to preinflate all apps, as user will open all apps in the frame
+     *
+     * <p>Param: apps are required to be sorted using the comparator COMPONENT_KEY_COMPARATOR
+     * in order to enable binary search on the mApps store
      */
     public void setApps(@Nullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map,
-            boolean shouldPreinflate)  {
+            boolean shouldPreinflate) {
         mApps = apps == null ? EMPTY_ARRAY : apps;
         mModelFlags = flags;
         notifyUpdate();
diff --git a/src/com/android/launcher3/allapps/AppInfoComparator.java b/src/com/android/launcher3/allapps/AppInfoComparator.java
index 311a40e..a0867db 100644
--- a/src/com/android/launcher3/allapps/AppInfoComparator.java
+++ b/src/com/android/launcher3/allapps/AppInfoComparator.java
@@ -43,9 +43,7 @@
     @Override
     public int compare(AppInfo a, AppInfo b) {
         // Order by the title in the current locale
-        int result = mLabelComparator.compare(
-                a.title == null ? "" : a.title.toString(),
-                b.title == null ? "" : b.title.toString());
+        int result = mLabelComparator.compare(getSortingTitle(a), getSortingTitle(b));
         if (result != 0) {
             return result;
         }
@@ -64,4 +62,14 @@
             return aUserSerial.compareTo(bUserSerial);
         }
     }
+
+    private String getSortingTitle(AppInfo info) {
+        if (info.appTitle != null) {
+            return info.appTitle.toString();
+        }
+        if (info.title != null) {
+            return info.title.toString();
+        }
+        return "";
+    }
 }
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 2f7f51e..d8fa90a 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -219,7 +219,19 @@
         CacheEntry entry = cacheLocked(application.componentName,
                 application.user, () -> null, mLauncherActivityInfoCachingLogic,
                 false, application.usingLowResIcon());
-        if (entry.bitmap != null && !isDefaultIcon(entry.bitmap, application.user)) {
+        if (entry.bitmap == null || isDefaultIcon(entry.bitmap, application.user)) {
+            return;
+        }
+
+        boolean preferPackageIcon = application.isArchived();
+        if (preferPackageIcon) {
+            String packageName = application.getTargetPackage();
+            CacheEntry packageEntry =
+                    cacheLocked(new ComponentName(packageName, packageName + EMPTY_CLASS_NAME),
+                            application.user, () -> null, mLauncherActivityInfoCachingLogic,
+                            false, application.usingLowResIcon());
+            applyPackageEntry(packageEntry, application, entry);
+        } else {
             applyCacheEntry(entry, application);
         }
     }
@@ -227,10 +239,14 @@
     /**
      * Fill in {@param info} with the icon and label for {@param activityInfo}
      */
+    @SuppressWarnings("NewApi")
     public synchronized void getTitleAndIcon(ItemInfoWithIcon info,
             LauncherActivityInfo activityInfo, boolean useLowResIcon) {
+        boolean isAppArchived = Utilities.enableSupportForArchiving() && activityInfo != null
+                && activityInfo.getActivityInfo().isArchived;
         // If we already have activity info, no need to use package icon
-        getTitleAndIcon(info, () -> activityInfo, false, useLowResIcon);
+        getTitleAndIcon(info, () -> activityInfo, isAppArchived, useLowResIcon,
+                isAppArchived);
     }
 
     /**
@@ -309,7 +325,7 @@
         } else {
             Intent intent = info.getIntent();
             getTitleAndIcon(info, () -> mLauncherApps.resolveActivity(intent, info.user),
-                    true, useLowResIcon);
+                    true, useLowResIcon, info.isArchived());
         }
     }
 
@@ -334,6 +350,28 @@
     }
 
     /**
+     * Fill in {@param mWorkspaceItemInfo} with the icon and label for {@param info}
+     */
+    public synchronized void getTitleAndIcon(
+            @NonNull ItemInfoWithIcon infoInOut,
+            @NonNull Supplier<LauncherActivityInfo> activityInfoProvider,
+            boolean usePkgIcon, boolean useLowResIcon, boolean preferPackageEntry) {
+        CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), infoInOut.user,
+                activityInfoProvider, mLauncherActivityInfoCachingLogic, usePkgIcon,
+                useLowResIcon);
+        if (preferPackageEntry) {
+            String packageName = infoInOut.getTargetPackage();
+            CacheEntry packageEntry = cacheLocked(
+                    new ComponentName(packageName, packageName + EMPTY_CLASS_NAME),
+                    infoInOut.user, activityInfoProvider, mLauncherActivityInfoCachingLogic,
+                    usePkgIcon, useLowResIcon);
+            applyPackageEntry(packageEntry, infoInOut, entry);
+        } else {
+            applyCacheEntry(entry, infoInOut);
+        }
+    }
+
+    /**
      * Creates an sql cursor for a query of a set of ItemInfoWithIcon icons and titles.
      *
      * @param iconRequestInfos List of IconRequestInfos representing titles and icons to query.
@@ -551,6 +589,19 @@
         }
     }
 
+    protected void applyPackageEntry(@NonNull final CacheEntry packageEntry,
+            @NonNull final ItemInfoWithIcon info, @NonNull final CacheEntry fallbackEntry) {
+        info.title = Utilities.trim(packageEntry.title);
+        info.appTitle = Utilities.trim(fallbackEntry.title);
+        info.contentDescription = packageEntry.contentDescription;
+        info.bitmap = packageEntry.bitmap;
+        if (packageEntry.bitmap == null) {
+            // TODO: entry.bitmap can never be null, so this should not happen at all.
+            Log.wtf(TAG, "Cannot find bitmap from the cache, default icon was loaded.");
+            info.bitmap = getDefaultIcon(info.user);
+        }
+    }
+
     public Drawable getFullResIcon(LauncherActivityInfo info) {
         return mIconProvider.getIcon(info, mIconDpi);
     }
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index c99b889..ddf4023 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -1,5 +1,9 @@
 package com.android.launcher3.model;
 
+import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN;
+import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD;
+import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX;
+
 import static com.android.launcher3.Utilities.ATLEAST_S;
 
 import android.annotation.SuppressLint;
@@ -7,13 +11,19 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.util.SparseArray;
+import android.widget.RemoteViews;
 
+import androidx.core.os.BuildCompat;
+
+import com.android.launcher3.Flags;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.WidgetManagerHelper;
 
 /**
  * An wrapper over various items displayed in a widget picker,
@@ -28,9 +38,11 @@
     public final String label;
     public final CharSequence description;
     public final int spanX, spanY;
+    public final SparseArray<RemoteViews> generatedPreviews;
 
     public WidgetItem(LauncherAppWidgetProviderInfo info,
-            InvariantDeviceProfile idp, IconCache iconCache, Context context) {
+            InvariantDeviceProfile idp, IconCache iconCache, Context context,
+            WidgetManagerHelper helper) {
         super(info.provider, info.getProfile());
 
         label = iconCache.getTitleNoCache(info);
@@ -40,6 +52,27 @@
 
         spanX = Math.min(info.spanX, idp.numColumns);
         spanY = Math.min(info.spanY, idp.numRows);
+
+        if (BuildCompat.isAtLeastV() && Flags.enableGeneratedPreviews()) {
+            generatedPreviews = new SparseArray<>(3);
+            for (int widgetCategory : new int[] {
+                    WIDGET_CATEGORY_HOME_SCREEN,
+                    WIDGET_CATEGORY_KEYGUARD,
+                    WIDGET_CATEGORY_SEARCHBOX,
+            }) {
+                if ((widgetCategory & widgetInfo.generatedPreviewCategories) != 0) {
+                    generatedPreviews.put(widgetCategory,
+                            helper.loadGeneratedPreview(widgetInfo, widgetCategory));
+                }
+            }
+        } else {
+            generatedPreviews = null;
+        }
+    }
+
+    public WidgetItem(LauncherAppWidgetProviderInfo info,
+            InvariantDeviceProfile idp, IconCache iconCache, Context context) {
+        this(info, idp, iconCache, context, new WidgetManagerHelper(context));
     }
 
     public WidgetItem(ShortcutConfigActivityInfo info, IconCache iconCache, PackageManager pm) {
@@ -50,6 +83,7 @@
         widgetInfo = null;
         activityInfo = info;
         spanX = spanY = 1;
+        generatedPreviews = null;
     }
 
     /**
@@ -78,4 +112,15 @@
     public boolean isShortcut() {
         return activityInfo != null;
     }
+
+    /**
+     * Returns whether this {@link WidgetItem} has a generated preview for the given widget
+     * category.
+     */
+    public boolean hasGeneratedPreview(int widgetCategory) {
+        if (!Flags.enableGeneratedPreviews() || generatedPreviews == null) {
+            return false;
+        }
+        return generatedPreviews.contains(widgetCategory);
+    }
 }
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index 88b98aa..287c29e 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -202,7 +202,7 @@
                             }
                         }
                     }
-                    pmHelper.isAppOnSdcard(targetPkg!!, c.user) -> {
+                    pmHelper.isAppOnSdcard(targetPkg, c.user) -> {
                         // Package is present but not available.
                         disabledState =
                             disabledState or WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE
@@ -278,7 +278,7 @@
                 info = c.loadSimpleWorkspaceItem()
 
                 // Shortcuts are only available on the primary profile
-                if (!TextUtils.isEmpty(targetPkg) && pmHelper.isAppSuspended(targetPkg!!, c.user)) {
+                if (!TextUtils.isEmpty(targetPkg) && pmHelper.isAppSuspended(targetPkg, c.user)) {
                     disabledState = disabledState or ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED
                 }
                 info.options = c.options
@@ -333,13 +333,12 @@
                         info.runtimeStatusFlags and
                             ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE.inv()
                 } else if (
-                    activityInfo ==
-                        null // For archived apps, include progress info in case there is
-                    // a pending install session post restart of device.
-                    ||
+                    activityInfo == null ||
                         (Utilities.enableSupportForArchiving() &&
                             activityInfo.applicationInfo.isArchived)
                 ) {
+                    // For archived apps, include progress info in case there is
+                    // a pending install session post restart of device.
                     val installProgress = (si.getProgress() * 100).toInt()
                     info.setProgressLevel(installProgress, PackageInstallInfo.STATUS_INSTALLING)
                 }
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 86393a0..55849c2 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -160,6 +160,13 @@
     public CharSequence title;
 
     /**
+     * Optionally set: The appTitle might e.g. be different if {@code title} is used to
+     * display progress (e.g. Downloading..).
+     */
+    @Nullable
+    public CharSequence appTitle;
+
+    /**
      * Content description of the item.
      */
     @Nullable
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 8f5e2b6..c75f9d1 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.widget;
 
+import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN;
+
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
 import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.fromProviderInfo;
 import static com.android.launcher3.widget.util.WidgetSizes.getWidgetItemSizePx;
@@ -44,6 +46,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.CheckLongPressHelper;
+import com.android.launcher3.Flags;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.FastBitmapDrawable;
@@ -241,6 +244,11 @@
             mAppWidgetHostViewPreview = createAppWidgetHostView(context);
             setAppWidgetHostViewPreview(mAppWidgetHostViewPreview, item.widgetInfo,
                     mRemoteViewsPreview);
+        } else if (Flags.enableGeneratedPreviews()
+                && item.hasGeneratedPreview(WIDGET_CATEGORY_HOME_SCREEN)) {
+            mAppWidgetHostViewPreview = createAppWidgetHostView(context);
+            setAppWidgetHostViewPreview(mAppWidgetHostViewPreview, item.widgetInfo,
+                    item.generatedPreviews.get(WIDGET_CATEGORY_HOME_SCREEN));
         } else if (item.hasPreviewLayout()) {
             // If the context is a Launcher activity, DragView will show mAppWidgetHostViewPreview
             // as a preview during drag & drop. And thus, we should use LauncherAppWidgetHostView,
diff --git a/src/com/android/launcher3/widget/WidgetManagerHelper.java b/src/com/android/launcher3/widget/WidgetManagerHelper.java
index 058523b..52767a4 100644
--- a/src/com/android/launcher3/widget/WidgetManagerHelper.java
+++ b/src/com/android/launcher3/widget/WidgetManagerHelper.java
@@ -24,8 +24,11 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.widget.RemoteViews;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -130,6 +133,23 @@
                 appWidgetId).getBoolean(WIDGET_OPTION_RESTORE_COMPLETED);
     }
 
+
+    /**
+     * Load RemoteViews preview for this provider if available.
+     *
+     * @param info The provider info for the widget you want to preview.
+     * @param widgetCategory The widget category for which you want to display previews.
+     *
+     * @return Returns the widget preview that matches selected category, if available.
+     */
+    @Nullable
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    public RemoteViews loadGeneratedPreview(@NonNull AppWidgetProviderInfo info,
+            int widgetCategory) {
+        if (!android.appwidget.flags.Flags.generatedPreviews()) return null;
+        return mAppWidgetManager.getWidgetPreview(info.provider, info.getProfile(), widgetCategory);
+    }
+
     private static Stream<AppWidgetProviderInfo> allWidgetsSteam(Context context) {
         AppWidgetManager awm = context.getSystemService(AppWidgetManager.class);
         return Stream.concat(
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 28bae59..4e704fd 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -20,15 +20,16 @@
 import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.LauncherPrefs.WIDGETS_EDUCATION_DIALOG_SEEN;
+import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
 
 import android.animation.Animator;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Build;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -99,7 +100,6 @@
     // resolution or landscape on phone. This ratio defines the max percentage of content area that
     // the table can display.
     private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.75f;
-
     private final UserCache mUserCache;
     private final UserManagerState mUserManagerState = new UserManagerState();
     private final UserHandle mCurrentUser = Process.myUserHandle();
@@ -522,11 +522,13 @@
     }
 
     @Override
-    public void enterSearchMode() {
+    public void enterSearchMode(boolean shouldLog) {
         if (mIsInSearchMode) return;
         setViewVisibilityBasedOnSearch(/*isInSearchMode= */ true);
         attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView);
-        mActivityContext.getStatsLogManager().logger().log(LAUNCHER_WIDGETSTRAY_SEARCHED);
+        if (shouldLog) {
+            mActivityContext.getStatsLogManager().logger().log(LAUNCHER_WIDGETSTRAY_SEARCHED);
+        }
     }
 
     @Override
@@ -789,16 +791,28 @@
                 + marginLayoutParams.topMargin;
     }
 
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
+    private int getCurrentAdapterHolderType() {
         if (mIsInSearchMode) {
-            mSearchBar.reset();
+            return SEARCH;
+        } else if (mViewPager != null) {
+            return mViewPager.getCurrentPage();
+        } else {
+            return AdapterHolder.PRIMARY;
+        }
+    }
+
+    private void restorePreviousAdapterHolderType(int previousAdapterHolderType) {
+        if (previousAdapterHolderType == AdapterHolder.WORK && mViewPager != null) {
+            mViewPager.setCurrentPage(previousAdapterHolderType);
+        } else if (previousAdapterHolderType == AdapterHolder.SEARCH) {
+            enterSearchMode(false);
         }
     }
 
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
+        super.onDeviceProfileChanged(dp);
+
         if (mDeviceProfile.isLandscape != dp.isLandscape && dp.isTablet && !dp.isTwoPanels) {
             handleClose(false);
             show(BaseActivity.fromContext(getContext()), false);
@@ -810,8 +824,12 @@
         // When folding/unfolding the foldables, we need to switch between the regular widget picker
         // and the two pane picker, so we rebuild the picker with the correct layout.
         if (mDeviceProfile.isTwoPanels != dp.isTwoPanels && enableUnfoldedTwoPanePicker()) {
+            SparseArray<Parcelable> widgetsState = new SparseArray<>();
+            saveHierarchyState(widgetsState);
             handleClose(false);
-            show(BaseActivity.fromContext(getContext()), false);
+            WidgetsFullSheet sheet = show(BaseActivity.fromContext(getContext()), false);
+            sheet.restoreHierarchyState(widgetsState);
+            sheet.restorePreviousAdapterHolderType(getCurrentAdapterHolderType());
         }
 
         mDeviceProfile = dp;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index 5a1ec87..744c45b 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -18,7 +18,6 @@
 import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
 
 import android.content.Context;
-import android.content.res.Configuration;
 import android.graphics.Outline;
 import android.graphics.Rect;
 import android.os.Process;
@@ -127,10 +126,6 @@
         mFastScroller.setVisibility(GONE);
     }
 
-    /** Overrides onConfigurationChanged method from WidgetsFullSheet. Needed for b/319150904 */
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {}
-
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
@@ -200,10 +195,14 @@
                 return false;
             }
         };
-        packageItemInfo.title = getContext().getString(R.string.suggested_widgets_header_title);
+        String suggestionsHeaderTitle = getContext().getString(
+                R.string.suggested_widgets_header_title);
+        String suggestionsRightPaneTitle = getContext().getString(
+                R.string.widget_picker_right_pane_accessibility_title, suggestionsHeaderTitle);
+        packageItemInfo.title = suggestionsHeaderTitle;
         WidgetsListHeaderEntry widgetsListHeaderEntry = WidgetsListHeaderEntry.create(
                         packageItemInfo,
-                        getContext().getString(R.string.suggested_widgets_header_title),
+                        suggestionsHeaderTitle,
                         mActivityContext.getPopupDataProvider().getRecommendedWidgets())
                 .withWidgetListShown();
 
@@ -216,10 +215,12 @@
             mRightPane.removeAllViews();
             mRightPane.addView(mRecommendedWidgetsTable);
             mRightPaneScrollView.setScrollY(0);
+            mRightPane.setAccessibilityPaneTitle(suggestionsRightPaneTitle);
             mSuggestedWidgetsPackageUserKey = PackageUserKey.fromPackageItemInfo(packageItemInfo);
             mSelectedHeader = mSuggestedWidgetsPackageUserKey;
         });
         mSuggestedWidgetsContainer.addView(mSuggestedWidgetsHeader);
+        mRightPane.setAccessibilityPaneTitle(suggestionsRightPaneTitle);
     }
 
     @Override
@@ -322,6 +323,10 @@
                 mRightPane.removeAllViews();
                 mRightPane.addView(widgetsRowViewHolder.itemView);
                 mRightPaneScrollView.setScrollY(0);
+                mRightPane.setAccessibilityPaneTitle(
+                        getContext().getString(
+                                R.string.widget_picker_right_pane_accessibility_title,
+                                contentEntry.mPkgItem.title));
             }
         };
     }
diff --git a/src/com/android/launcher3/widget/picker/search/SearchModeListener.java b/src/com/android/launcher3/widget/picker/search/SearchModeListener.java
index cee7d67..b2620d0 100644
--- a/src/com/android/launcher3/widget/picker/search/SearchModeListener.java
+++ b/src/com/android/launcher3/widget/picker/search/SearchModeListener.java
@@ -26,7 +26,7 @@
     /**
      * Notifies the subscriber when user enters widget picker search mode.
      */
-    void enterSearchMode();
+    void enterSearchMode(boolean shouldLog);
 
     /**
      * Notifies the subscriber when user exits widget picker search mode.
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
index a15508a..2d96cbd 100644
--- a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
@@ -70,7 +70,7 @@
             mCancelButton.setVisibility(GONE);
         } else {
             mSearchAlgorithm.cancel(/* interruptActiveRequests= */ false);
-            mSearchModeListener.enterSearchMode();
+            mSearchModeListener.enterSearchMode(true);
             mSearchAlgorithm.doSearch(mQuery, this);
             mCancelButton.setVisibility(VISIBLE);
         }
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 4c5bfd8..8b983fc 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -147,7 +147,8 @@
                         LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo);
 
                 widgetsAndShortcuts.add(new WidgetItem(
-                        launcherWidgetInfo, idp, app.getIconCache(), app.getContext()));
+                        launcherWidgetInfo, idp, app.getIconCache(), app.getContext(),
+                        widgetManager));
                 updatedItems.add(launcherWidgetInfo);
             }
 
@@ -206,6 +207,7 @@
 
     public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
             LauncherAppState app) {
+        WidgetManagerHelper widgetManager = new WidgetManagerHelper(app.getContext());
         for (Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
             if (packageNames.contains(entry.getKey().packageName)) {
                 List<WidgetItem> items = entry.getValue();
@@ -219,7 +221,7 @@
                         } else {
                             items.set(i, new WidgetItem(item.widgetInfo,
                                     app.getInvariantDeviceProfile(), app.getIconCache(),
-                                    app.getContext()));
+                                    app.getContext(), widgetManager));
                         }
                     }
                 }
@@ -337,4 +339,4 @@
             return mMap.values();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/Android.bp b/tests/Android.bp
index ed8609e..9ce0777 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -127,6 +127,7 @@
         "testables",
         "com_android_launcher3_flags_lib",
         "com_android_wm_shell_flags_lib",
+        "android.appwidget.flags-aconfig-java",
     ],
     manifest: "AndroidManifest-common.xml",
     platform_apis: true,
diff --git a/tests/multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 978e1f2..679bd01 100644
--- a/tests/multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -193,6 +193,7 @@
 
     protected AbstractLauncherUiTest() {
         mLauncher.enableCheckEventsForSuccessfulGestures();
+        mLauncher.setAnomalyChecker(AbstractLauncherUiTest::verifyKeyguardInvisible);
         try {
             mDevice.setOrientationNatural();
         } catch (RemoteException e) {
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index cdde605..e17994c 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -204,6 +204,9 @@
 
     private LogEventChecker mEventChecker;
 
+    // UI anomaly checker provided by the test.
+    private Runnable mTestAnomalyChecker;
+
     private boolean mCheckEventsForSuccessfulGestures = false;
     private Runnable mOnLauncherCrashed;
 
@@ -573,8 +576,31 @@
         checkForAnomaly(false, false);
     }
 
+    /**
+     * Allows the test to provide a pluggable anomaly checker. It’s supposed to throw an exception
+     * if the check fails. The test may provide its own anomaly checker, for example, if it wants to
+     * check for an anomaly that’s recognized by the standard TAPL anomaly checker, but wants a
+     * custom error message, such as adding information whether the keyguard is seen for the first
+     * time during the shard execution.
+     */
+    public void setAnomalyChecker(Runnable anomalyChecker) {
+        mTestAnomalyChecker = anomalyChecker;
+    }
+
+    /**
+     * Verifies that there are no visible UI anomalies. An "anomaly" is a state of UI that should
+     * never happen during the text execution. Anomaly is something different from just “regular”
+     * unexpected state of the Launcher such as when we see Workspace after swiping up to All Apps.
+     * Workspace is a normal state. We can contrast this with an anomaly, when, for example, we see
+     * a lock screen. Launcher tests can never bring the lock screen, so the very presence of the
+     * lock screen is an indication that something went very wrong, and perhaps is caused by reasons
+     * outside of the Launcher and its tests, perhaps, by a crash in System UI. Diagnosing anomalies
+     * helps to understand faster whether the problem is in the Launcher or its tests, or outside.
+     */
     public void checkForAnomaly(
             boolean ignoreNavmodeChangeStates, boolean ignoreOnlySystemUiViews) {
+        if (mTestAnomalyChecker != null) mTestAnomalyChecker.run();
+
         final String systemAnomalyMessage =
                 getSystemAnomalyMessage(ignoreNavmodeChangeStates, ignoreOnlySystemUiViews);
         if (systemAnomalyMessage != null) {
diff --git a/tests/res/layout/test_layout_appwidget_red.xml b/tests/res/layout/test_layout_appwidget_red.xml
index 48d3e81..0f2bda3 100644
--- a/tests/res/layout/test_layout_appwidget_red.xml
+++ b/tests/res/layout/test_layout_appwidget_red.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/content"
     android:orientation="vertical"
     android:background="#FFFF0000"
     android:layout_width="match_parent"
diff --git a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
index 6fce4c6..0f23165 100644
--- a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
@@ -25,6 +25,7 @@
 
 import android.content.Intent;
 import android.platform.test.annotations.PlatinumTest;
+import android.platform.test.rule.ScreenRecordRule;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -130,6 +131,7 @@
     @Test
     @PortraitLandscape
     @PlatinumTest(focusArea = "launcher")
+    @ScreenRecordRule.ScreenRecord // b/322228038
     public void testAllAppsFromHome() {
         // Test opening all apps
         assertNotNull("switchToAllApps() returned null",
diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index f771052..6c35f68 100644
--- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -2,21 +2,33 @@
 
 import static android.os.Process.myUserHandle;
 
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
 import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2;
 import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3;
 import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
+import static com.android.launcher3.util.TestUtil.DUMMY_CLASS_NAME;
+import static com.android.launcher3.util.TestUtil.DUMMY_PACKAGE;
 import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
+import androidx.test.uiautomator.UiDevice;
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.icons.BitmapInfo;
@@ -26,12 +38,15 @@
 import com.android.launcher3.util.LauncherLayoutBuilder;
 import com.android.launcher3.util.LauncherModelHelper;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.TestUtil;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -43,9 +58,18 @@
 @RunWith(AndroidJUnit4.class)
 public class CacheDataUpdatedTaskTest {
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
     private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1";
     private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2";
 
+    private static final String ARCHIVED_PACKAGE = DUMMY_PACKAGE;
+    private static final String ARCHIVED_CLASS_NAME = DUMMY_CLASS_NAME;
+    private static final String ARCHIVED_TITLE = "Aardwolf";
+
+
     private LauncherModelHelper mModelHelper;
     private Context mContext;
 
@@ -57,6 +81,7 @@
         mContext = mModelHelper.sandboxContext;
         mSession1 = mModelHelper.createInstallerSession(PENDING_APP_1);
         mModelHelper.createInstallerSession(PENDING_APP_2);
+        TestUtil.installDummyApp();
 
         LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
                 .atHotseat(1).putFolder("MyFolder")
@@ -73,14 +98,22 @@
                 .addApp(PENDING_APP_2, TEST_ACTIVITY)   // 8
                 .addApp(PENDING_APP_2, TEST_ACTIVITY2)  // 9
                 .addApp(PENDING_APP_2, TEST_ACTIVITY3)  // 10
+
+                // Dummy Test Package
+                .addApp(ARCHIVED_PACKAGE, ARCHIVED_CLASS_NAME) // 11
                 .build();
         mModelHelper.setupDefaultLayoutProvider(builder);
         mModelHelper.loadModelSync();
-        assertEquals(10, mModelHelper.getBgDataModel().itemsIdMap.size());
+        assertEquals(11, mModelHelper.getBgDataModel().itemsIdMap.size());
+
+        UiDevice device = UiDevice.getInstance(getInstrumentation());
+        assertThat(device.executeShellCommand(String.format("pm archive %s", ARCHIVED_PACKAGE)))
+                .isEqualTo("Success\n");
     }
 
     @After
-    public void tearDown() {
+    public void tearDown() throws IOException {
+        TestUtil.uninstallDummyApp();
         mModelHelper.destroy();
     }
 
@@ -138,6 +171,47 @@
         });
     }
 
+    @Test
+    @RequiresFlagsEnabled(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
+    public void testSessionUpdate_archivedApps_sessionInfoPrioritized() {
+        // Run on model executor so that no other task runs in the middle.
+        runOnExecutorSync(MODEL_EXECUTOR, () -> {
+            // Clear all icons from apps list so that its easy to check what was updated
+            allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO);
+            int mSession2 = mModelHelper.createInstallerSession(ARCHIVED_PACKAGE);
+            mModelHelper.getModel().enqueueModelUpdateTask(
+                    newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, ARCHIVED_PACKAGE));
+            List<Integer> pendingArchivedAppIds = List.of(11);
+            // Mark the app items as archived.
+            allItems().forEach(wi -> {
+                if (pendingArchivedAppIds.contains(wi.id)) {
+                    wi.runtimeStatusFlags |= FLAG_ARCHIVED;
+                }
+            });
+            // Before cache is updated with sessionInfo, confirm the title.
+            for (WorkspaceItemInfo info : allItems()) {
+                if (pendingArchivedAppIds.contains(info.id)) {
+                    assertEquals(info.title, ARCHIVED_TITLE);
+                }
+            }
+
+            // Update the cache with session details.
+            LauncherAppState.getInstance(mContext).getIconCache().updateSessionCache(
+                    new PackageUserKey(ARCHIVED_PACKAGE, myUserHandle()),
+                    mContext.getPackageManager().getPackageInstaller().getSessionInfo(mSession2));
+
+            // Trigger a refresh for workspace itemInfo objects.
+            mModelHelper.getModel().enqueueModelUpdateTask(
+                    newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, ARCHIVED_PACKAGE));
+            // Verify the new title from session is applied to the iconInfo.
+            for (WorkspaceItemInfo info : allItems()) {
+                if (pendingArchivedAppIds.contains(info.id)) {
+                    assertEquals(info.title, ARCHIVED_PACKAGE);
+                }
+            }
+        });
+    }
+
     private void verifyUpdate(int... idsUpdated) {
         IntSet updates = IntSet.wrap(idsUpdated);
         for (WorkspaceItemInfo info : allItems()) {
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
index e94dc02..fc7caed 100644
--- a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
+++ b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
@@ -18,6 +18,7 @@
 
 import android.appwidget.AppWidgetProviderInfo
 import android.content.ComponentName
+import android.content.Context
 import android.content.Intent
 import android.content.pm.LauncherApps
 import android.content.pm.PackageInstaller
@@ -27,245 +28,137 @@
 import com.android.launcher3.LauncherAppState
 import com.android.launcher3.LauncherSettings.Favorites
 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
+import com.android.launcher3.Utilities.EMPTY_PERSON_ARRAY
 import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError.Companion.MISSING_INFO
 import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError.Companion.PROFILE_DELETED
+import com.android.launcher3.model.data.FolderInfo
 import com.android.launcher3.model.data.IconRequestInfo
+import com.android.launcher3.model.data.ItemInfo
 import com.android.launcher3.model.data.WorkspaceItemInfo
 import com.android.launcher3.shortcuts.ShortcutKey
 import com.android.launcher3.util.ComponentKey
 import com.android.launcher3.util.PackageManagerHelper
 import com.android.launcher3.util.PackageUserKey
 import com.android.launcher3.widget.WidgetInflater
+import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Before
 import org.junit.Test
+import org.mockito.Mock
 import org.mockito.Mockito.RETURNS_DEEP_STUBS
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.doAnswer
 import org.mockito.kotlin.mock
-import org.mockito.kotlin.times
-import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
 class WorkspaceItemProcessorTest {
-    private var itemProcessor = createTestWorkspaceItemProcessor()
+
+    @Mock private lateinit var mockIconRequestInfo: IconRequestInfo<WorkspaceItemInfo>
+    @Mock private lateinit var mockWorkspaceInfo: WorkspaceItemInfo
+    @Mock private lateinit var mockBgDataModel: BgDataModel
+    @Mock private lateinit var mockContext: Context
+    @Mock private lateinit var mockAppState: LauncherAppState
+    @Mock private lateinit var mockIntent: Intent
+    @Mock private lateinit var mockPmHelper: PackageManagerHelper
+    @Mock private lateinit var mockLauncherApps: LauncherApps
+    @Mock private lateinit var mockCursor: LoaderCursor
+    @Mock private lateinit var mockUserManagerState: UserManagerState
+    @Mock private lateinit var mockWidgetInflater: WidgetInflater
+
+    private lateinit var userHandle: UserHandle
+    private lateinit var iconRequestInfos: MutableList<IconRequestInfo<WorkspaceItemInfo>>
+    private lateinit var componentName: ComponentName
+    private lateinit var unlockedUsersArray: LongSparseArray<Boolean>
+    private lateinit var keyToPinnedShortcutsMap: MutableMap<ShortcutKey, ShortcutInfo>
+    private lateinit var installingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo>
+    private lateinit var allDeepShortcuts: MutableList<ShortcutInfo>
+
+    private lateinit var itemProcessorUnderTest: WorkspaceItemProcessor
 
     @Before
     fun setup() {
-        itemProcessor = createTestWorkspaceItemProcessor()
-    }
-
-    @Test
-    fun `When user is null then mark item deleted`() {
-        // Given
-        val mockCursor = mock<LoaderCursor>().apply { id = 1 }
-        val itemProcessor = createTestWorkspaceItemProcessor(cursor = mockCursor)
-        // When
-        itemProcessor.processItem()
-        // Then
-        verify(mockCursor).markDeleted("User has been deleted for item id=1", PROFILE_DELETED)
-    }
-
-    @Test
-    fun `When app has null intent then mark deleted`() {
-        // Given
-        val mockCursor =
-            mock<LoaderCursor>().apply {
-                user = UserHandle(0)
-                id = 1
-                itemType = ITEM_TYPE_APPLICATION
-            }
-        val itemProcessor = createTestWorkspaceItemProcessor(cursor = mockCursor)
-        // When
-        itemProcessor.processItem()
-        // Then
-        verify(mockCursor).markDeleted("Null intent for item id=1", MISSING_INFO)
-    }
-
-    @Test
-    fun `When app has null target package then mark deleted`() {
-        // Given
-        val mockCursor =
-            mock<LoaderCursor>().apply {
-                user = UserHandle(0)
-                itemType = ITEM_TYPE_APPLICATION
-                id = 1
-                whenever(parseIntent()).thenReturn(Intent())
-            }
-        val itemProcessor = createTestWorkspaceItemProcessor(cursor = mockCursor)
-        // When
-        itemProcessor.processItem()
-        // Then
-        verify(mockCursor).markDeleted("No target package for item id=1", MISSING_INFO)
-    }
-
-    @Test
-    fun `When app has empty String target package then mark deleted`() {
-        // Given
-        val mockIntent =
-            mock<Intent>().apply {
-                whenever(component).thenReturn(null)
-                whenever(`package`).thenReturn("")
-            }
-        val mockCursor =
-            mock<LoaderCursor>().apply {
-                user = UserHandle(0)
-                itemType = ITEM_TYPE_APPLICATION
-                id = 1
-                whenever(parseIntent()).thenReturn(mockIntent)
-            }
-        val itemProcessor = createTestWorkspaceItemProcessor(cursor = mockCursor)
-        // When
-        itemProcessor.processItem()
-        // Then
-        verify(mockCursor).markDeleted("No target package for item id=1", MISSING_INFO)
-    }
-
-    @Test
-    fun `When valid app then mark restored`() {
-        // Given
-        val userHandle = UserHandle(0)
-        val componentName = ComponentName("package", "class")
-        val mockIntent =
+        userHandle = UserHandle(0)
+        mockIconRequestInfo = mock<IconRequestInfo<WorkspaceItemInfo>>()
+        iconRequestInfos = mutableListOf(mockIconRequestInfo)
+        mockWorkspaceInfo = mock<WorkspaceItemInfo>()
+        mockBgDataModel = mock<BgDataModel>()
+        componentName = ComponentName("package", "class")
+        unlockedUsersArray = LongSparseArray<Boolean>(1).apply { put(101, true) }
+        mockIntent =
             mock<Intent>().apply {
                 whenever(component).thenReturn(componentName)
-                whenever(`package`).thenReturn("")
+                whenever(`package`).thenReturn("pkg")
+                whenever(getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID)).thenReturn("")
             }
-        val mockLauncherApps =
-            mock<LauncherApps>().apply {
-                whenever(isPackageEnabled("package", userHandle)).thenReturn(true)
-                whenever(isActivityEnabled(componentName, userHandle)).thenReturn(true)
+        mockContext =
+            mock<Context>().apply {
+                whenever(packageManager).thenReturn(mock())
+                whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("")
             }
-        val mockCursor =
-            mock<LoaderCursor>().apply {
-                user = userHandle
-                itemType = ITEM_TYPE_APPLICATION
-                id = 1
-                restoreFlag = 1
-                whenever(parseIntent()).thenReturn(mockIntent)
-                whenever(markRestored()).doAnswer { restoreFlag = 0 }
+        mockAppState =
+            mock<LauncherAppState>().apply {
+                whenever(context).thenReturn(mockContext)
+                whenever(iconCache).thenReturn(mock())
+                whenever(iconCache.getShortcutIcon(any(), any(), any())).then {}
             }
-        val itemProcessor =
-            createTestWorkspaceItemProcessor(cursor = mockCursor, launcherApps = mockLauncherApps)
-        // When
-        itemProcessor.processItem()
-        // Then
-        assertWithMessage("item restoreFlag should be set to 0")
-            .that(mockCursor.restoreFlag)
-            .isEqualTo(0)
-        // currently gets marked restored twice, although markRestore() has check for restoreFlag
-        verify(mockCursor, times(2)).markRestored()
-    }
-
-    @Test
-    fun `When fallback Activity found for app then mark restored`() {
-        // Given
-        val userHandle = UserHandle(0)
-        val componentName = ComponentName("package", "class")
-        val mockIntent =
-            mock<Intent>().apply {
-                whenever(component).thenReturn(componentName)
-                whenever(`package`).thenReturn("")
-                whenever(toUri(0)).thenReturn("")
-            }
-        val mockLauncherApps =
-            mock<LauncherApps>().apply {
-                whenever(isPackageEnabled("package", userHandle)).thenReturn(true)
-                whenever(isActivityEnabled(componentName, userHandle)).thenReturn(false)
-            }
-        val mockPmHelper =
+        mockPmHelper =
             mock<PackageManagerHelper>().apply {
                 whenever(getAppLaunchIntent(componentName.packageName, userHandle))
                     .thenReturn(mockIntent)
             }
-        val mockCursor =
+        mockLauncherApps =
+            mock<LauncherApps>().apply {
+                whenever(isPackageEnabled("package", userHandle)).thenReturn(true)
+                whenever(isActivityEnabled(componentName, userHandle)).thenReturn(true)
+            }
+        mockCursor =
             mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply {
                 user = userHandle
                 itemType = ITEM_TYPE_APPLICATION
                 id = 1
                 restoreFlag = 1
+                serialNumber = 101
                 whenever(parseIntent()).thenReturn(mockIntent)
                 whenever(markRestored()).doAnswer { restoreFlag = 0 }
                 whenever(updater().put(Favorites.INTENT, mockIntent.toUri(0)).commit())
                     .thenReturn(1)
+                whenever(getAppShortcutInfo(any(), any(), any(), any()))
+                    .thenReturn(mockWorkspaceInfo)
+                whenever(createIconRequestInfo(any(), any())).thenReturn(mockIconRequestInfo)
             }
-        val itemProcessor =
-            createTestWorkspaceItemProcessor(
-                cursor = mockCursor,
-                launcherApps = mockLauncherApps,
-                pmHelper = mockPmHelper
-            )
-        // When
-        itemProcessor.processItem()
-        // Then
-        assertWithMessage("item restoreFlag should be set to 0")
-            .that(mockCursor.restoreFlag)
-            .isEqualTo(0)
-        verify(mockCursor.updater().put(Favorites.INTENT, mockIntent.toUri(0))).commit()
-    }
-
-    @Test
-    fun `When app with disabled activity and no fallback found then mark deleted`() {
-        // Given
-        val userHandle = UserHandle(0)
-        val componentName = ComponentName("package", "class")
-        val mockIntent =
-            mock<Intent>().apply {
-                whenever(component).thenReturn(componentName)
-                whenever(`package`).thenReturn("")
-            }
-        val mockLauncherApps =
-            mock<LauncherApps>().apply {
-                whenever(isPackageEnabled("package", userHandle)).thenReturn(true)
-                whenever(isActivityEnabled(componentName, userHandle)).thenReturn(false)
-            }
-        val mockPmHelper =
-            mock<PackageManagerHelper>().apply {
-                whenever(getAppLaunchIntent(componentName.packageName, userHandle)).thenReturn(null)
-            }
-        val mockCursor =
-            mock<LoaderCursor>().apply {
-                user = userHandle
-                itemType = ITEM_TYPE_APPLICATION
-                id = 1
-                restoreFlag = 1
-                whenever(parseIntent()).thenReturn(mockIntent)
-            }
-        val itemProcessor =
-            createTestWorkspaceItemProcessor(
-                cursor = mockCursor,
-                launcherApps = mockLauncherApps,
-                pmHelper = mockPmHelper
-            )
-        // When
-        itemProcessor.processItem()
-        // Then
-        assertWithMessage("item restoreFlag should be unchanged")
-            .that(mockCursor.restoreFlag)
-            .isEqualTo(1)
-        verify(mockCursor).markDeleted("Intent null, unable to find a launch target", MISSING_INFO)
+        mockUserManagerState = mock<UserManagerState>()
+        mockWidgetInflater = mock<WidgetInflater>()
+        keyToPinnedShortcutsMap = mutableMapOf()
+        installingPkgs = hashMapOf()
+        allDeepShortcuts = mutableListOf()
     }
 
     /**
      * Helper to create WorkspaceItemProcessor with defaults. WorkspaceItemProcessor has a lot of
      * dependencies, so this method can be used to inject concrete arguments while keeping the rest
-     * as mocks/defaults.
+     * as mocks/defaults, or to recreate it after modifying the default vars.
      */
-    private fun createTestWorkspaceItemProcessor(
-        cursor: LoaderCursor = mock(),
+    private fun createWorkspaceItemProcessorUnderTest(
+        cursor: LoaderCursor = mockCursor,
         memoryLogger: LoaderMemoryLogger? = null,
-        userManagerState: UserManagerState = mock(),
-        launcherApps: LauncherApps = mock(),
-        shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo> = mapOf(),
-        app: LauncherAppState = mock(),
-        bgDataModel: BgDataModel = mock(),
+        userManagerState: UserManagerState = mockUserManagerState,
+        launcherApps: LauncherApps = mockLauncherApps,
+        shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo> = keyToPinnedShortcutsMap,
+        app: LauncherAppState = mockAppState,
+        bgDataModel: BgDataModel = mockBgDataModel,
         widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?> = mutableMapOf(),
-        widgetInflater: WidgetInflater = mock(),
-        pmHelper: PackageManagerHelper = mock(),
+        widgetInflater: WidgetInflater = mockWidgetInflater,
+        pmHelper: PackageManagerHelper = mockPmHelper,
         iconRequestInfos: MutableList<IconRequestInfo<WorkspaceItemInfo>> = mutableListOf(),
         isSdCardReady: Boolean = false,
         pendingPackages: MutableSet<PackageUserKey> = mutableSetOf(),
-        unlockedUsers: LongSparseArray<Boolean> = LongSparseArray(),
+        unlockedUsers: LongSparseArray<Boolean> = unlockedUsersArray,
         installingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo> = hashMapOf(),
         allDeepShortcuts: MutableList<ShortcutInfo> = mutableListOf()
     ) =
@@ -287,4 +180,244 @@
             installingPkgs = installingPkgs,
             allDeepShortcuts = allDeepShortcuts
         )
+
+    @Test
+    fun `When user is null then mark item deleted`() {
+        // Given
+        mockCursor = mock<LoaderCursor>().apply { id = 1 }
+        itemProcessorUnderTest = createWorkspaceItemProcessorUnderTest()
+        // When
+        itemProcessorUnderTest.processItem()
+        // Then
+        verify(mockCursor).markDeleted("User has been deleted for item id=1", PROFILE_DELETED)
+        verify(mockCursor, times(0)).checkAndAddItem(any(), any(), anyOrNull())
+    }
+
+    @Test
+    fun `When app has null intent then mark deleted`() {
+        // Given
+        mockCursor.apply { whenever(parseIntent()).thenReturn(null) }
+        itemProcessorUnderTest = createWorkspaceItemProcessorUnderTest()
+        // When
+        itemProcessorUnderTest.processItem()
+        // Then
+        verify(mockCursor).markDeleted("Null intent for item id=1", MISSING_INFO)
+        verify(mockCursor, times(0)).checkAndAddItem(any(), any(), anyOrNull())
+    }
+
+    @Test
+    fun `When app has null target package then mark deleted`() {
+
+        // Given
+        mockIntent.apply {
+            whenever(component).thenReturn(null)
+            whenever(`package`).thenReturn(null)
+        }
+        itemProcessorUnderTest = createWorkspaceItemProcessorUnderTest()
+
+        // When
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        verify(mockCursor).markDeleted("No target package for item id=1", MISSING_INFO)
+        verify(mockCursor, times(0)).checkAndAddItem(any(), any(), anyOrNull())
+    }
+
+    @Test
+    fun `When app has empty String target package then mark deleted`() {
+
+        // Given
+        componentName = ComponentName("", "")
+        whenever(mockIntent.component).thenReturn(componentName)
+        whenever(mockCursor.parseIntent()).thenReturn(mockIntent)
+        itemProcessorUnderTest = createWorkspaceItemProcessorUnderTest()
+
+        // When
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        verify(mockCursor).markDeleted("No target package for item id=1", MISSING_INFO)
+        verify(mockCursor, times(0)).checkAndAddItem(any(), any(), anyOrNull())
+    }
+
+    @Test
+    fun `When valid app then mark restored`() {
+
+        // Given
+        itemProcessorUnderTest = createWorkspaceItemProcessorUnderTest()
+
+        // When
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        assertWithMessage("item restoreFlag should be set to 0")
+            .that(mockCursor.restoreFlag)
+            .isEqualTo(0)
+        // currently gets marked restored twice, although markRestore() has check for restoreFlag
+        verify(mockCursor, times(2)).markRestored()
+        assertThat(iconRequestInfos).containsExactly(mockIconRequestInfo)
+        verify(mockCursor).checkAndAddItem(mockWorkspaceInfo, mockBgDataModel, null)
+    }
+
+    @Test
+    fun `When fallback Activity found for app then mark restored`() {
+
+        // Given
+        mockLauncherApps =
+            mock<LauncherApps>().apply {
+                whenever(isPackageEnabled("package", userHandle)).thenReturn(true)
+                whenever(isActivityEnabled(componentName, userHandle)).thenReturn(false)
+            }
+        mockPmHelper =
+            mock<PackageManagerHelper>().apply {
+                whenever(getAppLaunchIntent(componentName.packageName, userHandle))
+                    .thenReturn(mockIntent)
+            }
+        itemProcessorUnderTest = createWorkspaceItemProcessorUnderTest()
+
+        // When
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        assertWithMessage("item restoreFlag should be set to 0")
+            .that(mockCursor.restoreFlag)
+            .isEqualTo(0)
+        verify(mockCursor.updater().put(Favorites.INTENT, mockIntent.toUri(0))).commit()
+        assertThat(iconRequestInfos).containsExactly(mockIconRequestInfo)
+        verify(mockCursor).checkAndAddItem(mockWorkspaceInfo, mockBgDataModel, null)
+    }
+
+    @Test
+    fun `When app with disabled activity and no fallback found then mark deleted`() {
+
+        // Given
+        mockLauncherApps =
+            mock<LauncherApps>().apply {
+                whenever(isPackageEnabled("package", userHandle)).thenReturn(true)
+                whenever(isActivityEnabled(componentName, userHandle)).thenReturn(false)
+            }
+        mockPmHelper =
+            mock<PackageManagerHelper>().apply {
+                whenever(getAppLaunchIntent(componentName.packageName, userHandle)).thenReturn(null)
+            }
+        itemProcessorUnderTest = createWorkspaceItemProcessorUnderTest()
+
+        // When
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        assertWithMessage("item restoreFlag should be unchanged")
+            .that(mockCursor.restoreFlag)
+            .isEqualTo(1)
+        verify(mockCursor).markDeleted("Intent null, unable to find a launch target", MISSING_INFO)
+        verify(mockCursor, times(0)).checkAndAddItem(any(), any(), anyOrNull())
+    }
+
+    @Test
+    fun `When valid Pinned Deep Shortcut then mark restored`() {
+
+        // Given
+        mockCursor.itemType = ITEM_TYPE_DEEP_SHORTCUT
+        val expectedShortcutInfo =
+            mock<ShortcutInfo>().apply {
+                whenever(id).thenReturn("")
+                whenever(`package`).thenReturn("")
+                whenever(activity).thenReturn(mock())
+                whenever(longLabel).thenReturn("")
+                whenever(isEnabled).thenReturn(true)
+                whenever(disabledMessage).thenReturn("")
+                whenever(disabledReason).thenReturn(0)
+                whenever(persons).thenReturn(EMPTY_PERSON_ARRAY)
+            }
+        val shortcutKey = ShortcutKey.fromIntent(mockIntent, mockCursor.user)
+        keyToPinnedShortcutsMap[shortcutKey] = expectedShortcutInfo
+        iconRequestInfos = mutableListOf()
+        itemProcessorUnderTest =
+            createWorkspaceItemProcessorUnderTest(allDeepShortcuts = allDeepShortcuts)
+
+        // When
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        assertWithMessage("item restoreFlag should be set to 0")
+            .that(mockCursor.restoreFlag)
+            .isEqualTo(0)
+        assertThat(iconRequestInfos).isEmpty()
+        assertThat(allDeepShortcuts).containsExactly(expectedShortcutInfo)
+        verify(mockCursor).markRestored()
+        verify(mockCursor).checkAndAddItem(any(), any(), anyOrNull())
+    }
+
+    @Test
+    fun `When Pinned Deep Shortcut not found then mark deleted`() {
+
+        // Given
+        mockCursor.itemType = ITEM_TYPE_DEEP_SHORTCUT
+        iconRequestInfos = mutableListOf()
+        keyToPinnedShortcutsMap = hashMapOf()
+        itemProcessorUnderTest = createWorkspaceItemProcessorUnderTest()
+
+        // When
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        assertWithMessage("item restoreFlag should be set to 0")
+            .that(mockCursor.restoreFlag)
+            .isEqualTo(0)
+        assertThat(iconRequestInfos).isEmpty()
+        verify(mockCursor, times(0)).checkAndAddItem(any(), any(), anyOrNull())
+        verify(mockCursor)
+            .markDeleted(
+                "Pinned shortcut not found from request. package=pkg, user=UserHandle{0}",
+                "shortcut_not_found"
+            )
+    }
+
+    @Test
+    fun `When processing Folder then create FolderInfo and mark restored`() {
+        val actualFolderInfo = FolderInfo()
+        mockBgDataModel =
+            mock<BgDataModel>().apply { whenever(findOrMakeFolder(1)).thenReturn(actualFolderInfo) }
+        mockCursor =
+            mock<LoaderCursor>().apply {
+                user = UserHandle(0)
+                itemType = ITEM_TYPE_FOLDER
+                id = 1
+                container = 100
+                restoreFlag = 1
+                serialNumber = 101
+                whenever(applyCommonProperties(any<ItemInfo>())).then {}
+                whenever(markRestored()).doAnswer { restoreFlag = 0 }
+                whenever(getColumnIndex(Favorites.TITLE)).thenReturn(4)
+                whenever(getString(4)).thenReturn("title")
+                whenever(options).thenReturn(5)
+            }
+        val expectedFolderInfo =
+            FolderInfo().apply {
+                itemType = ITEM_TYPE_FOLDER
+                spanX = 1
+                spanY = 1
+                options = 5
+            }
+        itemProcessorUnderTest = createWorkspaceItemProcessorUnderTest()
+
+        // When
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        assertWithMessage("item restoreFlag should be set to 0")
+            .that(mockCursor.restoreFlag)
+            .isEqualTo(0)
+        verify(mockCursor).markRestored()
+        assertThat(actualFolderInfo.id).isEqualTo(expectedFolderInfo.id)
+        assertThat(actualFolderInfo.container).isEqualTo(expectedFolderInfo.container)
+        assertThat(actualFolderInfo.itemType).isEqualTo(expectedFolderInfo.itemType)
+        assertThat(actualFolderInfo.screenId).isEqualTo(expectedFolderInfo.screenId)
+        assertThat(actualFolderInfo.cellX).isEqualTo(expectedFolderInfo.cellX)
+        assertThat(actualFolderInfo.cellY).isEqualTo(expectedFolderInfo.cellY)
+        assertThat(actualFolderInfo.spanX).isEqualTo(expectedFolderInfo.spanX)
+        assertThat(actualFolderInfo.spanY).isEqualTo(expectedFolderInfo.spanY)
+        assertThat(actualFolderInfo.options).isEqualTo(expectedFolderInfo.options)
+        verify(mockCursor).checkAndAddItem(actualFolderInfo, mockBgDataModel, null)
+    }
 }
diff --git a/tests/src/com/android/launcher3/widget/GeneratedPreviewTest.kt b/tests/src/com/android/launcher3/widget/GeneratedPreviewTest.kt
new file mode 100644
index 0000000..11855e6
--- /dev/null
+++ b/tests/src/com/android/launcher3/widget/GeneratedPreviewTest.kt
@@ -0,0 +1,142 @@
+package com.android.launcher3.widget
+
+import android.appwidget.AppWidgetProviderInfo
+import android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN
+import android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD
+import android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.view.LayoutInflater
+import android.widget.RemoteViews
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.Flags.FLAG_ENABLE_GENERATED_PREVIEWS
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.icons.IconCache
+import com.android.launcher3.icons.IconProvider
+import com.android.launcher3.model.WidgetItem
+import com.android.launcher3.tests.R
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.util.Executors
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GeneratedPreviewTest {
+    @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+    private val providerName =
+        ComponentName(
+            "com.android.launcher3.tests",
+            "com.android.launcher3.testcomponent.AppWidgetNoConfig"
+        )
+    private val generatedPreviewLayout = R.layout.test_layout_appwidget_blue
+    private lateinit var context: Context
+    private lateinit var generatedPreview: RemoteViews
+    private lateinit var widgetCell: WidgetCell
+    private lateinit var helper: WidgetManagerHelper
+    private lateinit var appWidgetProviderInfo: LauncherAppWidgetProviderInfo
+    private lateinit var widgetItem: WidgetItem
+
+    @Before
+    fun setup() {
+        context = getApplicationContext()
+        generatedPreview = RemoteViews(context.packageName, generatedPreviewLayout)
+        widgetCell =
+            LayoutInflater.from(ActivityContextWrapper(context))
+                .inflate(com.android.launcher3.R.layout.widget_cell, null) as WidgetCell
+        appWidgetProviderInfo =
+            AppWidgetProviderInfo()
+                .apply {
+                    generatedPreviewCategories = WIDGET_CATEGORY_HOME_SCREEN
+                    provider = providerName
+                    providerInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() }
+                }
+                .let { LauncherAppWidgetProviderInfo.fromProviderInfo(context, it) }
+        helper =
+            object : WidgetManagerHelper(context) {
+                override fun loadGeneratedPreview(
+                    info: AppWidgetProviderInfo,
+                    widgetCategory: Int
+                ) =
+                    generatedPreview.takeIf {
+                        info === appWidgetProviderInfo &&
+                            widgetCategory == WIDGET_CATEGORY_HOME_SCREEN
+                    }
+            }
+        createWidgetItem()
+    }
+
+    private fun createWidgetItem() {
+        Executors.MODEL_EXECUTOR.submit {
+                val idp = InvariantDeviceProfile()
+                widgetItem =
+                    WidgetItem(
+                        appWidgetProviderInfo,
+                        idp,
+                        IconCache(context, idp, null, IconProvider(context)),
+                        context,
+                        helper,
+                    )
+            }
+            .get()
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_ENABLE_GENERATED_PREVIEWS)
+    fun widgetItem_hasGeneratedPreview() {
+        assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_HOME_SCREEN)).isTrue()
+        assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_KEYGUARD)).isFalse()
+        assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_SEARCHBOX)).isFalse()
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_ENABLE_GENERATED_PREVIEWS)
+    fun widgetItem_hasGeneratedPreview_noPreview() {
+        appWidgetProviderInfo.generatedPreviewCategories = 0
+        createWidgetItem()
+        assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_HOME_SCREEN)).isFalse()
+        assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_KEYGUARD)).isFalse()
+        assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_SEARCHBOX)).isFalse()
+    }
+
+    @Test
+    @RequiresFlagsDisabled(FLAG_ENABLE_GENERATED_PREVIEWS)
+    fun widgetItem_hasGeneratedPreview_flagDisabled() {
+        assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_HOME_SCREEN)).isFalse()
+        assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_KEYGUARD)).isFalse()
+        assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_SEARCHBOX)).isFalse()
+    }
+    @Test
+    @RequiresFlagsEnabled(FLAG_ENABLE_GENERATED_PREVIEWS)
+    fun widgetItem_getGeneratedPreview() {
+        val preview = widgetItem.generatedPreviews.get(WIDGET_CATEGORY_HOME_SCREEN)
+        assertThat(preview).isEqualTo(generatedPreview)
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_ENABLE_GENERATED_PREVIEWS)
+    fun widgetCell_showGeneratedPreview() {
+        widgetCell.applyFromCellItem(widgetItem)
+        assertThat(widgetCell.appWidgetHostViewPreview).isNotNull()
+        assertThat(widgetCell.appWidgetHostViewPreview?.appWidgetInfo)
+            .isEqualTo(appWidgetProviderInfo)
+    }
+
+    @Test
+    @RequiresFlagsDisabled(FLAG_ENABLE_GENERATED_PREVIEWS)
+    fun widgetCell_showGeneratedPreview_flagDisabled() {
+        widgetCell.applyFromCellItem(widgetItem)
+        assertThat(widgetCell.appWidgetHostViewPreview).isNull()
+    }
+}
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
index 60590e7..0286279 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -52,6 +52,7 @@
 import com.android.launcher3.util.WidgetUtils;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.WidgetManagerHelper;
 import com.android.launcher3.widget.model.WidgetsListContentEntry;
 
 import org.junit.Before;
@@ -137,6 +138,7 @@
     }
 
     private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
+        WidgetManagerHelper widgetManager = new WidgetManagerHelper(mContext);
         ArrayList<WidgetItem> widgetItems = new ArrayList<>();
         for (int i = 0; i < numOfWidgets; i++) {
             ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
@@ -144,7 +146,7 @@
 
             widgetItems.add(new WidgetItem(
                     LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
-                    mTestProfile, mIconCache, mContext));
+                    mTestProfile, mIconCache, mContext, widgetManager));
         }
         return widgetItems;
     }
diff --git a/tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java b/tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
index 583d37f..8457ac6 100644
--- a/tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
@@ -78,7 +78,7 @@
     public void afterTextChanged_shouldInformSearchModeListenerToEnterSearch() {
         mEditText.setText("abc");
 
-        verify(mSearchModeListener).enterSearchMode();
+        verify(mSearchModeListener).enterSearchMode(true);
         verifyNoMoreInteractions(mSearchModeListener);
     }