Merge "Fixed issue with shadow for overflow icon" into main
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index 694475a..84c2ed2 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -28,14 +28,15 @@
 import android.text.TextPaint;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.ColorInt;
 import androidx.core.content.ContextCompat;
 
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.FloatingHeaderRow;
 import com.android.launcher3.allapps.FloatingHeaderView;
-import com.android.launcher3.util.Themes;
 
 /**
  * A view which shows a horizontal divider
@@ -54,6 +55,7 @@
 
     private final @ColorInt int mStrokeColor;
     private final @ColorInt int mAllAppsLabelTextColor;
+    private final AccessibilityManager mAccessibilityManager;
 
     private Layout mAllAppsLabelLayout;
     private boolean mShowAllAppsLabel;
@@ -87,7 +89,8 @@
         mAllAppsLabelTextColor = ContextCompat.getColor(context,
                 R.color.material_color_on_surface_variant);
 
-        mShowAllAppsLabel = !ALL_APPS_VISITED_COUNT.hasReachedMax(context);
+        mAccessibilityManager = AccessibilityManager.getInstance(context);
+        setShowAllAppsLabel(!ALL_APPS_VISITED_COUNT.hasReachedMax(context));
     }
 
     public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
@@ -99,6 +102,9 @@
 
     /** {@code true} if all apps label should be shown in place of divider. */
     public void setShowAllAppsLabel(boolean showAllAppsLabel) {
+        if (mAccessibilityManager.isEnabled() && !Utilities.isRunningInTestHarness()) {
+            showAllAppsLabel = true;
+        }
         if (showAllAppsLabel != mShowAllAppsLabel) {
             mShowAllAppsLabel = showAllAppsLabel;
             updateDividerType();
@@ -148,6 +154,7 @@
             mDividerType = dividerType;
             int topPadding;
             int bottomPadding;
+            setContentDescription(null);
             switch (dividerType) {
                 case LINE:
                     topPadding = 0;
@@ -161,6 +168,7 @@
                     bottomPadding = getResources()
                             .getDimensionPixelSize(R.dimen.all_apps_label_bottom_padding);
                     mPaint.setColor(mAllAppsLabelTextColor);
+                    setContentDescription(mAllAppsLabelLayout.getText());
                     break;
                 case NONE:
                 default:
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 6163dad..95c4e25 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -153,6 +153,16 @@
                 context.deviceProfile.widthPx,
                 windowLayoutParams.height
             )
+
+            // if there's an animating bubble add it to the touch region so that it's clickable
+            val animatingBubbleBounds =
+                controllers.bubbleControllers
+                    .getOrNull()
+                    ?.bubbleBarViewController
+                    ?.animatingBubbleBounds
+            if (animatingBubbleBounds != null) {
+                defaultTouchableRegion.op(animatingBubbleBounds, Region.Op.UNION)
+            }
         }
 
         // Pre-calculate insets for different providers across different rotations for this gravity
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index e47640b..3196bfb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -62,6 +62,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks;
 import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.DisplayController;
@@ -70,7 +71,6 @@
 import com.android.quickstep.AllAppsActionManager;
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.AssistUtils;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
@@ -209,15 +209,18 @@
 
     @SuppressLint("WrongConstant")
     public TaskbarManager(
-            TouchInteractionService service, AllAppsActionManager allAppsActionManager) {
+            Context context,
+            AllAppsActionManager allAppsActionManager,
+            TaskbarNavButtonCallbacks navCallbacks) {
+
         Display display =
-                service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
-        mContext = service.createWindowContext(display,
+                context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
+        mContext = context.createWindowContext(display,
                 ENABLE_TASKBAR_NAVBAR_UNIFICATION ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL,
                 null);
         mAllAppsActionManager = allAppsActionManager;
         mNavigationBarPanelContext = ENABLE_TASKBAR_NAVBAR_UNIFICATION
-                ? service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null)
+                ? context.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null)
                 : null;
         if (enableTaskbarNoRecreate()) {
             mWindowManager = mContext.getSystemService(WindowManager.class);
@@ -234,8 +237,11 @@
                 }
             };
         }
-        mNavButtonController = new TaskbarNavButtonController(service,
-                SystemUiProxy.INSTANCE.get(mContext), new Handler(),
+        mNavButtonController = new TaskbarNavButtonController(
+                context,
+                navCallbacks,
+                SystemUiProxy.INSTANCE.get(mContext),
+                new Handler(),
                 AssistUtils.newInstance(mContext));
         mComponentCallbacks = new ComponentCallbacks() {
             private Configuration mOldConfig = mContext.getResources().getConfiguration();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 03f55ca..ade4649 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -31,6 +31,7 @@
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 
+import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
@@ -47,10 +48,8 @@
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.quickstep.LauncherActivityInterface;
-import com.android.quickstep.OverviewCommandHelper;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskUtils;
-import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.AssistUtils;
 
 import java.io.PrintWriter;
@@ -106,7 +105,8 @@
     private static final int SCREEN_UNPIN_COMBO = BUTTON_BACK | BUTTON_RECENTS;
     private int mLongPressedButtons = 0;
 
-    private final TouchInteractionService mService;
+    private final Context mContext;
+    private final TaskbarNavButtonCallbacks mCallbacks;
     private final SystemUiProxy mSystemUiProxy;
     private final Handler mHandler;
     private final AssistUtils mAssistUtils;
@@ -114,9 +114,14 @@
 
     private final Runnable mResetLongPress = this::resetScreenUnpin;
 
-    public TaskbarNavButtonController(TouchInteractionService service,
-            SystemUiProxy systemUiProxy, Handler handler, AssistUtils assistUtils) {
-        mService = service;
+    public TaskbarNavButtonController(
+            Context context,
+            TaskbarNavButtonCallbacks callbacks,
+            SystemUiProxy systemUiProxy,
+            Handler handler,
+            AssistUtils assistUtils) {
+        mContext = context;
+        mCallbacks = callbacks;
         mSystemUiProxy = systemUiProxy;
         mHandler = handler;
         mAssistUtils = assistUtils;
@@ -286,7 +291,7 @@
             desktopVisibilityController.onHomeActionTriggered();
         }
 
-        mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME);
+        mCallbacks.onNavigateHome();
     }
 
     private void navigateToOverview() {
@@ -295,7 +300,7 @@
         }
         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
         TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-        mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_TOGGLE);
+        mCallbacks.onToggleOverview();
     }
 
     private void executeBack() {
@@ -310,7 +315,7 @@
         if (longClick) {
             mSystemUiProxy.notifyAccessibilityButtonLongClicked();
         } else {
-            mSystemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
+            mSystemUiProxy.notifyAccessibilityButtonClicked(mContext.getDisplayId());
         }
     }
 
@@ -333,4 +338,13 @@
     private void showNotifications() {
         mSystemUiProxy.toggleNotificationPanel();
     }
