Merge "Adds BubbleControllers to TaskbarControllers" into udc-dev
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 55301ff..cebcd42 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -135,7 +135,7 @@
   }
 }
 
-// Next value 45
+// Next value 48
 enum Attribute {
   option allow_alias = true;
 
@@ -187,6 +187,11 @@
   ALL_APPS_SEARCH_RESULT_SYSTEM_POINTER = 42;
   ALL_APPS_SEARCH_RESULT_EDUCARD = 43;
 
+  // Result sources
+  DATA_SOURCE_APPSEARCH_APP_PREVIEW = 45;
+  DATA_SOURCE_APPSEARCH_APP_SRP_PREVIEW = 46;
+  DATA_SOURCE_AIAI_SEARCH_ROOT = 47;
+
   // Web suggestions provided by AGA
   ALL_APPS_SEARCH_RESULT_WEB_SUGGEST = 39;
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
index 268024f..885afff 100644
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
@@ -47,9 +47,7 @@
         mStartContextualContainer = mNavButtonsView.findViewById(R.id.start_contextual_buttons);
         mAllAppsButton = LayoutInflater.from(context)
                 .inflate(R.layout.taskbar_all_apps_button, mStartContextualContainer, false);
-        mAllAppsButton.setOnClickListener((View v) -> {
-            mControllers.taskbarAllAppsController.show();
-        });
+        mAllAppsButton.setOnClickListener(v -> mControllers.taskbarAllAppsController.toggle());
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 21f78b2..d94d8f7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -24,6 +24,7 @@
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
+import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY;
 import static com.android.launcher3.Utilities.isRunningInTestHarness;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
@@ -681,7 +682,10 @@
     void onDragEndOrViewRemoved() {
         boolean isDragInProgress = mControllers.taskbarDragController.isSystemDragInProgress();
 
-        if (!isDragInProgress && !AbstractFloatingView.hasOpenView(this, TYPE_ALL)) {
+        // Overlay AFVs are in a separate window and do not require Taskbar to be fullscreen.
+        if (!isDragInProgress
+                && !AbstractFloatingView.hasOpenView(
+                        this, TYPE_ALL & ~TYPE_TASKBAR_OVERLAY_PROXY)) {
             // Reverts Taskbar window to its original size
             setTaskbarWindowFullscreen(false);
         }
@@ -793,6 +797,7 @@
     }
 
     protected void onTaskbarIconClicked(View view) {
+        boolean shouldCloseAllOpenViews = true;
         Object tag = view.getTag();
         if (tag instanceof Task) {
             Task task = (Task) tag;
@@ -800,6 +805,7 @@
                     ActivityOptions.makeBasic());
             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
         } else if (tag instanceof FolderInfo) {
+            shouldCloseAllOpenViews = false;
             FolderIcon folderIcon = (FolderIcon) view;
             Folder folder = folderIcon.getFolder();
 
@@ -896,7 +902,9 @@
             Log.e(TAG, "Unknown type clicked: " + tag);
         }
 
-        AbstractFloatingView.closeAllOpenViews(this);
+        if (shouldCloseAllOpenViews) {
+            AbstractFloatingView.closeAllOpenViews(this);
+        }
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 41093bd..72add4f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -308,15 +308,12 @@
         // Pre-drag has ended, start the global system drag.
         if (mDisallowGlobalDrag) {
             AbstractFloatingView.closeAllOpenViewsExcept(mActivity, TYPE_TASKBAR_ALL_APPS);
-        } else {
-            AbstractFloatingView.closeAllOpenViews(mActivity);
+            return;
         }
-
         startSystemDrag((BubbleTextView) mDragObject.originalView);
     }
 
     private void startSystemDrag(BubbleTextView btv) {
-        if (mDisallowGlobalDrag) return;
         View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(btv) {
 
             @Override
@@ -412,6 +409,9 @@
                         .log(StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
             }
         }
+
+        // Wait to close until after system drag has started, if applicable.
+        AbstractFloatingView.closeAllOpenViews(mActivity);
     }
 
     private void onSystemDragStarted(BubbleTextView btv) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 19b9a18..4422fd4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -34,8 +34,6 @@
 import android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD
 import android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION
 import com.android.internal.policy.GestureNavigationSettingsObserver
-import com.android.launcher3.AbstractFloatingView
-import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY
 import com.android.launcher3.DeviceProfile
 import com.android.launcher3.R
 import com.android.launcher3.anim.AlphaUpdateListener
@@ -190,7 +188,7 @@
     /**
      * Called to update the touchable insets.
      *
-     * @see InternalInsetsInfo.setTouchableInsets
+     * @see ViewTreeObserver.InternalInsetsInfo.setTouchableInsets
      */
     fun updateInsetsTouchability(insetsInfo: ViewTreeObserver.InternalInsetsInfo) {
         insetsInfo.touchableRegion.setEmpty()
@@ -205,7 +203,7 @@
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
         } else if (
             controllers.navbarButtonsViewController.isImeVisible &&
-                controllers.taskbarStashController.isStashed()
+                controllers.taskbarStashController.isStashed
         ) {
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
         } else if (!controllers.uiController.isTaskbarTouchable) {
@@ -214,26 +212,16 @@
         } else if (controllers.taskbarDragController.isSystemDragInProgress) {
             // Let touches pass through us.
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
-        } else if (AbstractFloatingView.hasOpenView(context, TYPE_TASKBAR_OVERLAY_PROXY)) {
-            // Let touches pass through us if icons are hidden.
-            if (controllers.taskbarViewController.areIconsVisible()) {
-                insetsInfo.touchableRegion.set(touchableRegion)
-            }
-            insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
+        } else if (context.isTaskbarWindowFullscreen) {
+            // Intercept entire fullscreen window.
+            insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME)
+            insetsIsTouchableRegion = false
         } else if (
-            controllers.taskbarViewController.areIconsVisible() ||
-                AbstractFloatingView.hasOpenView(context, AbstractFloatingView.TYPE_ALL) ||
-                context.isNavBarKidsModeActive
+            controllers.taskbarViewController.areIconsVisible() || context.isNavBarKidsModeActive
         ) {
             // Taskbar has some touchable elements, take over the full taskbar area
-            insetsInfo.setTouchableInsets(
-                if (context.isTaskbarWindowFullscreen) {
-                    TOUCHABLE_INSETS_FRAME
-                } else {
-                    insetsInfo.touchableRegion.set(touchableRegion)
-                    TOUCHABLE_INSETS_REGION
-                }
-            )
+            insetsInfo.touchableRegion.set(touchableRegion)
+            insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
             insetsIsTouchableRegion = false
         } else {
             insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index ed78e2d..75cfd05 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -207,6 +207,10 @@
                     com.android.launcher3.taskbar.Utilities.setOverviewDragState(
                             mControllers, finalState.disallowTaskbarGlobalDrag(),
                             disallowLongClick, finalState.allowTaskbarInitialSplitSelection());
+                    // LauncherTaskbarUIController depends on the state when checking whether
+                    // to handle resume, so it should also be poked if current state changes
+                    mLauncher.getTaskbarUIController().onLauncherResumedOrPaused(
+                            mLauncher.hasBeenResumed());
                 }
             };
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 5de5904..c2175f2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -934,20 +934,6 @@
     }
 
     /**
-     * Resets the flag if no system gesture is in progress.
-     * <p>
-     * Otherwise, the reset should be deferred until after the gesture is finished.
-     *
-     * @see #setSystemGestureInProgress
-     */
-    public void resetFlagIfNoGestureInProgress(int flag) {
-        if (!mIsSystemGestureInProgress) {
-            updateStateForFlag(flag, false);
-            applyState(mControllers.taskbarOverlayController.getCloseDuration());
-        }
-    }
-
-    /**
      * When hiding the IME, delay the unstash animation to align with the end of the transition.
      */
     private long getTaskbarStashStartDelayForIme() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index a7e2daa..7429185 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -639,7 +639,7 @@
         public View.OnClickListener getAllAppsButtonClickListener() {
             return v -> {
                 mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP);
-                mControllers.taskbarAllAppsController.show();
+                mControllers.taskbarAllAppsController.toggle();
             };
         }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index 4266c71..459a658 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -100,9 +100,13 @@
         }
     }
 