+
+    /** Callbacks for navigation buttons on Taskbar. */
+    public interface TaskbarNavButtonCallbacks {
+        /** Callback invoked when the home button is pressed. */
+        default void onNavigateHome() {}
+
+        /** Callback invoked when the overview button is pressed. */
+        default void onToggleOverview() {}
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index d8a8b29..5234936 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -104,6 +104,8 @@
      * updates the bounds and accounts for translation.
      */
     private final Rect mBubbleBarBounds = new Rect();
+    /** The bounds of the animating bubble in the coordinate space of the BubbleBarView. */
+    private final Rect mAnimatingBubbleBounds = new Rect();
     // The amount the bubbles overlap when they are stacked in the bubble bar
     private final float mIconOverlapAmount;
     // The spacing between the bubbles when bubble bar is expanded
@@ -460,6 +462,30 @@
         return mBubbleBarBounds;
     }
 
+    /** Returns the bounds of the animating bubble, or {@code null} if no bubble is animating. */
+    @Nullable
+    public Rect getAnimatingBubbleBounds() {
+        if (mIsAnimatingNewBubble) {
+            return mAnimatingBubbleBounds;
+        }
+        return null;
+    }
+
+    /**
+     * Updates the animating bubble bounds. This should be called when the bubble is fully animated
+     * in so that we can include it in taskbar touchable region.
+     *
+     * <p>The bounds are adjusted to the coordinate space of BubbleBarView so that it can be used
+     * by taskbar.
+     */
+    public void updateAnimatingBubbleBounds(int left, int top, int width, int height) {
+        Rect bubbleBarBounds = getBubbleBarBounds();
+        mAnimatingBubbleBounds.left = bubbleBarBounds.left + left;
+        mAnimatingBubbleBounds.top = bubbleBarBounds.top + top;
+        mAnimatingBubbleBounds.right = mAnimatingBubbleBounds.left + width;
+        mAnimatingBubbleBounds.bottom = mAnimatingBubbleBounds.top + height;
+    }
+
     /**
      * Set bubble bar relative pivot value for X and Y, applied as a fraction of view width/height
      * respectively. If the value is not in range of 0 to 1 it will be normalized.
@@ -856,10 +882,15 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (!mIsBarExpanded) {
+        if (!mIsBarExpanded && !mIsAnimatingNewBubble) {
             // When the bar is collapsed, all taps on it should expand it.
             return true;
         }
         return super.onInterceptTouchEvent(ev);
     }
+
+    /** Whether a new bubble is currently animating. */
+    public boolean isAnimatingNewBubble() {
+        return mIsAnimatingNewBubble;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 0e62eaf..3c46f32 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -138,6 +138,15 @@
         if (bubble == null) {
             Log.e(TAG, "bubble click listener, bubble was null");
         }
+
+        if (mBarView.isAnimatingNewBubble()) {
+            mBubbleBarViewAnimator.onBubbleClickedWhileAnimating();
+            mBubbleStashController.showBubbleBarImmediate();
+            setExpanded(true);
+            mBubbleBarController.showAndSelectBubble(bubble);
+            return;
+        }
+
         final String currentlySelected = mBubbleBarController.getSelectedBubbleKey();
         if (mBarView.isExpanded() && Objects.equals(bubble.getKey(), currentlySelected)) {
             // Tapping the currently selected bubble while expanded collapses the view.
@@ -213,6 +222,12 @@
         return mBarView.getBubbleBarBounds();
     }
 
+    /** The bounds of the animating bubble, or {@code null} if no bubble is animating. */
+    @Nullable
+    public Rect getAnimatingBubbleBounds() {
+        return mBarView.getAnimatingBubbleBounds();
+    }
+
     /** The horizontal margin of the bubble bar from the edge of the screen. */
     public int getHorizontalMargin() {
         return mBarView.getHorizontalMargin();
@@ -373,7 +388,7 @@
 
             boolean isInApp = mTaskbarStashController.isInApp();
             // only animate the new bubble if we're in an app and not auto expanding
-            if (b instanceof BubbleBarBubble && isInApp && !isExpanding) {
+            if (b instanceof BubbleBarBubble && isInApp && !isExpanding && !isExpanded()) {
                 mBubbleBarViewAnimator.animateBubbleInForStashed((BubbleBarBubble) b);
             }
         } else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
index 76d86de..bea0af8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
@@ -347,7 +347,7 @@
                 hotseatCellHeight - mUnstashedHeight) / 2;
     }
 
-    float getBubbleBarTranslationY() {
+    public float getBubbleBarTranslationY() {
         // If we're on home, adjust the translation so the bubble bar aligns with hotseat.
         // Otherwise we're either showing in an app or in overview. In either case adjust it so
         // the bubble bar aligns with the taskbar.
@@ -374,4 +374,19 @@
     public PhysicsAnimator<View> getStashedHandlePhysicsAnimator() {
         return mHandleViewController.getPhysicsAnimator();
     }
+
+    /** Notifies taskbar that it should update its touchable region. */
+    public void updateTaskbarTouchRegion() {
+        mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
+    }
+
+    /** Shows the bubble bar immediately without animation. */
+    public void showBubbleBarImmediate() {
+        mHandleViewController.setTranslationYForSwipe(0);
+        mIconTranslationYForStash.updateValue(getBubbleBarTranslationY());
+        mIconAlphaForStash.setValue(1);
+        mIconScaleForStash.updateValue(1);
+        mIsStashed = false;
+        onIsStashedChanged();
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
index 2d8983f..da36944 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
@@ -35,6 +35,8 @@
     private val scheduler: Scheduler = HandlerScheduler(bubbleBarView)
 ) {
 
+    private var animatingBubble: AnimatingBubble? = null
+
     private companion object {
         /** The time to show the flyout. */
         const val FLYOUT_DELAY_MS: Long = 2500
@@ -54,26 +56,40 @@
         const val BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y = -20f
     }
 
+    /** Wrapper around the animating bubble with its show and hide animations. */
+    private data class AnimatingBubble(
+        val bubbleView: BubbleView,
+        val showAnimation: Runnable,
+        val hideAnimation: Runnable
+    )
+
     /** An interface for scheduling jobs. */
     interface Scheduler {
 
         /** Schedule the given [block] to run. */
-        fun post(block: () -> Unit)
+        fun post(block: Runnable)
 
         /** Schedule the given [block] to start with a delay of [delayMillis]. */
-        fun postDelayed(delayMillis: Long, block: () -> Unit)
+        fun postDelayed(delayMillis: Long, block: Runnable)
+
+        /** Cancel the given [block] if it hasn't started yet. */
+        fun cancel(block: Runnable)
     }
 
     /** A [Scheduler] that uses a Handler to run jobs. */
     private class HandlerScheduler(private val view: View) : Scheduler {
 
-        override fun post(block: () -> Unit) {
+        override fun post(block: Runnable) {
             view.post(block)
         }
 
-        override fun postDelayed(delayMillis: Long, block: () -> Unit) {
+        override fun postDelayed(delayMillis: Long, block: Runnable) {
             view.postDelayed(block, delayMillis)
         }
+
+        override fun cancel(block: Runnable) {
+            view.removeCallbacks(block)
+        }
     }
 
     private val springConfig =
@@ -91,6 +107,7 @@
         // and the second part hides it after a delay.
         val showAnimation = buildShowAnimation(bubbleView, b.key)
         val hideAnimation = buildHideAnimation(bubbleView)
+        animatingBubble = AnimatingBubble(bubbleView, showAnimation, hideAnimation)
         scheduler.post(showAnimation)
         scheduler.postDelayed(FLYOUT_DELAY_MS, hideAnimation)
     }
@@ -113,7 +130,7 @@
     private fun buildShowAnimation(
         bubbleView: BubbleView,
         key: String,
-    ): () -> Unit = {
+    ) = Runnable {
         bubbleBarView.prepareForAnimatingBubbleWhileStashed(key)
         // calculate the initial translation x the bubble should have in order to align it with the
         // stash handle.
@@ -140,7 +157,7 @@
 
                     // map the path [0, BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y] to [0,1]
                     val fraction = ty / BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y
-                    target.alpha = 1 - fraction / 2
+                    target.alpha = 1 - fraction
                 }
                 ty >= totalTranslationY -> {
                     // this is the second leg of the animation. the handle should be completely
@@ -173,6 +190,16 @@
                 }
             }
         }
+        animator.addEndListener { _, _, _, _, _, _, _ ->
+            // the bubble is now fully settled in. make it touchable
+            bubbleBarView.updateAnimatingBubbleBounds(
+                bubbleView.left,
+                bubbleView.top,
+                bubbleView.width,
+                bubbleView.height
+            )
+            bubbleStashController.updateTaskbarTouchRegion()
+        }
         animator.start()
     }
 
@@ -189,7 +216,7 @@
      * 1. In the second part the bubble is fully hidden and the handle animates in.
      * 1. The third part is the overshoot. The handle is made fully visible.
      */
-    private fun buildHideAnimation(bubbleView: BubbleView): () -> Unit = {
+    private fun buildHideAnimation(bubbleView: BubbleView) = Runnable {
         // this is the total distance that both the stashed handle and the bubble will be traveling
         val totalTranslationY =
             BUBBLE_ANIMATION_BUBBLE_TRANSLATION_Y + BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y
@@ -230,6 +257,7 @@
             }
         }
         animator.addEndListener { _, _, _, _, _, _, _ ->
+            animatingBubble = null
             bubbleView.alpha = 0f
             bubbleView.translationY = 0f
             bubbleView.scaleY = 1f
@@ -237,9 +265,18 @@
                 bubbleBarView.alpha = 0f
             }
             bubbleBarView.onAnimatingBubbleCompleted()
+            bubbleStashController.updateTaskbarTouchRegion()
         }
         animator.start()
     }
+
+    /** Handles clicking on the animating bubble while the animation is still playing. */
+    fun onBubbleClickedWhileAnimating() {
+        val hideAnimation = animatingBubble?.hideAnimation ?: return
+        scheduler.cancel(hideAnimation)
+        bubbleBarView.onAnimatingBubbleCompleted()
+        animatingBubble = null
+    }
 }
 
 /** The X position in screen coordinates of the center of the bubble. */
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 79b09fd..225b127 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -220,6 +220,11 @@
                 }
             });
         }
+
+        @Override
+        public void setTriggerBack(boolean triggerBack) {
+            // TODO(b/261654570): track touch from the Launcher process.
+        }
     }
 
     private static class RemoteAnimationRunnerStub extends IRemoteAnimationRunner.Stub {
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index a842b51..832f4e1 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -94,6 +94,7 @@
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.launcher3.taskbar.TaskbarManager;
+import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.testing.shared.TestProtocol;
@@ -470,6 +471,18 @@
 
     private final ScreenOnTracker.ScreenOnListener mScreenOnListener = this::onScreenOnChanged;
 
+    private final TaskbarNavButtonCallbacks mNavCallbacks = new TaskbarNavButtonCallbacks() {
+        @Override
+        public void onNavigateHome() {
+            mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HOME);
+        }
+
+        @Override
+        public void onToggleOverview() {
+            mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
+        }
+    };
+
     private ActivityManagerWrapper mAM;
     private OverviewCommandHelper mOverviewCommandHelper;
     private OverviewComponentObserver mOverviewComponentObserver;
@@ -500,7 +513,7 @@
         mDeviceState = new RecentsAnimationDeviceState(this, true);
         mAllAppsActionManager = new AllAppsActionManager(
                 this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent);
-        mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager);
+        mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager, mNavCallbacks);
         mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
         mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
 
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 9df568e..2a27dea 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -136,21 +136,16 @@
             });
         }
 
-        if (!ALL_APPS_VISITED_COUNT.hasReachedMax(launcher)) {
+        if (!Utilities.isRunningInTestHarness()) {
             launcher.getStateManager().addStateListener(new StateListener<LauncherState>() {
                 @Override
                 public void onStateTransitionComplete(LauncherState finalState) {
                     if (finalState == ALL_APPS) {
                         ALL_APPS_VISITED_COUNT.increment(launcher);
-                        return;
                     }
-
-                    boolean hasReachedMaxCount = ALL_APPS_VISITED_COUNT.hasReachedMax(launcher);
-                    launcher.getAppsView().getFloatingHeaderView().findFixedRowByType(
-                            AppsDividerView.class).setShowAllAppsLabel(!hasReachedMaxCount);
-                    if (hasReachedMaxCount) {
-                        launcher.getStateManager().removeStateListener(this);
-                    }
+                    launcher.getAppsView().getFloatingHeaderView()
+                            .findFixedRowByType(AppsDividerView.class)
+                            .setShowAllAppsLabel(!ALL_APPS_VISITED_COUNT.hasReachedMax(launcher));
                 }
             });
         }
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
index 58be345..0f06d98 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -12,10 +12,10 @@
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.SCREEN_PIN_LONG_PRESS_THRESHOLD;
-import static com.android.quickstep.OverviewCommandHelper.TYPE_HOME;
-import static com.android.quickstep.OverviewCommandHelper.TYPE_TOGGLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
@@ -31,7 +31,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.logging.StatsLogManager;
-import com.android.quickstep.OverviewCommandHelper;
+import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.AssistUtils;
@@ -52,8 +52,6 @@
     @Mock
     TouchInteractionService mockService;
     @Mock
-    OverviewCommandHelper mockCommandHelper;
-    @Mock
     Handler mockHandler;
     @Mock
     AssistUtils mockAssistUtils;
@@ -68,13 +66,26 @@
     @Mock
     View mockView;
 
+    private int mHomePressCount;
+    private int mOverviewToggleCount;
+    private final TaskbarNavButtonCallbacks mCallbacks = new TaskbarNavButtonCallbacks() {
+        @Override
+        public void onNavigateHome() {
+            mHomePressCount++;
+        }
+
+        @Override
+        public void onToggleOverview() {
+            mOverviewToggleCount++;
+        }
+    };
+
     private TaskbarNavButtonController mNavButtonController;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         when(mockService.getDisplayId()).thenReturn(DISPLAY_ID);
-        when(mockService.getOverviewCommandHelper()).thenReturn(mockCommandHelper);
         when(mockService.getApplicationContext())
                 .thenReturn(InstrumentationRegistry.getInstrumentation().getTargetContext()
                         .getApplicationContext());
@@ -82,8 +93,12 @@
         when(mockTaskbarControllers.getTaskbarActivityContext())
                 .thenReturn(mockTaskbarActivityContext);
         doReturn(mockStatsLogManager).when(mockTaskbarActivityContext).getStatsLogManager();
-        mNavButtonController = new TaskbarNavButtonController(mockService,
-                mockSystemUiProxy, mockHandler, mockAssistUtils);
+        mNavButtonController = new TaskbarNavButtonController(
+                mockService,
+                mCallbacks,
+                mockSystemUiProxy,
+                mockHandler,
+                mockAssistUtils);
     }
 
     @Test