-    /** Opens the {@link TaskbarAllAppsContainerView} in a new window. */
-    public void show() {
-        show(true);
+    /** Toggles visibility of {@link TaskbarAllAppsContainerView} in the overlay window. */
+    public void toggle() {
+        if (isOpen()) {
+            mSlideInView.close(true);
+        } else {
+            show(true);
+        }
     }
 
     /** Returns {@code true} if All Apps is open. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index 7a3b3e8..01342af 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.taskbar.allapps;
 
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_TASKBAR_ALL_APPS;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -88,8 +87,10 @@
     }
 
     private void setUpTaskbarStashing() {
-        mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, true);
-        mTaskbarStashController.applyState(mOverlayController.getOpenDuration());
+        if (DisplayController.isTransientTaskbar(mContext)) {
+            mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, true);
+            mTaskbarStashController.applyState(mOverlayController.getOpenDuration());
+        }
 
         mNavbarButtonsViewController.setSlideInViewVisible(true);
         mSlideInView.setOnCloseBeginListener(() -> {
@@ -100,11 +101,6 @@
             if (DisplayController.isTransientTaskbar(mContext)) {
                 mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
                 mTaskbarStashController.applyState(mOverlayController.getCloseDuration());
-            } else {
-                // Post in case view is closing due to gesture navigation. If a gesture is in
-                // progress, wait to unstash until after the gesture is finished.
-                MAIN_EXECUTOR.post(() -> mTaskbarStashController.resetFlagIfNoGestureInProgress(
-                        FLAG_STASHED_IN_TASKBAR_ALL_APPS));
             }
         });
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index 66d5918..84a5228 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -80,6 +80,12 @@
         return mOverlayController;
     }
 
+    /** Returns {@code true} if overlay or Taskbar windows are handling a system drag. */
+    boolean isAnySystemDragInProgress() {
+        return mDragController.isSystemDragInProgress()
+                || mTaskbarContext.getDragController().isSystemDragInProgress();
+    }
+
     @Override
     public DeviceProfile getDeviceProfile() {
         return mOverlayController.getLauncherDeviceProfile();
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
index 2c3e1ac..b4ec682 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -112,7 +112,7 @@
 
     @Override
     public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
-        if (mActivity.getDragController().isSystemDragInProgress()) {
+        if (mActivity.isAnySystemDragInProgress()) {
             inoutInfo.touchableRegion.setEmpty();
             inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
index e1ce9b1..b901a87 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
@@ -359,18 +359,6 @@
             return true;
         });
         sandboxCategory.addPreference(launchOverviewTutorialPreference);
-        Preference launchSandboxModeTutorialPreference = new Preference(context);
-        launchSandboxModeTutorialPreference.setKey("launchSandboxMode");
-        launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
-        launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures");
-        launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> {
-            startActivity(launchSandboxIntent
-                    .putExtra("use_tutorial_menu", false)
-                    .putExtra("tutorial_steps", new String[] {"SANDBOX_MODE"}));
-            return true;
-        });
-        sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
-
         Preference launchSecondaryDisplayPreference = new Preference(context);
         launchSecondaryDisplayPreference.setKey("launchSecondaryDisplay");
         launchSecondaryDisplayPreference.setTitle("Launch Secondary Display");
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 5333cbe..7d47945 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -2114,12 +2114,11 @@
     }
 
     protected void linkRecentsViewScroll() {
-        SurfaceTransactionApplier.create(mRecentsView, applier -> {
-            runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
-                            .setSyncTransactionApplier(applier));
-            runOnRecentsAnimationAndLauncherBound(() ->
-                    mRecentsAnimationTargets.addReleaseCheck(applier));
-        });
+        SurfaceTransactionApplier applier = new SurfaceTransactionApplier(mRecentsView);
+        runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
+                        .setSyncTransactionApplier(applier));
+        runOnRecentsAnimationAndLauncherBound(() ->
+                mRecentsAnimationTargets.addReleaseCheck(applier));
 
         mRecentsView.addOnScrollChangedListener(mOnRecentsScrollListener);
         runOnRecentsAnimationAndLauncherBound(() ->
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 913f08f..d798e62 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -145,6 +145,9 @@
      * @param filter Returns true if GroupTask should be in the list of considerations
      */
     public void isTaskRemoved(int taskId, Consumer<Boolean> callback, Predicate<GroupTask> filter) {
+        // Invalidate the existing list before checking to ensure this reflects the current state in
+        // the system
+        mTaskList.onRecentTasksChanged();
         mTaskList.getTasks(true /* loadKeysOnly */, (taskGroups) -> {
             for (GroupTask group : taskGroups) {
                 if (group.containsTask(taskId)) {
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 4b1dd43..074aedd 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -79,7 +79,7 @@
     }
 
     @Override
-    public void startHome() {
+    public void startHome(boolean animated) {
         mActivity.startHome();
         AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
     }
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java
deleted file mode 100644
index f0bd4f9..0000000
--- a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.interaction;
-
-import android.graphics.PointF;
-
-import com.android.launcher3.R;
-import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
-import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
-
-/** A {@link TutorialController} for the Sandbox Mode. */
-public class SandboxModeTutorialController extends SwipeUpGestureTutorialController {
-
-    SandboxModeTutorialController(SandboxModeTutorialFragment fragment, TutorialType tutorialType) {
-        super(fragment, tutorialType);
-    }
-
-    @Override
-    public void onBackGestureAttempted(BackGestureResult result) {
-        switch (result) {
-            case BACK_COMPLETED_FROM_LEFT:
-            case BACK_COMPLETED_FROM_RIGHT:
-                showRippleEffect(null);
-                showFeedback(R.string.sandbox_mode_back_gesture_feedback_successful);
-                break;
-            case BACK_CANCELLED_FROM_LEFT:
-            case BACK_CANCELLED_FROM_RIGHT:
-                showFeedback(R.string.back_gesture_feedback_cancelled);
-                break;
-            case BACK_NOT_STARTED_TOO_FAR_FROM_EDGE:
-                showFeedback(R.string.sandbox_mode_back_gesture_feedback_swipe_too_far_from_edge);
-                break;
-        }
-    }
-
-    @Override
-    public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
-        switch (result) {
-            case HOME_GESTURE_COMPLETED:
-                animateFakeTaskViewHome(finalVelocity, () -> {
-                    showFeedback(R.string.sandbox_mode_home_gesture_feedback_successful);
-                });
-                break;
-            case OVERVIEW_GESTURE_COMPLETED:
-                fadeOutFakeTaskView(true, true, () -> {
-                    showFeedback(R.string.sandbox_mode_overview_gesture_feedback_successful);
-                });
-                break;
-            case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
-            case HOME_OR_OVERVIEW_CANCELLED:
-            case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
-            case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE:
-                showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge);
-                break;
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java
deleted file mode 100644
index 7bd52f7..0000000
--- a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.interaction;
-
-import android.view.MotionEvent;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.logging.StatsLogManager;
-import com.android.quickstep.interaction.TutorialController.TutorialType;
-
-/** Shows the general navigation gesture sandbox environment. */
-public class SandboxModeTutorialFragment extends TutorialFragment {
-
-    public SandboxModeTutorialFragment(boolean fromTutorialMenu) {
-        super(fromTutorialMenu);
-    }
-
-    @Override
-    TutorialController createController(TutorialType type) {
-        return new SandboxModeTutorialController(this, type);
-    }
-
-    @Override
-    Class<? extends TutorialController> getControllerClass() {
-        return SandboxModeTutorialController.class;
-    }
-
-    @Override
-    public boolean onTouch(View view, MotionEvent motionEvent) {
-        if (motionEvent.getAction() == MotionEvent.ACTION_DOWN && mTutorialController != null) {
-            mTutorialController.setRippleHotspot(motionEvent.getX(), motionEvent.getY());
-        }
-        return super.onTouch(view, motionEvent);
-    }
-
-    @Override
-    void logTutorialStepShown(@NonNull StatsLogManager statsLogManager) {
-        // No-Op: tutorial step not currently shown to users
-    }
-
-    @Override
-    void logTutorialStepCompleted(@NonNull StatsLogManager statsLogManager) {
-        // No-Op: tutorial step not currently shown to users
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 6efdb07..d4ff457 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -829,9 +829,6 @@
         HOME_NAVIGATION,
         HOME_NAVIGATION_COMPLETE,
         OVERVIEW_NAVIGATION,
-        OVERVIEW_NAVIGATION_COMPLETE,
-        ASSISTANT,
-        ASSISTANT_COMPLETE,
-        SANDBOX_MODE
+        OVERVIEW_NAVIGATION_COMPLETE
     }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 25de605..9f15e19 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -117,8 +117,6 @@
             case OVERVIEW_NAVIGATION:
             case OVERVIEW_NAVIGATION_COMPLETE:
                 return new OverviewGestureTutorialFragment(fromTutorialMenu);
-            case SANDBOX_MODE:
-                return new SandboxModeTutorialFragment(fromTutorialMenu);
             default:
                 Log.e(LOG_TAG, "Failed to find an appropriate fragment for " + tutorialType.name());
         }
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 95473dc..bb028a7 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -22,13 +22,11 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewRootImpl;
 
 import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
 
-import java.util.function.Consumer;
-
-
 /**
  * Helper class to apply surface transactions in sync with RenderThread similar to
  *   android.view.SyncRtSurfaceTransactionApplier
@@ -39,22 +37,47 @@
 
     private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
 
-    private final SurfaceControl mBarrierSurfaceControl;
-    private final ViewRootImpl mTargetViewRootImpl;
     private final Handler mApplyHandler;
 
+    private boolean mInitialized;
+    private SurfaceControl mBarrierSurfaceControl;
+    private ViewRootImpl mTargetViewRootImpl;
+
     private int mLastSequenceNumber = 0;
 
     /**
      * @param targetView The view in the surface that acts as synchronization anchor.
      */
     public SurfaceTransactionApplier(View targetView) {
-        mTargetViewRootImpl = targetView.getViewRootImpl();
-        mBarrierSurfaceControl = mTargetViewRootImpl.getSurfaceControl();
+        if (targetView.isAttachedToWindow()) {
+            initialize(targetView);
+        } else {
+            mInitialized = false;
+            targetView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View v) {
+                    if (!mInitialized) {
+                        targetView.removeOnAttachStateChangeListener(this);
+                        initialize(targetView);
+                    }
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                    // Do nothing
+                }
+            });
+        }
         mApplyHandler = new Handler(this::onApplyMessage);
         setCanRelease(true);
     }
 
+    private void initialize(View view) {
+        mTargetViewRootImpl = view.getViewRootImpl();
+        mBarrierSurfaceControl = mTargetViewRootImpl.getSurfaceControl();
+        mInitialized = true;
+    }
+
     protected boolean onApplyMessage(Message msg) {
         if (msg.what == MSG_UPDATE_SEQUENCE_NUMBER) {
             setCanRelease(msg.arg1 == mLastSequenceNumber);
@@ -70,6 +93,10 @@
      *               this method to avoid synchronization issues.
      */
     public void scheduleApply(SurfaceTransaction params) {
+        if (!mInitialized) {
+            params.getTransaction().apply();
+            return;
+        }
         View view = mTargetViewRootImpl.getView();
         if (view == null) {
             return;
@@ -93,33 +120,4 @@
         // Make sure a frame gets scheduled.
         view.invalidate();
     }
-
-    /**
-     * Creates an instance of SurfaceTransactionApplier, deferring until the target view is
-     * attached if necessary.
-     */
-    public static void create(
-            final View targetView, final Consumer<SurfaceTransactionApplier> callback) {
-        if (targetView == null) {
-            // No target view, no applier
-            callback.accept(null);
-        } else if (targetView.isAttachedToWindow()) {
-            // Already attached, we're good to go
-            callback.accept(new SurfaceTransactionApplier(targetView));
-        } else {
-            // Haven't been attached before we can get the view root
-            targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
-                @Override
-                public void onViewAttachedToWindow(View v) {
-                    targetView.removeOnAttachStateChangeListener(this);
-                    callback.accept(new SurfaceTransactionApplier(targetView));
-                }
-
-                @Override
-                public void onViewDetachedFromWindow(View v) {
-                    // Do nothing
-                }
-            });
-        }
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/TaskRemovedDuringLaunchListener.java b/quickstep/src/com/android/quickstep/util/TaskRemovedDuringLaunchListener.java
new file mode 100644
index 0000000..c22e0bc
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TaskRemovedDuringLaunchListener.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
+import android.app.Activity;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
+import com.android.quickstep.RecentsModel;
+
+/**
+ * This class tracks the failure of a task launch through the TaskView.launchTask() call, in an
+ * edge case in which starting a new task may initially succeed (startActivity returns true), but
+ * the launch ultimately fails if the activity finishes while it is resuming.
+ *
+ * There are two signals this class checks, the launcher lifecycle and the transition completion.
+ * If we hit either of those signals and the task is no longer valid, then the registered failure
+ * callback will be notified.
+ */
+public class TaskRemovedDuringLaunchListener implements ActivityLifecycleCallbacksAdapter {
+
+    private Activity mActivity;
+    private int mLaunchedTaskId = INVALID_TASK_ID;
+    private Runnable mTaskLaunchFailedCallback = null;
+
+    /**
+     * Registers a failure listener callback if it detects a scenario in which an app launch
+     * failed before the transition finished.
+     */
+    public void register(Activity activity, int launchedTaskId,
+            @NonNull Runnable taskLaunchFailedCallback) {
+        activity.registerActivityLifecycleCallbacks(this);
+        mActivity = activity;
+        mLaunchedTaskId = launchedTaskId;
+        mTaskLaunchFailedCallback = taskLaunchFailedCallback;
+    }
+
+    /**
+     * Unregisters the failure listener.
+     */
+    private void unregister() {
+        mActivity.unregisterActivityLifecycleCallbacks(this);
+        mActivity = null;
+        mLaunchedTaskId = INVALID_TASK_ID;
+        mTaskLaunchFailedCallback = null;
+    }
+
+    /**
+     * Called when the transition finishes.
+     */
+    public void onTransitionFinished() {
+        // The transition finished and Launcher was not stopped, check if the launch failed
+        checkTaskLaunchFailed();
+    }
+
+    @Override
+    public void onActivityStopped(Activity activity) {
+        // The normal task launch case, Launcher stops and updates its state correctly
+        unregister();
+    }
+
+    @Override
+    public void onActivityResumed(Activity activity) {
+        // The transition hasn't finished but Launcher was resumed, check if the launch failed
+        checkTaskLaunchFailed();
+    }
+
+    @Override
+    public void onActivityDestroyed(Activity activity) {
+        // If we somehow don't get any of the above signals, then just unregister this listener
+        unregister();
+    }
+
+    private void checkTaskLaunchFailed() {
+        if (mLaunchedTaskId != INVALID_TASK_ID) {
+            final int launchedTaskId = mLaunchedTaskId;
+            final Runnable taskLaunchFailedCallback = mTaskLaunchFailedCallback;
+            RecentsModel.INSTANCE.getNoCreate().isTaskRemoved(mLaunchedTaskId, (taskRemoved) -> {
+                if (taskRemoved) {
+                    ActiveGestureLog.INSTANCE.addLog("Launch failed, task (id=" + launchedTaskId
+                            + ") finished mid transition");
+                    taskLaunchFailedCallback.run();
+                }
+            }, (task) -> true /* filter */);
+            unregister();
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index 379722b..1cfaf14 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -321,7 +321,7 @@
     }
 
     @Override
-    public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
+    public void launchTask(@NonNull Consumer<Boolean> callback, boolean isQuickswitch) {
         launchTasks();
         callback.accept(true);
     }
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 5bfd035..c6c84bd 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -237,9 +237,9 @@
     }
 
     @Override
-    public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
+    public void launchTask(@NonNull Consumer<Boolean> callback, boolean isQuickswitch) {
         getRecentsView().getSplitSelectController().launchTasks(mTask.key.id, mSecondaryTask.key.id,
-                SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList,
+                SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, callback, isQuickswitch,
                 getSplitRatio());
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 8ff0e9b..2008129 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statehandlers.DesktopVisibilityController;
+import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.PendingSplitSelectInfo;
@@ -79,9 +80,11 @@
     }
 
     @Override
-    public void startHome() {
-        mActivity.getStateManager().goToState(NORMAL);
-        AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
+    public void startHome(boolean animated) {
+        StateManager stateManager = mActivity.getStateManager();
+        animated &= stateManager.shouldAnimateStateChange();
+        stateManager.goToState(NORMAL, animated);
+        AbstractFloatingView.closeAllOpenViews(mActivity, animated);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f0afa69..ff5af28 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2285,7 +2285,11 @@
         }
     }
 
-    public abstract void startHome();
+    public void startHome() {
+        startHome(mActivity.isStarted());
+    }
+
+    public abstract void startHome(boolean animated);
 
     public void reset() {
         setCurrentTask(-1);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 53660b5..796cd62 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -102,6 +102,7 @@
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.SplitSelectStateController;
 import com.android.quickstep.util.TaskCornerRadius;
+import com.android.quickstep.util.TaskRemovedDuringLaunchListener;
 import com.android.quickstep.util.TransformParams;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -815,23 +816,44 @@
      * Starts the task associated with this view without any animation
      */
     public void launchTask(@NonNull Consumer<Boolean> callback) {
-        launchTask(callback, false /* freezeTaskList */);
+        launchTask(callback, false /* isQuickswitch */);
     }
 
     /**
      * Starts the task associated with this view without any animation
      */
-    public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
+    public void launchTask(@NonNull Consumer<Boolean> callback, boolean isQuickswitch) {
         if (mTask != null) {
             TestLogging.recordEvent(
                     TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
 
+            final TaskRemovedDuringLaunchListener
+                    failureListener = new TaskRemovedDuringLaunchListener();
+            if (isQuickswitch) {
+                // We only listen for failures to launch in quickswitch because the during this
+                // gesture launcher is in the background state, vs other launches which are in
+                // the actual overview state
+                failureListener.register(mActivity, mTask.key.id, () -> {
+                    notifyTaskLaunchFailed(TAG);
+                    // Disable animations for now, as it is an edge case and the app usually covers
+                    // launcher and also any state transition animation also gets clobbered by
+                    // QuickstepTransitionManager.createWallpaperOpenAnimations when launcher
+                    // shows again
+                    getRecentsView().startHome(false /* animated */);
+                });
+            }
             // Indicate success once the system has indicated that the transition has started
-            ActivityOptions opts = makeCustomAnimation(getContext(), 0, 0,
-                    () -> callback.accept(true), MAIN_EXECUTOR.getHandler());
+            ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(getContext(), 0, 0,
+                    MAIN_EXECUTOR.getHandler(),
+                    elapsedRealTime -> {
+                        callback.accept(true);
+                    },
+                    elapsedRealTime -> {
+                        failureListener.onTransitionFinished();
+                    });
             opts.setLaunchDisplayId(
                     getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
-            if (freezeTaskList) {
+            if (isQuickswitch) {
                 opts.setFreezeRecentTasksReordering();
             }
             opts.setDisableStartingWindow(mSnapshotView.shouldShowSplashView());
diff --git a/res/layout/widgets_bottom_sheet_content.xml b/res/layout/widgets_bottom_sheet_content.xml
index b76eef7..065c2ed 100644
--- a/res/layout/widgets_bottom_sheet_content.xml
+++ b/res/layout/widgets_bottom_sheet_content.xml
@@ -42,7 +42,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:fadeScrollbars="false"
-            android:layout_marginVertical="16dp">
+            android:layout_marginTop="16dp">
             <include layout="@layout/widgets_table_container"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
diff --git a/src/com/android/launcher3/DropTargetHandler.kt b/src/com/android/launcher3/DropTargetHandler.kt
index 277f8b3..6560e16 100644
--- a/src/com/android/launcher3/DropTargetHandler.kt
+++ b/src/com/android/launcher3/DropTargetHandler.kt
@@ -6,7 +6,6 @@
 import com.android.launcher3.SecondaryDropTarget.DeferredOnComplete
 import com.android.launcher3.dragndrop.DragLayer
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent
-import com.android.launcher3.model.ModelWriter
 import com.android.launcher3.model.data.ItemInfo
 import com.android.launcher3.model.data.LauncherAppWidgetInfo
 import com.android.launcher3.util.IntSet
@@ -22,8 +21,6 @@
 class DropTargetHandler(launcher: Launcher) {
     val mLauncher: Launcher = launcher
 
-    val modelWriter: ModelWriter = mLauncher.modelWriter
-
     fun onDropAnimationComplete() {
         mLauncher.stateManager.goToState(LauncherState.NORMAL)
     }
@@ -87,7 +84,7 @@
             else mLauncher.workspace.currentPageScreenIds
         val onUndoClicked = Runnable {
             mLauncher.setPagesToBindSynchronously(pageIds)
-            modelWriter.abortDelete()
+            mLauncher.modelWriter.abortDelete()
             mLauncher.statsLogManager.logger().log(LauncherEvent.LAUNCHER_UNDO)
         }
 
@@ -95,7 +92,7 @@
             mLauncher,
             R.string.item_removed,
             R.string.undo,
-            modelWriter::commitDelete,
+            mLauncher.modelWriter::commitDelete,
             onUndoClicked
         )
     }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4764d72..29b0f08 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1621,6 +1621,9 @@
         return mModel;
     }
 
+    /**
+     * Returns the ModelWriter writer, make sure to call the function every time you want to use it.
+     */
     public ModelWriter getModelWriter() {
         return mModelWriter;
     }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index fef6639..7f04860 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -253,7 +253,7 @@
             "COLLECT_SEARCH_HISTORY", DISABLED, "Allow launcher to collect search history for log");
 
     public static final BooleanFlag ENABLE_TWOLINE_ALLAPPS = getDebugFlag(270390937,
-            "ENABLE_TWOLINE_ALLAPPS", DISABLED, "Enables two line label inside all apps.");
+            "ENABLE_TWOLINE_ALLAPPS", TEAMFOOD, "Enables two line label inside all apps.");
 
     public static final BooleanFlag IME_STICKY_SNACKBAR_EDU = getDebugFlag(270391693,
             "IME_STICKY_SNACKBAR_EDU", ENABLED, "Show sticky IME edu in AllApps");
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 4906c1d..00f4285 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -304,7 +304,8 @@
         mWidgetOptions = pendingInfo.getDefaultSizeOptions(this);
         mWidgetCell.getWidgetView().setTag(pendingInfo);
 
-        applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));
+        applyWidgetItemAsync(() -> new WidgetItem(
+                widgetInfo, mIdp, mApp.getIconCache(), mApp.getContext()));
         return WidgetsModel.newPendingItemInfo(this, widgetInfo.getComponent(),
                 widgetInfo.getUser());
     }
diff --git a/src/com/android/launcher3/logging/StartupLatencyLogger.kt b/src/com/android/launcher3/logging/StartupLatencyLogger.kt
index 27b8c3b..93e9de5 100644
--- a/src/com/android/launcher3/logging/StartupLatencyLogger.kt
+++ b/src/com/android/launcher3/logging/StartupLatencyLogger.kt
@@ -21,14 +21,13 @@
         const val UNSET_LONG = -1L
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     val startTimeByEvent = SparseLongArray()
-    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     val endTimeByEvent = SparseLongArray()
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
-    var cardinality: Int = UNSET_INT
-    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) var cardinality: Int = UNSET_INT
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     var workspaceLoadStartTime: Long = UNSET_LONG
 
     private var isInTest = false
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index 7198d54..c99b889 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -3,6 +3,7 @@
 import static com.android.launcher3.Utilities.ATLEAST_S;
 
 import android.annotation.SuppressLint;
+import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
@@ -25,13 +26,15 @@
     public final ShortcutConfigActivityInfo activityInfo;
 
     public final String label;
+    public final CharSequence description;
     public final int spanX, spanY;
 
     public WidgetItem(LauncherAppWidgetProviderInfo info,
-            InvariantDeviceProfile idp, IconCache iconCache) {
+            InvariantDeviceProfile idp, IconCache iconCache, Context context) {
         super(info.provider, info.getProfile());
 
         label = iconCache.getTitleNoCache(info);
+        description = ATLEAST_S ? info.loadDescription(context) : null;
         widgetInfo = info;
         activityInfo = null;
 
@@ -43,6 +46,7 @@
         super(info.getComponent(), info.getUser());
         label = info.isPersistable() ? iconCache.getTitleNoCache(info) :
                 Utilities.trim(info.getLabel(pm));
+        description = null;
         widgetInfo = null;
         activityInfo = info;
         spanX = spanY = 1;
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 43218a1..c30342a 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.widget;
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
-import static com.android.launcher3.Utilities.ATLEAST_S;
 import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.fromProviderInfo;
 import static com.android.launcher3.widget.util.WidgetSizes.getWidgetItemSizePx;
 
@@ -25,6 +24,7 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.os.Process;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Size;
@@ -219,14 +219,11 @@
                 mItem.spanX, mItem.spanY));
         mWidgetDims.setContentDescription(context.getString(
                 R.string.widget_accessible_dims_format, mItem.spanX, mItem.spanY));
-        if (ATLEAST_S && mItem.widgetInfo != null) {
-            CharSequence description = mItem.widgetInfo.loadDescription(context);
-            if (description != null && description.length() > 0) {
-                mWidgetDescription.setText(description);
-                mWidgetDescription.setVisibility(VISIBLE);
-            } else {
-                mWidgetDescription.setVisibility(GONE);
-            }
+        if (!TextUtils.isEmpty(mItem.description)) {
+            mWidgetDescription.setText(mItem.description);
+            mWidgetDescription.setVisibility(VISIBLE);
+        } else {
+            mWidgetDescription.setVisibility(GONE);
         }
 
         if (item.activityInfo != null) {
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 23cdae9..846dafd 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -252,8 +252,11 @@
         super.setInsets(insets);
         int bottomPadding = Math.max(insets.bottom, mNavBarScrimHeight);
 
-        mContent.setPadding(mContent.getPaddingStart(),
-                mContent.getPaddingTop(), mContent.getPaddingEnd(),
+        View widgetsTable = findViewById(R.id.widgets_table);
+        widgetsTable.setPadding(
+                widgetsTable.getPaddingLeft(),
+                widgetsTable.getPaddingTop(),
+                widgetsTable.getPaddingRight(),
                 bottomPadding);
         if (bottomPadding > 0) {
             setupNavBarColor();
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 1b743e8..2f16065 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -129,7 +129,7 @@
                         LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo);
 
                 widgetsAndShortcuts.add(new WidgetItem(
-                        launcherWidgetInfo, idp, app.getIconCache()));
+                        launcherWidgetInfo, idp, app.getIconCache(), app.getContext()));
                 updatedItems.add(launcherWidgetInfo);
             }
 
@@ -200,7 +200,8 @@
                                     app.getContext().getPackageManager()));
                         } else {
                             items.set(i, new WidgetItem(item.widgetInfo,
-                                    app.getInvariantDeviceProfile(), app.getIconCache()));
+                                    app.getInvariantDeviceProfile(), app.getIconCache(),
+                                    app.getContext()));
                         }
                     }
                 }
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
index 76492ba..8fc4481 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
@@ -147,7 +147,7 @@
 
             widgetItems.add(new WidgetItem(
                     LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
-                    mTestProfile, mIconCache));
+                    mTestProfile, mIconCache, mContext));
         }
         return widgetItems;
     }
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
index e0101f5..60590e7 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -144,7 +144,7 @@
 
             widgetItems.add(new WidgetItem(
                     LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
-                    mTestProfile, mIconCache));
+                    mTestProfile, mIconCache, mContext));
         }
         return widgetItems;
     }