@@ -154,20 +169,20 @@
     @Test
     public void testPressHome() {
         mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
-        verify(mockCommandHelper, times(1)).addCommand(TYPE_HOME);
+        assertThat(mHomePressCount).isEqualTo(1);
     }
 
     @Test
     public void testPressRecents() {
         mNavButtonController.onButtonClick(BUTTON_RECENTS, mockView);
-        verify(mockCommandHelper, times(1)).addCommand(TYPE_TOGGLE);
+        assertThat(mOverviewToggleCount).isEqualTo(1);
     }
 
     @Test
-    public void testPressRecentsWithScreenPinned() {
+    public void testPressRecentsWithScreenPinned_noNavigationToOverview() {
         mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
         mNavButtonController.onButtonClick(BUTTON_RECENTS, mockView);
-        verify(mockCommandHelper, times(0)).addCommand(TYPE_TOGGLE);
+        assertThat(mOverviewToggleCount).isEqualTo(0);
     }
 
     @Test
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index d90e048..3d8484d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -130,18 +130,90 @@
         assertThat(handle.translationY).isEqualTo(0)
     }
 
-    private class TestBubbleBarViewAnimatorScheduler : BubbleBarViewAnimator.Scheduler {
+    @Test
+    fun animateBubbleInForStashed_tapAnimatingBubble() {
+        lateinit var overflowView: BubbleView
+        lateinit var bubbleView: BubbleView
+        lateinit var bubble: BubbleBarBubble
+        val bubbleBarView = BubbleBarView(context)
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            bubbleBarView.layoutParams = FrameLayout.LayoutParams(0, 0)
+            val inflater = LayoutInflater.from(context)
 
-        var delayedBlock: (() -> Unit)? = null
-            private set
+            val bitmap = ColorDrawable(Color.WHITE).toBitmap(width = 20, height = 20)
+            overflowView =
+                inflater.inflate(R.layout.bubblebar_item_view, bubbleBarView, false) as BubbleView
+            overflowView.setOverflow(BubbleBarOverflow(overflowView), bitmap)
+            bubbleBarView.addView(overflowView)
 
-        override fun post(block: () -> Unit) {
-            block.invoke()
+            val bubbleInfo = BubbleInfo("key", 0, null, null, 0, context.packageName, null, false)
+            bubbleView =
+                inflater.inflate(R.layout.bubblebar_item_view, bubbleBarView, false) as BubbleView
+            bubble =
+                BubbleBarBubble(bubbleInfo, bubbleView, bitmap, bitmap, Color.WHITE, Path(), "")
+            bubbleView.setBubble(bubble)
+            bubbleBarView.addView(bubbleView)
+        }
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+        val bubbleStashController = mock<BubbleStashController>()
+        whenever(bubbleStashController.isStashed).thenReturn(true)
+
+        val handle = View(context)
+        val handleAnimator = PhysicsAnimator.getInstance(handle)
+        whenever(bubbleStashController.stashedHandlePhysicsAnimator).thenReturn(handleAnimator)
+
+        val animator =
+            BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            animator.animateBubbleInForStashed(bubble)
         }
 
-        override fun postDelayed(delayMillis: Long, block: () -> Unit) {
+        // let the animation start and wait for it to complete
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
+
+        assertThat(handle.alpha).isEqualTo(0)
+        assertThat(handle.translationY).isEqualTo(-70)
+        assertThat(overflowView.visibility).isEqualTo(INVISIBLE)
+        assertThat(bubbleBarView.visibility).isEqualTo(VISIBLE)
+        assertThat(bubbleView.visibility).isEqualTo(VISIBLE)
+        assertThat(bubbleView.alpha).isEqualTo(1)
+        assertThat(bubbleView.translationY).isEqualTo(-20)
+        assertThat(bubbleView.scaleY).isEqualTo(1)
+
+        // verify the hide bubble animation is pending
+        assertThat(animatorScheduler.delayedBlock).isNotNull()
+
+        animator.onBubbleClickedWhileAnimating()
+
+        assertThat(animatorScheduler.delayedBlock).isNull()
+        assertThat(overflowView.visibility).isEqualTo(VISIBLE)
+        assertThat(overflowView.alpha).isEqualTo(1)
+        assertThat(bubbleView.alpha).isEqualTo(1)
+        assertThat(bubbleView.visibility).isEqualTo(VISIBLE)
+        assertThat(bubbleBarView.background).isNotNull()
+        assertThat(bubbleBarView.isAnimatingNewBubble).isFalse()
+    }
+
+    private class TestBubbleBarViewAnimatorScheduler : BubbleBarViewAnimator.Scheduler {
+
+        var delayedBlock: Runnable? = null
+            private set
+
+        override fun post(block: Runnable) {
+            block.run()
+        }
+
+        override fun postDelayed(delayMillis: Long, block: Runnable) {
             check(delayedBlock == null) { "there is already a pending block waiting to run" }
             delayedBlock = block
         }
+
+        override fun cancel(block: Runnable) {
+            check(delayedBlock == block) { "the pending block does not match the canceled block" }
+            delayedBlock = null
+        }
     }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
index edc0a6f..7708233 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
@@ -123,6 +123,21 @@
         psContainer.verifyInstalledAppIsPresent(INSTALLED_APP_NAME);
     }
 
+    @Test
+    @ScreenRecordRule.ScreenRecord // b/334946529
+    public void testPrivateSpaceAppLongPressUninstallMenu() throws IOException {
+        // Ensure that the App is not installed in main user otherwise, it may not be found in
+        // PS container.
+        TestUtil.uninstallDummyApp();
+        // Install the app in Private Profile
+        TestUtil.installDummyAppForUser(mProfileUserId);
+        waitForLauncherUIUpdate();
+        // Scroll to the bottom of All Apps
+        executeOnLauncher(launcher -> launcher.getAppsView().resetAndScrollToPrivateSpaceHeader());
+        // Get the "uninstall" menu item.
+        mLauncher.getAllApps().getAppIcon(INSTALLED_APP_NAME).openMenu().getMenuItem("Uninstall");
+    }
+
     private void waitForPrivateSpaceSetup() {
         waitForLauncherCondition("Private Profile not setup",
                 launcher -> launcher.getAppsView().hasPrivateProfile(),
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index f16c69b..31def04 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -494,7 +494,7 @@
 
     <!-- Private Space parameters -->
     <dimen name="ps_container_corner_radius">24dp</dimen>
-    <dimen name="ps_header_height">64dp</dimen>
+    <dimen name="ps_header_height">72dp</dimen>
     <dimen name="ps_header_relative_layout_height">48dp</dimen>
     <dimen name="ps_header_image_height">48dp</dimen>
     <dimen name="ps_header_text_height">24dp</dimen>
diff --git a/tests/src/com/android/launcher3/dragging/TaplDragTest.java b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
index d64d096..1c41ded 100644
--- a/tests/src/com/android/launcher3/dragging/TaplDragTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
@@ -21,8 +21,6 @@
 import static com.android.launcher3.util.TestConstants.AppNames.PHOTOS_APP_NAME;
 import static com.android.launcher3.util.TestConstants.AppNames.STORE_APP_NAME;
 import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME;
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -43,8 +41,6 @@
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
 import com.android.launcher3.util.TestUtil;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
-import com.android.launcher3.util.rule.TestStabilityRule;
 
 import org.junit.Test;
 
@@ -67,9 +63,6 @@
      */
     @Test
     @PortraitLandscape
-    @ScreenRecord
-    // Staging; will be promoted to presubmit if stable
-    @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
     @PlatinumTest(focusArea = "launcher")
     public void testDragToFolder() {
         // TODO: add the use case to drag an icon to an existing folder. Currently it either fails
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
index c2e73fc..6bbcf85 100644
--- a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
+++ b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
@@ -24,18 +24,29 @@
 import android.content.pm.PackageInstaller
 import android.content.pm.ShortcutInfo
 import android.os.UserHandle
+import android.platform.test.annotations.EnableFlags
 import android.util.LongSparseArray
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING
 import com.android.launcher3.LauncherAppState
 import com.android.launcher3.LauncherSettings.Favorites
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
+import com.android.launcher3.Utilities
 import com.android.launcher3.Utilities.EMPTY_PERSON_ARRAY
+import com.android.launcher3.backuprestore.LauncherRestoreEventLogger
 import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError.Companion.MISSING_INFO
+import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError.Companion.MISSING_WIDGET_PROVIDER
 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.LauncherAppWidgetInfo
+import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
+import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_UI_NOT_READY
 import com.android.launcher3.model.data.WorkspaceItemInfo
 import com.android.launcher3.pm.UserCache
 import com.android.launcher3.shortcuts.ShortcutKey
@@ -43,11 +54,14 @@
 import com.android.launcher3.util.PackageManagerHelper
 import com.android.launcher3.util.PackageUserKey
 import com.android.launcher3.util.UserIconInfo
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo
 import com.android.launcher3.widget.WidgetInflater
+import com.android.launcher3.widget.WidgetSections
 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.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.RETURNS_DEEP_STUBS
 import org.mockito.Mockito.mock
@@ -56,8 +70,10 @@
 import org.mockito.kotlin.any
 import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
 
 class WorkspaceItemProcessorTest {
 
@@ -73,29 +89,31 @@
     @Mock private lateinit var mockUserManagerState: UserManagerState
     @Mock private lateinit var mockWidgetInflater: WidgetInflater
 
-    private lateinit var intent: Intent
-    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 var intent: Intent = Intent()
+    private var mUserHandle: UserHandle = UserHandle(0)
+    private var mIconRequestInfos: MutableList<IconRequestInfo<WorkspaceItemInfo>> = mutableListOf()
+    private var mComponentName: ComponentName = ComponentName("package", "class")
+    private var mUnlockedUsersArray: LongSparseArray<Boolean> = LongSparseArray()
+    private var mKeyToPinnedShortcutsMap: MutableMap<ShortcutKey, ShortcutInfo> = mutableMapOf()
+    private var mInstallingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo> = hashMapOf()
+    private var mAllDeepShortcuts: MutableList<ShortcutInfo> = mutableListOf()
+    private var mWidgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?> =
+        mutableMapOf()
+    private var mPendingPackages: MutableSet<PackageUserKey> = mutableSetOf()
 
     private lateinit var itemProcessorUnderTest: WorkspaceItemProcessor
 
     @Before
     fun setup() {
-        userHandle = UserHandle(0)
+        mUserHandle = 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) }
+        mComponentName = ComponentName("package", "class")
+        mUnlockedUsersArray = LongSparseArray<Boolean>(1).apply { put(101, true) }
         intent =
             Intent().apply {
-                component = componentName
+                component = mComponentName
                 `package` = "pkg"
                 putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "")
             }
@@ -112,17 +130,17 @@
             }
         mockPmHelper =
             mock<PackageManagerHelper>().apply {
-                whenever(getAppLaunchIntent(componentName.packageName, userHandle))
+                whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
                     .thenReturn(intent)
             }
         mockLauncherApps =
             mock<LauncherApps>().apply {
-                whenever(isPackageEnabled("package", userHandle)).thenReturn(true)
-                whenever(isActivityEnabled(componentName, userHandle)).thenReturn(true)
+                whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
+                whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true)
             }
         mockCursor =
             mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply {
-                user = userHandle
+                user = mUserHandle
                 itemType = ITEM_TYPE_APPLICATION
                 id = 1
                 restoreFlag = 1
@@ -137,15 +155,18 @@
         mockUserCache =
             mock<UserCache>().apply {
                 val userIconInfo =
-                    mock<UserIconInfo>().apply() { whenever(isPrivate).thenReturn(false) }
+                    mock<UserIconInfo>().apply { whenever(isPrivate).thenReturn(false) }
                 whenever(getUserInfo(any())).thenReturn(userIconInfo)
             }
 
         mockUserManagerState = mock<UserManagerState>()
         mockWidgetInflater = mock<WidgetInflater>()
-        keyToPinnedShortcutsMap = mutableMapOf()
-        installingPkgs = hashMapOf()
-        allDeepShortcuts = mutableListOf()
+        mKeyToPinnedShortcutsMap = mutableMapOf()
+        mInstallingPkgs = hashMapOf()
+        mAllDeepShortcuts = mutableListOf()
+        mWidgetProvidersMap = mutableMapOf()
+        mIconRequestInfos = mutableListOf()
+        mPendingPackages = mutableSetOf()
     }
 
     /**
@@ -159,18 +180,18 @@
         userCache: UserCache = mockUserCache,
         userManagerState: UserManagerState = mockUserManagerState,
         launcherApps: LauncherApps = mockLauncherApps,
-        shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo> = keyToPinnedShortcutsMap,
+        shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo> = mKeyToPinnedShortcutsMap,
         app: LauncherAppState = mockAppState,
         bgDataModel: BgDataModel = mockBgDataModel,
-        widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?> = mutableMapOf(),
+        widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?> = mWidgetProvidersMap,
         widgetInflater: WidgetInflater = mockWidgetInflater,
         pmHelper: PackageManagerHelper = mockPmHelper,
-        iconRequestInfos: MutableList<IconRequestInfo<WorkspaceItemInfo>> = mutableListOf(),
+        iconRequestInfos: MutableList<IconRequestInfo<WorkspaceItemInfo>> = mIconRequestInfos,
         isSdCardReady: Boolean = false,
-        pendingPackages: MutableSet<PackageUserKey> = mutableSetOf(),
-        unlockedUsers: LongSparseArray<Boolean> = unlockedUsersArray,
-        installingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo> = hashMapOf(),
-        allDeepShortcuts: MutableList<ShortcutInfo> = mutableListOf()
+        pendingPackages: MutableSet<PackageUserKey> = mPendingPackages,
+        unlockedUsers: LongSparseArray<Boolean> = mUnlockedUsersArray,
+        installingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo> = mInstallingPkgs,
+        allDeepShortcuts: MutableList<ShortcutInfo> = mAllDeepShortcuts
     ) =
         WorkspaceItemProcessor(
             c = cursor,
@@ -241,8 +262,8 @@
     fun `When app has empty String target package then mark deleted`() {
 
         // Given
-        componentName = ComponentName("", "")
-        intent.component = componentName
+        mComponentName = ComponentName("", "")
+        intent.component = mComponentName
         intent.`package` = ""
 
         // When
@@ -267,7 +288,7 @@
             .isEqualTo(0)
         // currently gets marked restored twice, although markRestore() has check for restoreFlag
         verify(mockCursor, times(2)).markRestored()
-        assertThat(iconRequestInfos).containsExactly(mockIconRequestInfo)
+        assertThat(mIconRequestInfos).containsExactly(mockIconRequestInfo)
         verify(mockCursor).checkAndAddItem(mockWorkspaceInfo, mockBgDataModel, null)
     }
 
@@ -277,12 +298,12 @@
         // Given
         mockLauncherApps =
             mock<LauncherApps>().apply {
-                whenever(isPackageEnabled("package", userHandle)).thenReturn(true)
-                whenever(isActivityEnabled(componentName, userHandle)).thenReturn(false)
+                whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
+                whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(false)
             }
         mockPmHelper =
             mock<PackageManagerHelper>().apply {
-                whenever(getAppLaunchIntent(componentName.packageName, userHandle))
+                whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
                     .thenReturn(intent)
             }
 
@@ -295,7 +316,7 @@
             .that(mockCursor.restoreFlag)
             .isEqualTo(0)
         verify(mockCursor.updater().put(Favorites.INTENT, intent.toUri(0))).commit()
-        assertThat(iconRequestInfos).containsExactly(mockIconRequestInfo)
+        assertThat(mIconRequestInfos).containsExactly(mockIconRequestInfo)
         verify(mockCursor).checkAndAddItem(mockWorkspaceInfo, mockBgDataModel, null)
     }
 
@@ -305,12 +326,13 @@
         // Given
         mockLauncherApps =
             mock<LauncherApps>().apply {
-                whenever(isPackageEnabled("package", userHandle)).thenReturn(true)
-                whenever(isActivityEnabled(componentName, userHandle)).thenReturn(false)
+                whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
+                whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(false)
             }
         mockPmHelper =
             mock<PackageManagerHelper>().apply {
-                whenever(getAppLaunchIntent(componentName.packageName, userHandle)).thenReturn(null)
+                whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
+                    .thenReturn(null)
             }
 
         // When
@@ -349,20 +371,20 @@
                 whenever(persons).thenReturn(EMPTY_PERSON_ARRAY)
             }
         val shortcutKey = ShortcutKey.fromIntent(intent, mockCursor.user)
-        keyToPinnedShortcutsMap[shortcutKey] = expectedShortcutInfo
-        iconRequestInfos = mutableListOf()
+        mKeyToPinnedShortcutsMap[shortcutKey] = expectedShortcutInfo
+        mIconRequestInfos = mutableListOf()
 
         // When
         itemProcessorUnderTest =
-            createWorkspaceItemProcessorUnderTest(allDeepShortcuts = allDeepShortcuts)
+            createWorkspaceItemProcessorUnderTest(allDeepShortcuts = mAllDeepShortcuts)
         itemProcessorUnderTest.processItem()
 
         // Then
         assertWithMessage("item restoreFlag should be set to 0")
             .that(mockCursor.restoreFlag)
             .isEqualTo(0)
-        assertThat(iconRequestInfos).isEmpty()
-        assertThat(allDeepShortcuts).containsExactly(expectedShortcutInfo)
+        assertThat(mIconRequestInfos).isEmpty()
+        assertThat(mAllDeepShortcuts).containsExactly(expectedShortcutInfo)
         verify(mockCursor).markRestored()
         verify(mockCursor).checkAndAddItem(any(), any(), anyOrNull())
     }
@@ -372,8 +394,8 @@
 
         // Given
         mockCursor.itemType = ITEM_TYPE_DEEP_SHORTCUT
-        iconRequestInfos = mutableListOf()
-        keyToPinnedShortcutsMap = hashMapOf()
+        mIconRequestInfos = mutableListOf()
+        mKeyToPinnedShortcutsMap = hashMapOf()
 
         // When
         itemProcessorUnderTest = createWorkspaceItemProcessorUnderTest()
@@ -383,7 +405,7 @@
         assertWithMessage("item restoreFlag should be set to 0")
             .that(mockCursor.restoreFlag)
             .isEqualTo(0)
-        assertThat(iconRequestInfos).isEmpty()
+        assertThat(mIconRequestInfos).isEmpty()
         verify(mockCursor, times(0)).checkAndAddItem(any(), any(), anyOrNull())
         verify(mockCursor)
             .markDeleted(
@@ -408,25 +430,25 @@
                 whenever(disabledReason).thenReturn(0)
                 whenever(persons).thenReturn(EMPTY_PERSON_ARRAY)
             }
-        iconRequestInfos = mutableListOf()
+        mIconRequestInfos = mutableListOf()
         // Make sure shortcuts map has expected key from expected package
-        intent.`package` = componentName.packageName
+        intent.`package` = mComponentName.packageName
         val shortcutKey = ShortcutKey.fromIntent(intent, mockCursor.user)
-        keyToPinnedShortcutsMap[shortcutKey] = expectedShortcutInfo
+        mKeyToPinnedShortcutsMap[shortcutKey] = expectedShortcutInfo
         // set intent package back to null to test scenario
         intent.`package` = null
 
         // When
         itemProcessorUnderTest =
-            createWorkspaceItemProcessorUnderTest(allDeepShortcuts = allDeepShortcuts)
+            createWorkspaceItemProcessorUnderTest(allDeepShortcuts = mAllDeepShortcuts)
         itemProcessorUnderTest.processItem()
 
         // Then
         assertWithMessage("item restoreFlag should be set to 0")
             .that(mockCursor.restoreFlag)
             .isEqualTo(0)
-        assertThat(iconRequestInfos).isEmpty()
-        assertThat(allDeepShortcuts).containsExactly(expectedShortcutInfo)
+        assertThat(mIconRequestInfos).isEmpty()
+        assertThat(mAllDeepShortcuts).containsExactly(expectedShortcutInfo)
         verify(mockCursor).markRestored()
         verify(mockCursor).checkAndAddItem(any(), any(), anyOrNull())
     }
@@ -478,4 +500,346 @@
         assertThat(actualFolderInfo.options).isEqualTo(expectedFolderInfo.options)
         verify(mockCursor).checkAndAddItem(actualFolderInfo, mockBgDataModel, null)
     }
+
+    @Test
+    fun `When valid TYPE_REAL App Widget then add item`() {
+
+        // Given
+        val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
+        val expectedComponentName =
+            ComponentName.unflattenFromString(expectedProvider)!!.flattenToString()
+        val expectedRestoreStatus = FLAG_UI_NOT_READY
+        val expectedAppWidgetId = 0
+        mockCursor.apply {
+            itemType = ITEM_TYPE_APPWIDGET
+            user = mUserHandle
+            restoreFlag = FLAG_UI_NOT_READY
+            container = CONTAINER_DESKTOP
+            whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
+            whenever(appWidgetProvider).thenReturn(expectedProvider)
+            whenever(appWidgetId).thenReturn(expectedAppWidgetId)
+            whenever(spanX).thenReturn(2)
+            whenever(spanY).thenReturn(1)
+            whenever(options).thenReturn(0)
+            whenever(appWidgetSource).thenReturn(20)
+            whenever(applyCommonProperties(any())).thenCallRealMethod()
+            whenever(
+                    updater()
+                        .put(Favorites.APPWIDGET_PROVIDER, expectedComponentName)
+                        .put(Favorites.APPWIDGET_ID, expectedAppWidgetId)
+                        .put(Favorites.RESTORED, expectedRestoreStatus)
+                        .commit()
+                )
+                .thenReturn(1)
+        }
+        val expectedWidgetInfo =
+            LauncherAppWidgetInfo().apply {
+                appWidgetId = expectedAppWidgetId
+                providerName = ComponentName.unflattenFromString(expectedProvider)
+                restoreStatus = expectedRestoreStatus
+            }
+        val expectedWidgetProviderInfo =
+            mock<LauncherAppWidgetProviderInfo>().apply {
+                provider = ComponentName.unflattenFromString(expectedProvider)
+                whenever(user).thenReturn(mUserHandle)
+            }
+        val inflationResult =
+            WidgetInflater.InflationResult(
+                type = WidgetInflater.TYPE_REAL,
+                widgetInfo = expectedWidgetProviderInfo
+            )
+        mockWidgetInflater =
+            mock<WidgetInflater>().apply {
+                whenever(inflateAppWidget(any())).thenReturn(inflationResult)
+            }
+        val packageUserKey = PackageUserKey("com.google.android.testApp", mUserHandle)
+        mInstallingPkgs[packageUserKey] = PackageInstaller.SessionInfo()
+
+        // When
+        itemProcessorUnderTest =
+            createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        val widgetInfoCaptor = ArgumentCaptor.forClass(LauncherAppWidgetInfo::class.java)
+        verify(mockCursor).checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel))
+        val actualWidgetInfo = widgetInfoCaptor.value
+        with(actualWidgetInfo) {
+            assertThat(providerName).isEqualTo(expectedWidgetInfo.providerName)
+            assertThat(restoreStatus).isEqualTo(expectedWidgetInfo.restoreStatus)
+            assertThat(targetComponent).isEqualTo(expectedWidgetInfo.targetComponent)
+            assertThat(appWidgetId).isEqualTo(expectedWidgetInfo.appWidgetId)
+        }
+        val expectedComponentKey =
+            ComponentKey(expectedWidgetProviderInfo.provider, expectedWidgetProviderInfo.user)
+        assertThat(mWidgetProvidersMap[expectedComponentKey]).isEqualTo(expectedWidgetProviderInfo)
+    }
+
+    @Test
+    fun `When valid Pending Widget then checkAndAddItem`() {
+
+        // Given
+        mockCursor =
+            mock<LoaderCursor>().apply {
+                itemType = ITEM_TYPE_APPWIDGET
+                id = 1
+                user = UserHandle(1)
+                restoreFlag = FLAG_UI_NOT_READY
+                container = CONTAINER_DESKTOP
+                whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
+                whenever(appWidgetProvider)
+                    .thenReturn("com.google.android.testApp/com.android.testApp.testAppProvider")
+                whenever(appWidgetId).thenReturn(0)
+                whenever(spanX).thenReturn(2)
+                whenever(spanY).thenReturn(1)
+                whenever(options).thenReturn(0)
+                whenever(appWidgetSource).thenReturn(20)
+                whenever(applyCommonProperties(any())).thenCallRealMethod()
+            }
+        val mockProviderInfo =
+            mock<LauncherAppWidgetProviderInfo>().apply {
+                provider = mock()
+                whenever(user).thenReturn(UserHandle(1))
+            }
+        val inflationResult =
+            WidgetInflater.InflationResult(
+                type = WidgetInflater.TYPE_PENDING,
+                widgetInfo = mockProviderInfo
+            )
+        mockWidgetInflater =
+            mock<WidgetInflater>().apply {
+                whenever(inflateAppWidget(any())).thenReturn(inflationResult)
+            }
+        itemProcessorUnderTest =
+            createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
+
+        // When
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        verify(mockCursor).checkAndAddItem(any(), any())
+    }
+
+    @Test
+    fun `When Unrestored Pending App Widget then mark deleted`() {
+
+        // Given
+        val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
+        mockCursor =
+            mock<LoaderCursor>().apply {
+                itemType = ITEM_TYPE_APPWIDGET
+                id = 1
+                user = UserHandle(1)
+                restoreFlag = FLAG_UI_NOT_READY
+                container = CONTAINER_DESKTOP
+                whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
+                whenever(appWidgetProvider).thenReturn(expectedProvider)
+                whenever(appWidgetId).thenReturn(0)
+                whenever(spanX).thenReturn(2)
+                whenever(spanY).thenReturn(1)
+                whenever(options).thenReturn(0)
+                whenever(appWidgetSource).thenReturn(20)
+                whenever(applyCommonProperties(any())).thenCallRealMethod()
+            }
+        mInstallingPkgs = hashMapOf()
+        val inflationResult =
+            WidgetInflater.InflationResult(type = WidgetInflater.TYPE_PENDING, widgetInfo = null)
+        mockWidgetInflater =
+            mock<WidgetInflater>().apply {
+                whenever(inflateAppWidget(any())).thenReturn(inflationResult)
+            }
+        val expectedComponentName = ComponentName.unflattenFromString(expectedProvider)
+
+        // When
+        itemProcessorUnderTest =
+            createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        verify(mockCursor)
+            .markDeleted(
+                "processWidget: Unrestored Pending widget removed: id=1, appWidgetId=0, component=$expectedComponentName, restoreFlag:=4",
+                LauncherRestoreEventLogger.RestoreError.APP_NOT_INSTALLED
+            )
+    }
+
+    @Test
+    fun `When Pending App Widget has not started restore then update db and add item`() {
+
+        val mockitoSession =
+            ExtendedMockito.mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .mockStatic(WidgetSections::class.java)
+                .startMocking()
+        try {
+            // Given
+            val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
+            val expectedComponentName =
+                ComponentName.unflattenFromString(expectedProvider)!!.flattenToString()
+            val expectedRestoreStatus = FLAG_UI_NOT_READY or FLAG_RESTORE_STARTED
+            val expectedAppWidgetId = 0
+            mockCursor.apply {
+                itemType = ITEM_TYPE_APPWIDGET
+                user = mUserHandle
+                restoreFlag = FLAG_UI_NOT_READY
+                container = CONTAINER_DESKTOP
+                whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
+                whenever(appWidgetProvider).thenReturn(expectedProvider)
+                whenever(appWidgetId).thenReturn(expectedAppWidgetId)
+                whenever(spanX).thenReturn(2)
+                whenever(spanY).thenReturn(1)
+                whenever(options).thenReturn(0)
+                whenever(appWidgetSource).thenReturn(20)
+                whenever(applyCommonProperties(any())).thenCallRealMethod()
+                whenever(
+                        updater()
+                            .put(Favorites.APPWIDGET_PROVIDER, expectedComponentName)
+                            .put(Favorites.APPWIDGET_ID, expectedAppWidgetId)
+                            .put(Favorites.RESTORED, expectedRestoreStatus)
+                            .commit()
+                    )
+                    .thenReturn(1)
+            }
+            val inflationResult =
+                WidgetInflater.InflationResult(
+                    type = WidgetInflater.TYPE_PENDING,
+                    widgetInfo = null
+                )
+            mockWidgetInflater =
+                mock<WidgetInflater>().apply {
+                    whenever(inflateAppWidget(any())).thenReturn(inflationResult)
+                }
+            val packageUserKey = PackageUserKey("com.google.android.testApp", mUserHandle)
+            mInstallingPkgs[packageUserKey] = PackageInstaller.SessionInfo()
+
+            // When
+            itemProcessorUnderTest =
+                createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
+            itemProcessorUnderTest.processItem()
+
+            // Then
+            val expectedWidgetInfo =
+                LauncherAppWidgetInfo().apply {
+                    appWidgetId = expectedAppWidgetId
+                    providerName = ComponentName.unflattenFromString(expectedProvider)
+                    restoreStatus = expectedRestoreStatus
+                }
+            verify(
+                    mockCursor
+                        .updater()
+                        .put(Favorites.APPWIDGET_PROVIDER, expectedProvider)
+                        .put(Favorites.APPWIDGET_ID, expectedAppWidgetId)
+                        .put(Favorites.RESTORED, expectedRestoreStatus)
+                )
+                .commit()
+            val widgetInfoCaptor = ArgumentCaptor.forClass(LauncherAppWidgetInfo::class.java)
+            verify(mockCursor).checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel))
+            val actualWidgetInfo = widgetInfoCaptor.value
+            with(actualWidgetInfo) {
+                assertThat(providerName).isEqualTo(expectedWidgetInfo.providerName)
+                assertThat(restoreStatus).isEqualTo(expectedWidgetInfo.restoreStatus)
+                assertThat(targetComponent).isEqualTo(expectedWidgetInfo.targetComponent)
+                assertThat(appWidgetId).isEqualTo(expectedWidgetInfo.appWidgetId)
+            }
+        } finally {
+            mockitoSession.finishMocking()
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
+    fun `When Archived Pending App Widget then checkAndAddItem`() {
+        val mockitoSession =
+            ExtendedMockito.mockitoSession().mockStatic(Utilities::class.java).startMocking()
+        try {
+            // Given
+            val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
+            val expectedComponentName = ComponentName.unflattenFromString(expectedProvider)
+            val expectedPackage = expectedComponentName!!.packageName
+            mockPmHelper =
+                mock<PackageManagerHelper>().apply {
+                    whenever(isAppArchived(expectedPackage)).thenReturn(true)
+                }
+            mockCursor =
+                mock<LoaderCursor>().apply {
+                    itemType = ITEM_TYPE_APPWIDGET
+                    id = 1
+                    user = UserHandle(1)
+                    restoreFlag = FLAG_UI_NOT_READY
+                    container = CONTAINER_DESKTOP
+                    whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
+                    whenever(appWidgetProvider).thenReturn(expectedProvider)
+                    whenever(appWidgetId).thenReturn(0)
+                    whenever(spanX).thenReturn(2)
+                    whenever(spanY).thenReturn(1)
+                    whenever(options).thenReturn(0)
+                    whenever(appWidgetSource).thenReturn(20)
+                    whenever(applyCommonProperties(any())).thenCallRealMethod()
+                }
+            mInstallingPkgs = hashMapOf()
+            val inflationResult =
+                WidgetInflater.InflationResult(
+                    type = WidgetInflater.TYPE_PENDING,
+                    widgetInfo = null
+                )
+            mockWidgetInflater =
+                mock<WidgetInflater>().apply {
+                    whenever(inflateAppWidget(any())).thenReturn(inflationResult)
+                }
+            itemProcessorUnderTest =
+                createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
+
+            // When
+            itemProcessorUnderTest.processItem()
+
+            // Then
+            verify(mockCursor).checkAndAddItem(any(), any())
+        } finally {
+            mockitoSession.finishMocking()
+        }
+    }
+
+    @Test
+    fun `When widget inflation result is TYPE_DELETE then mark deleted`() {
+        // Given
+        val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
+        val expectedComponentName = ComponentName.unflattenFromString(expectedProvider)
+        val expectedPackage = expectedComponentName!!.packageName
+        mockPmHelper =
+            mock<PackageManagerHelper>().apply {
+                whenever(isAppArchived(expectedPackage)).thenReturn(true)
+            }
+        mockCursor =
+            mock<LoaderCursor>().apply {
+                itemType = ITEM_TYPE_APPWIDGET
+                id = 1
+                user = UserHandle(1)
+                container = CONTAINER_DESKTOP
+                whenever(spanX).thenReturn(2)
+                whenever(spanY).thenReturn(1)
+                whenever(appWidgetProvider).thenReturn(expectedProvider)
+                whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
+                whenever(applyCommonProperties(any())).thenCallRealMethod()
+            }
+        mInstallingPkgs = hashMapOf()
+        val inflationResult =
+            WidgetInflater.InflationResult(
+                type = WidgetInflater.TYPE_DELETE,
+                widgetInfo = null,
+                reason = "test_delete_reason",
+                restoreErrorType = MISSING_WIDGET_PROVIDER
+            )
+        mockWidgetInflater =
+            mock<WidgetInflater>().apply {
+                whenever(inflateAppWidget(any())).thenReturn(inflationResult)
+            }
+        itemProcessorUnderTest =
+            createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
+
+        // When
+        itemProcessorUnderTest.processItem()
+
+        // Then
+        verify(mockCursor).markDeleted(inflationResult.reason, inflationResult.restoreErrorType)
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
index 7845dec..9dbd866 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
@@ -86,7 +86,6 @@
      * A custom shortcut is a 1x1 widget that launches a specific intent when user tap on it.
      * Custom shortcuts are replaced by deep shortcuts after api 25.
      */
-    @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
     @Test
     @PortraitLandscape
     public void testDragCustomShortcut() throws Throwable {