diff --git a/tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java b/tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
index d8f1f14..7552619 100644
--- a/tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
@@ -26,6 +26,7 @@
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
+import android.content.Context;
 import android.os.UserHandle;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -249,12 +250,13 @@
         String label = mWidgetsToLabels.get(componentName);
         AppWidgetProviderInfo widgetInfo = createAppWidgetProviderInfo(componentName);
 
+        Context context = getApplicationContext();
         LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
-                LauncherAppWidgetProviderInfo.fromProviderInfo(getApplicationContext(), widgetInfo);
+                LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo);
         launcherAppWidgetProviderInfo.spanX = spanX;
         launcherAppWidgetProviderInfo.spanY = spanY;
         launcherAppWidgetProviderInfo.label = label;
 
-        return new WidgetItem(launcherAppWidgetProviderInfo, mTestProfile, mIconCache);
+        return new WidgetItem(launcherAppWidgetProviderInfo, mTestProfile, mIconCache, context);
     }
 }
diff --git a/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java b/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
index 0124f73..9c03ccf 100644
--- a/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
@@ -202,7 +202,7 @@
 
             WidgetItem widgetItem = new WidgetItem(
                     LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
-                    mTestProfile, mIconCache);
+                    mTestProfile, mIconCache, mContext);
             widgetItems.add(widgetItem);
         }
         return widgetItems;
diff --git a/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
index 834f851..2c5a396 100644
--- a/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
@@ -218,19 +218,18 @@
                 new Point(2, 4), new Point(4, 4));
 
         ArrayList<WidgetItem> widgetItems = new ArrayList<>();
-        widgetSizes.stream().forEach(
-                widgetSize -> {
-                    AppWidgetProviderInfo info = createAppWidgetProviderInfo(
-                            ComponentName.createRelative(
-                                    TEST_PACKAGE,
-                                    ".WidgetProvider_" + widgetSize.x + "x" + widgetSize.y));
-                    LauncherAppWidgetProviderInfo widgetInfo =
-                            LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
-                    widgetInfo.spanX = widgetSize.x;
-                    widgetInfo.spanY = widgetSize.y;
-                    widgetItems.add(new WidgetItem(widgetInfo, mTestInvariantProfile, mIconCache));
-                }
-        );
+        widgetSizes.stream().forEach(widgetSize -> {
+            AppWidgetProviderInfo info = createAppWidgetProviderInfo(
+                    ComponentName.createRelative(
+                            TEST_PACKAGE,
+                            ".WidgetProvider_" + widgetSize.x + "x" + widgetSize.y));
+            LauncherAppWidgetProviderInfo widgetInfo =
+                    LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+            widgetInfo.spanX = widgetSize.x;
+            widgetInfo.spanY = widgetSize.y;
+            widgetItems.add(new WidgetItem(
+                    widgetInfo, mTestInvariantProfile, mIconCache, mContext));
+        });
         mWidget1x1 = widgetItems.get(0);
         mWidget2x2 = widgetItems.get(1);
         mWidget2x3 = widgetItems.get(2);
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 2687b28..0a0cf07 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -37,7 +37,8 @@
     }
 
     static BySelector getAppIconSelector(String appName, LauncherInstrumentation launcher) {
-        return By.clazz(TextView.class).text(appName).pkg(launcher.getLauncherPackageName());
+        return By.clazz(TextView.class).textContains(appName)
+                .pkg(launcher.getLauncherPackageName());
     }
 
     static BySelector getAnyAppIconSelector() {