Merge "Add unit test for CellLayout making sure the reorder are valid and fix Multipage CellLyout" into main
diff --git a/quickstep/res/layout/icon_app_chip_view.xml b/quickstep/res/layout/icon_app_chip_view.xml
index fa9fd2b..b7acb70 100644
--- a/quickstep/res/layout/icon_app_chip_view.xml
+++ b/quickstep/res/layout/icon_app_chip_view.xml
@@ -60,9 +60,8 @@
         android:maxLines="1"
         android:ellipsize="end"
         android:textAlignment="viewStart"
-        android:textColor="?androidprv:attr/materialColorOnSurface"
-        android:textSize="@dimen/task_thumbnail_icon_menu_text_size"
-        android:importantForAccessibility="no" />
+        android:importantForAccessibility="no"
+        style="@style/IconAppChipMenuTextStyle" />
 
     <TextView
         android:id="@+id/icon_text_expanded"
@@ -72,9 +71,8 @@
         android:maxLines="1"
         android:ellipsize="end"
         android:textAlignment="viewStart"
-        android:textColor="?androidprv:attr/materialColorOnSurface"
-        android:textSize="@dimen/task_thumbnail_icon_menu_text_size"
-        android:importantForAccessibility="no" />
+        android:importantForAccessibility="no"
+        style="@style/IconAppChipMenuTextStyle" />
 
     <ImageView
         android:id="@+id/icon_arrow"
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 295074a..079f01b 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -55,7 +55,7 @@
     <!--  The max width of the icon menu text  -->
     <dimen name="task_thumbnail_icon_menu_text_max_width">118dp</dimen>
     <!--  The size of the icon menu text  -->
-    <dimen name="task_thumbnail_icon_menu_text_size">16sp</dimen>
+    <dimen name="task_thumbnail_icon_menu_text_size">14sp</dimen>
     <!--  The max width of the thumbnail icon menu background  -->
     <dimen name="task_thumbnail_icon_menu_background_max_width">164dp</dimen>
     <!--  The height of the thumbnail icon menu  -->
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index fc03704..ae62c26 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -303,4 +303,12 @@
         <item name="arrowTipBackground">?androidprv:attr/materialColorSurfaceContainer</item>
         <item name="arrowTipTextColor">?androidprv:attr/materialColorOnSurface</item>
     </style>
+
+    <style name="IconAppChipMenuTextStyle">
+        <item name="android:fontFamily">google-sans-text-medium</item>
+        <item name="android:textSize">@dimen/task_thumbnail_icon_menu_text_size</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+        <item name="android:letterSpacing">0.025</item>
+        <item name="android:lineHeight">20sp</item>
+    </style>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 165ed80..5b0c8c3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -480,7 +480,11 @@
 
     @Override
     public void onDestroy() {
-        mAppTransitionManager.onActivityDestroyed();
+        if (mAppTransitionManager != null) {
+            mAppTransitionManager.onActivityDestroyed();
+        }
+        mAppTransitionManager = null;
+
         if (mUnfoldTransitionProgressProvider != null) {
             SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null);
             mUnfoldTransitionProgressProvider.destroy();
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 95ce406..35fa539 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -238,6 +238,12 @@
         if (toState == MODAL_TASK) {
             setOverviewSelectEnabled(true);
         }
+
+        // Set border after select mode changes to avoid showing border during state transition
+        if (!toState.overviewUi() || toState == MODAL_TASK) {
+            setTaskBorderEnabled(false);
+        }
+
         setFreezeViewVisibility(true);
     }
 
@@ -253,6 +259,11 @@
         if (finalState != MODAL_TASK) {
             setOverviewSelectEnabled(false);
         }
+
+        if (finalState.overviewUi() && finalState != MODAL_TASK) {
+            setTaskBorderEnabled(true);
+        }
+
         if (finalState != OVERVIEW_SPLIT_SELECT) {
             if (FeatureFlags.enableSplitContextually()) {
                 mSplitSelectStateController.resetState();
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index b542909..5b5d0de 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -145,6 +145,12 @@
         if (toState == OVERVIEW_MODAL_TASK) {
             setOverviewSelectEnabled(true);
         }
+
+        // Set border after select mode changes to avoid showing border during state transition
+        if (!toState.overviewUi || toState == OVERVIEW_MODAL_TASK) {
+            setTaskBorderEnabled(false);
+        }
+
         setFreezeViewVisibility(true);
         if (mActivity.getDesktopVisibilityController() != null) {
             mActivity.getDesktopVisibilityController().onLauncherStateChanged(toState);
@@ -165,6 +171,10 @@
             setOverviewSelectEnabled(false);
         }
 
+        if (finalState.overviewUi && finalState != OVERVIEW_MODAL_TASK) {
+            setTaskBorderEnabled(true);
+        }
+
         if (isOverlayEnabled) {
             runActionOnRemoteHandles(remoteTargetHandle ->
                     remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f55dce9..22a5064 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1410,6 +1410,17 @@
     }
 
     /**
+     * Enable or disable showing border on hover and focus change on task views
+     */
+    public void setTaskBorderEnabled(boolean enabled) {
+        int taskCount = getTaskViewCount();
+        for (int i = 0; i < taskCount; i++) {
+            TaskView taskView = requireTaskViewAt(i);
+            taskView.setBorderEnabled(enabled);
+        }
+    }
+
+    /**
      * Whether the Clear All button is hidden or fully visible. Used to determine if center
      * displayed page is a task or the Clear All button.
      *
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 49eda8f..af4f402 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -408,6 +408,7 @@
             new TaskIdAttributeContainer[2];
 
     private boolean mShowScreenshot;
+    private boolean mBorderEnabled;
 
     // The current background requests to load the task thumbnail and icon
     @Nullable
@@ -439,8 +440,15 @@
         this(context, attrs, defStyleAttr, 0);
     }
 
-    public TaskView(
-            Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    public TaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        this(context, attrs, defStyleAttr, defStyleRes, null, null);
+    }
+
+    @VisibleForTesting
+    public TaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes, BorderAnimator focusBorderAnimator,
+            BorderAnimator hoverBorderAnimator) {
         super(context, attrs, defStyleAttr, defStyleRes);
         mActivity = StatefulActivity.fromContext(context);
         setOnClickListener(this::onClick);
@@ -457,28 +465,35 @@
         TypedArray styledAttrs = context.obtainStyledAttributes(
                 attrs, R.styleable.TaskView, defStyleAttr, defStyleRes);
 
-        mFocusBorderAnimator = keyboardFocusHighlightEnabled
-                ? BorderAnimator.createSimpleBorderAnimator(
-                        /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
-                        /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
-                                R.dimen.keyboard_quick_switch_border_width),
-                        /* boundsBuilder= */ this::updateBorderBounds,
-                        /* targetView= */ this,
-                        /* borderColor= */ styledAttrs.getColor(
-                                R.styleable.TaskView_focusBorderColor, DEFAULT_BORDER_COLOR))
-                : null;
+        if (focusBorderAnimator != null) {
+            mFocusBorderAnimator = focusBorderAnimator;
+        } else {
+            mFocusBorderAnimator = keyboardFocusHighlightEnabled
+                    ? BorderAnimator.createSimpleBorderAnimator(
+                    /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
+                    /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
+                            R.dimen.keyboard_quick_switch_border_width),
+                    /* boundsBuilder= */ this::updateBorderBounds,
+                    /* targetView= */ this,
+                    /* borderColor= */ styledAttrs.getColor(
+                            R.styleable.TaskView_focusBorderColor, DEFAULT_BORDER_COLOR))
+                    : null;
+        }
 
-        mHoverBorderAnimator = cursorHoverStatesEnabled
-                ? BorderAnimator.createSimpleBorderAnimator(
-                        /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
-                        /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
-                                R.dimen.task_hover_border_width),
-                        /* boundsBuilder= */ this::updateBorderBounds,
-                        /* targetView= */ this,
-                        /* borderColor= */ styledAttrs.getColor(
-                                R.styleable.TaskView_hoverBorderColor, DEFAULT_BORDER_COLOR))
-                : null;
-
+        if (hoverBorderAnimator != null) {
+            mHoverBorderAnimator = hoverBorderAnimator;
+        } else {
+            mHoverBorderAnimator = cursorHoverStatesEnabled
+                    ? BorderAnimator.createSimpleBorderAnimator(
+                    /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
+                    /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
+                            R.dimen.task_hover_border_width),
+                    /* boundsBuilder= */ this::updateBorderBounds,
+                    /* targetView= */ this,
+                    /* borderColor= */ styledAttrs.getColor(
+                            R.styleable.TaskView_hoverBorderColor, DEFAULT_BORDER_COLOR))
+                    : null;
+        }
         styledAttrs.recycle();
     }
 
@@ -538,24 +553,25 @@
     }
 
     @Override
-    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-        if (mFocusBorderAnimator != null) {
+        if (mFocusBorderAnimator != null && mBorderEnabled) {
             mFocusBorderAnimator.setBorderVisibility(gainFocus, /* animated= */ true);
         }
     }
 
     @Override
     public boolean onHoverEvent(MotionEvent event) {
-        if (mHoverBorderAnimator != null) {
+        if (mHoverBorderAnimator != null && mBorderEnabled) {
             switch (event.getAction()) {
                 case MotionEvent.ACTION_HOVER_ENTER:
-                    mHoverBorderAnimator.setBorderVisibility(
-                            /* visible= */ true, /* animated= */ true);
+                    mHoverBorderAnimator.setBorderVisibility(/* visible= */ true, /* animated= */
+                            true);
                     break;
                 case MotionEvent.ACTION_HOVER_EXIT:
-                    mHoverBorderAnimator.setBorderVisibility(
-                            /* visible= */ false, /* animated= */ true);
+                    mHoverBorderAnimator.setBorderVisibility(/* visible= */ false, /* animated= */
+                            true);
                     break;
                 default:
                     break;
@@ -564,6 +580,24 @@
         return super.onHoverEvent(event);
     }
 
+    /**
+     * Enable or disable showing border on hover and focus change
+     */
+    public void setBorderEnabled(boolean enabled) {
+        mBorderEnabled = enabled;
+        // Set the animation correctly in case it misses the hover/focus event during state
+        // transition
+        if (mHoverBorderAnimator != null) {
+            mHoverBorderAnimator.setBorderVisibility(/* visible= */
+                    enabled && isHovered(), /* animated= */ true);
+        }
+
+        if (mFocusBorderAnimator != null) {
+            mFocusBorderAnimator.setBorderVisibility(/* visible= */
+                    enabled && isFocused(), /* animated= */true);
+        }
+    }
+
     @Override
     public boolean onInterceptHoverEvent(MotionEvent event) {
         if (enableCursorHoverStates()) {
@@ -1327,6 +1361,7 @@
         mSnapshotView.setThumbnail(mTask, null);
         setOverlayEnabled(false);
         onTaskListVisibilityChanged(false);
+        mBorderEnabled = false;
     }
 
     public float getTaskCornerRadius() {
diff --git a/quickstep/tests/src/com/android/quickstep/TaskViewTest.java b/quickstep/tests/src/com/android/quickstep/TaskViewTest.java
new file mode 100644
index 0000000..d744194
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaskViewTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.quickstep.util.BorderAnimator;
+import com.android.quickstep.views.TaskView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+public class TaskViewTest {
+
+    @Mock
+    private StatefulActivity mContext;
+    @Mock
+    private Resources mResource;
+    @Mock
+    private BorderAnimator mHoverAnimator;
+    @Mock
+    private BorderAnimator mFocusAnimator;
+    private TaskView mTaskView;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mResource.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
+        when(mResource.getConfiguration()).thenReturn(new Configuration());
+
+        when(mContext.getResources()).thenReturn(mResource);
+        when(mContext.getTheme()).thenReturn(mock(Resources.Theme.class));
+        when(mContext.getApplicationInfo()).thenReturn(mock(ApplicationInfo.class));
+        when(mContext.obtainStyledAttributes(any(), any(), anyInt(), anyInt())).thenReturn(
+                mock(TypedArray.class));
+
+        mTaskView = new TaskView(mContext, null, 0, 0, mFocusAnimator, mHoverAnimator);
+    }
+
+    @Test
+    public void notShowBorderOnBorderDisabled() {
+        mTaskView.setBorderEnabled(/* enabled= */ false);
+        MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0);
+        mTaskView.onHoverEvent(MotionEvent.obtain(event));
+        verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */ true, /* animated= */
+                true);
+
+        mTaskView.onFocusChanged(false, 0, new Rect());
+        verify(mFocusAnimator, never()).setBorderVisibility(/* visible= */ true, /* animated= */
+                true);
+    }
+
+    @Test
+    public void showBorderOnBorderEnabled() {
+        mTaskView.setBorderEnabled(/* enabled= */ true);
+        MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0);
+        mTaskView.onHoverEvent(MotionEvent.obtain(event));
+        verify(mHoverAnimator, times(1)).setBorderVisibility(/* visible= */ true, /* animated= */
+                true);
+        mTaskView.onFocusChanged(true, 0, new Rect());
+        verify(mFocusAnimator, times(1)).setBorderVisibility(/* visible= */ true, /* animated= */
+                true);
+    }
+
+    @Test
+    public void hideBorderOnBorderDisabled() {
+        mTaskView.setBorderEnabled(/* enabled= */ false);
+        verify(mHoverAnimator, times(1)).setBorderVisibility(/* visible= */ false, /* animated= */
+                true);
+        verify(mFocusAnimator, times(1)).setBorderVisibility(/* visible= */ false, /* animated= */
+                true);
+    }
+
+    @Test
+    public void notShowBorderByDefault() {
+        MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0);
+        mTaskView.onHoverEvent(MotionEvent.obtain(event));
+        verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */ false, /* animated= */
+                true);
+        mTaskView.onFocusChanged(true, 0, new Rect());
+        verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */ false, /* animated= */
+                true);
+    }
+}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0099806..9f7575d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -528,6 +528,7 @@
         mAppWidgetManager = new WidgetManagerHelper(this);
         mAppWidgetHolder = createAppWidgetHolder();
         mAppWidgetHolder.startListening();
+        mAppWidgetHolder.addProviderChangeListener(() -> refreshAndBindWidgetsForPackageUser(null));
 
         mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
 
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 9a0a6eb..4f2d398 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -34,7 +34,6 @@
 import androidx.annotation.NonNull;
 
 import com.android.launcher3.Flags;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -136,14 +135,6 @@
                         iconCache.updateIconsForPkg(packages[i], mUser);
                         activitiesLists.put(
                                 packages[i], appsList.updatePackage(context, packages[i], mUser));
-
-                        // The update may have changed which shortcuts/widgets are available.
-                        // Refresh the widgets for the package if we have an activity running.
-                        Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
-                        if (launcher != null) {
-                            launcher.refreshAndBindWidgetsForPackageUser(
-                                    new PackageUserKey(packages[i], mUser));
-                        }
                     }
                 }
                 // Since package was just updated, the target must be available now.
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 96cc412..9e7d4dc 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -24,9 +24,12 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ITEM_LONG_PRESSED;
 
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.view.View;
 import android.view.View.OnLongClickListener;
 
+import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.celllayout.CellInfo;
@@ -40,6 +43,10 @@
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.views.BubbleTextHolder;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.NavigableAppWidgetHostView;
+import com.android.launcher3.widget.PendingItemDragHelper;
+import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.WidgetImageView;
 
 /**
  * Class to handle long-clicks on workspace items and start drag as a result.
@@ -91,7 +98,46 @@
         launcher.getWorkspace().startDrag(longClickCellInfo, dragOptions);
     }
 
+    private static boolean onWidgetItemLongClick(WidgetCell v) {
+        // Get the widget preview as the drag representation
+        WidgetImageView image = v.getWidgetView();
+        Launcher launcher = Launcher.getLauncher(v.getContext());
+        DragSource dragSource = (target, dragObject, success) -> { };
+
+        // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
+        // we abort the drag.
+        if (image.getDrawable() == null && v.getAppWidgetHostViewPreview() == null) {
+            return false;
+        }
+
+        PendingItemDragHelper dragHelper = new PendingItemDragHelper(v);
+        // RemoteViews are being rendered in AppWidgetHostView in WidgetCell. And thus, the scale of
+        // RemoteViews is equivalent to the AppWidgetHostView scale.
+        dragHelper.setRemoteViewsPreview(v.getRemoteViewsPreview(), v.getAppWidgetHostViewScale());
+        dragHelper.setAppWidgetHostViewPreview(v.getAppWidgetHostViewPreview());
+
+        if (image.getDrawable() != null) {
+            int[] loc = new int[2];
+            launcher.getDragLayer().getLocationInDragLayer(image, loc);
+
+            dragHelper.startDrag(image.getBitmapBounds(), image.getDrawable().getIntrinsicWidth(),
+                    image.getWidth(), new Point(loc[0], loc[1]), dragSource, new DragOptions());
+        } else {
+            NavigableAppWidgetHostView preview = v.getAppWidgetHostViewPreview();
+            int[] loc = new int[2];
+            launcher.getDragLayer().getLocationInDragLayer(preview, loc);
+            Rect r = new Rect();
+            preview.getWorkspaceVisualDragBounds(r);
+            dragHelper.startDrag(r, preview.getMeasuredWidth(), preview.getMeasuredWidth(),
+                    new Point(loc[0], loc[1]), dragSource, new DragOptions());
+        }
+        return true;
+    }
+
     private static boolean onAllAppsItemLongClick(View view) {
+        if (view instanceof WidgetCell wc) {
+            return onWidgetItemLongClick(wc);
+        }
         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onAllAppsItemLongClick");
         view.cancelLongPress();
         View v = (view instanceof BubbleTextHolder)
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 20bcc3c..bef84f7 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -53,6 +53,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.core.view.WindowInsetsCompat;
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
@@ -315,6 +316,25 @@
     }
 
     /**
+     * Returns if the software keyboard is hidden. Hardware keyboards do not display on screen by
+     * default.
+     */
+    default boolean isSoftwareKeyboardHidden() {
+        if (isHardwareKeyboard()) {
+            return true;
+        } else {
+            View dragLayer = getDragLayer();
+            WindowInsets insets = dragLayer.getRootWindowInsets();
+            if (insets == null) {
+                return false;
+            }
+            WindowInsetsCompat insetsCompat =
+                    WindowInsetsCompat.toWindowInsetsCompat(insets, dragLayer);
+            return !insetsCompat.isVisible(WindowInsetsCompat.Type.ime());
+        }
+    }
+
+    /**
      * Sends a pending intent animating from a view.
      *
      * @param v View to animate.
diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java
index e70b1cb..40c6115 100644
--- a/src/com/android/launcher3/views/WidgetsEduView.java
+++ b/src/com/android/launcher3/views/WidgetsEduView.java
@@ -20,15 +20,15 @@
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 
+import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 
 /**
  * Education view about widgets.
  */
-public class WidgetsEduView extends AbstractSlideInView<Launcher> implements Insettable {
+public class WidgetsEduView extends AbstractSlideInView<BaseActivity> implements Insettable {
 
     private static final int DEFAULT_CLOSE_DURATION = 200;
 
@@ -124,10 +124,10 @@
     }
 
     /** Shows widget education dialog. */
-    public static WidgetsEduView showEducationDialog(Launcher launcher) {
-        LayoutInflater layoutInflater = LayoutInflater.from(launcher);
+    public static WidgetsEduView showEducationDialog(BaseActivity activity) {
+        LayoutInflater layoutInflater = LayoutInflater.from(activity);
         WidgetsEduView v = (WidgetsEduView) layoutInflater.inflate(
-                R.layout.widgets_edu, launcher.getDragLayer(), false);
+                R.layout.widgets_edu, activity.getDragLayer(), false);
         v.show();
         return v;
     }
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index fc9c774..26e191d 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
@@ -35,32 +34,28 @@
 import androidx.annotation.Px;
 import androidx.core.view.ViewCompat;
 
+import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.window.WindowManagerProxy;
 import com.android.launcher3.views.AbstractSlideInView;
-import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.ArrowTipView;
 
 /**
  * Base class for various widgets popup
  */
-public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
-        implements OnClickListener, OnLongClickListener, DragSource,
+public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity>
+        implements OnClickListener, OnLongClickListener,
         PopupDataProvider.PopupDataChangeListener, Insettable, OnDeviceProfileChangeListener {
     /** The default number of cells that can fit horizontally in a widget sheet. */
     public static final int DEFAULT_MAX_HORIZONTAL_SPANS = 4;
@@ -129,21 +124,25 @@
         } else {
             mWidgetInstructionToast = showWidgetToast(getContext(), mWidgetInstructionToast);
         }
-
     }
 
     @Override
     public boolean onLongClick(View v) {
         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
         v.cancelLongPress();
-        if (!ItemLongClickListener.canStartDrag(mActivityContext)) return false;
 
+        boolean result = false;
         if (v instanceof WidgetCell) {
-            return beginDraggingWidget((WidgetCell) v);
-        } else if (v.getParent() instanceof WidgetCell) {
-            return beginDraggingWidget((WidgetCell) v.getParent());
+            result = mActivityContext.getAllAppsItemLongClickListener().onLongClick(v);
+        } else if (v.getParent() instanceof WidgetCell wc) {
+            result = mActivityContext.getAllAppsItemLongClickListener().onLongClick(wc);
+        } else {
+            return true;
         }
-        return true;
+        if (result) {
+            close(true);
+        }
+        return result;
     }
 
     @Override
@@ -212,55 +211,12 @@
                 MeasureSpec.getSize(heightMeasureSpec));
     }
 
-    private boolean beginDraggingWidget(WidgetCell v) {
-        // Get the widget preview as the drag representation
-        WidgetImageView image = v.getWidgetView();
-
-        // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
-        // we abort the drag.
-        if (image.getDrawable() == null && v.getAppWidgetHostViewPreview() == null) {
-            return false;
-        }
-
-        PendingItemDragHelper dragHelper = new PendingItemDragHelper(v);
-        // RemoteViews are being rendered in AppWidgetHostView in WidgetCell. And thus, the scale of
-        // RemoteViews is equivalent to the AppWidgetHostView scale.
-        dragHelper.setRemoteViewsPreview(v.getRemoteViewsPreview(), v.getAppWidgetHostViewScale());
-        dragHelper.setAppWidgetHostViewPreview(v.getAppWidgetHostViewPreview());
-
-        if (image.getDrawable() != null) {
-            int[] loc = new int[2];
-            getPopupContainer().getLocationInDragLayer(image, loc);
-
-            dragHelper.startDrag(image.getBitmapBounds(), image.getDrawable().getIntrinsicWidth(),
-                    image.getWidth(), new Point(loc[0], loc[1]), this, new DragOptions());
-        } else {
-            NavigableAppWidgetHostView preview = v.getAppWidgetHostViewPreview();
-            int[] loc = new int[2];
-            getPopupContainer().getLocationInDragLayer(preview, loc);
-            Rect r = new Rect();
-            preview.getWorkspaceVisualDragBounds(r);
-            dragHelper.startDrag(r, preview.getMeasuredWidth(), preview.getMeasuredWidth(),
-                    new Point(loc[0], loc[1]), this, new DragOptions());
-        }
-        close(true);
-        return true;
-    }
-
     @Override
     protected Interpolator getIdleInterpolator() {
         return mActivityContext.getDeviceProfile().isTablet
                 ? EMPHASIZED : super.getIdleInterpolator();
     }
 
-    //
-    // Drag related handling methods that implement {@link DragSource} interface.
-    //
-
-    @Override
-    public void onDropCompleted(View target, DragObject d, boolean success) { }
-
-
     protected void onCloseComplete() {
         super.onCloseComplete();
         clearNavBarColor();
@@ -344,7 +300,8 @@
     @Override
     protected void setTranslationShift(float translationShift) {
         super.setTranslationShift(translationShift);
-        Launcher launcher = ActivityContext.lookupContext(getContext());
-        launcher.onWidgetsTransition(1 - translationShift);
+        if (mActivityContext instanceof Launcher ls) {
+            ls.onWidgetsTransition(1 - translationShift);
+        }
     }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index af77d03..953ecda 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -55,8 +55,9 @@
 import androidx.recyclerview.widget.DefaultItemAnimator;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -72,7 +73,6 @@
 import com.android.launcher3.views.StickyHeaderLayout;
 import com.android.launcher3.views.WidgetsEduView;
 import com.android.launcher3.widget.BaseWidgetSheet;
-import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 import com.android.launcher3.widget.picker.search.SearchModeListener;
 import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
@@ -89,7 +89,7 @@
  * Popup for showing the full list of available widgets
  */
 public class WidgetsFullSheet extends BaseWidgetSheet
-        implements ProviderChangedListener, OnActivePageChangedListener,
+        implements OnActivePageChangedListener,
         WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
 
     private static final long FADE_IN_DURATION = 150;
@@ -166,7 +166,7 @@
     private boolean mIsInSearchMode;
     private boolean mIsNoWidgetsViewNeeded;
     @Px private int mMaxSpanPerRow;
-    private DeviceProfile mDeviceProfile;
+    private final DeviceProfile mDeviceProfile;
 
     private int mOrientation;
 
@@ -181,7 +181,7 @@
 
     public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mDeviceProfile = Launcher.getLauncher(context).getDeviceProfile();
+        mDeviceProfile = mActivityContext.getDeviceProfile();
         mHasWorkProfile = context.getSystemService(LauncherApps.class).getProfiles().size() > 1;
         mOrientation = context.getResources().getConfiguration().orientation;
         mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
@@ -353,15 +353,14 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mActivityContext.getAppWidgetHolder().addProviderChangeListener(this);
-        notifyWidgetProvidersChanged();
+        LauncherAppState.getInstance(mActivityContext).getModel()
+                .refreshAndBindWidgetsAndShortcuts(null);
         onRecommendedWidgetsBound();
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mActivityContext.getAppWidgetHolder().removeProviderChangeListener(this);
         mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView
                 .removeOnAttachStateChangeListener(mBindScrollbarInSearchMode);
         if (mHasWorkProfile) {
@@ -482,11 +481,6 @@
     }
 
     @Override
-    public void notifyWidgetProvidersChanged() {
-        mActivityContext.refreshAndBindWidgetsForPackageUser(null);
-    }
-
-    @Override
     public void onWidgetsBound() {
         if (mIsInSearchMode) {
             return;
@@ -682,22 +676,22 @@
     }
 
     /** Shows the {@link WidgetsFullSheet} on the launcher. */
-    public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
-        boolean isTwoPane = launcher.getDeviceProfile().isTablet
-                && launcher.getDeviceProfile().isLandscape
-                && (!launcher.getDeviceProfile().isTwoPanels
+    public static WidgetsFullSheet show(BaseActivity activity, boolean animate) {
+        boolean isTwoPane = activity.getDeviceProfile().isTablet
+                && activity.getDeviceProfile().isLandscape
+                && (!activity.getDeviceProfile().isTwoPanels
                     || FeatureFlags.UNFOLDED_WIDGET_PICKER.get());
 
         WidgetsFullSheet sheet;
         if (isTwoPane) {
-            sheet = (WidgetsTwoPaneSheet) launcher.getLayoutInflater().inflate(
+            sheet = (WidgetsTwoPaneSheet) activity.getLayoutInflater().inflate(
                     R.layout.widgets_two_pane_sheet,
-                    launcher.getDragLayer(),
+                    activity.getDragLayer(),
                     false);
         } else {
-            sheet = (WidgetsFullSheet) launcher.getLayoutInflater().inflate(
+            sheet = (WidgetsFullSheet) activity.getLayoutInflater().inflate(
                     R.layout.widgets_full_sheet,
-                    launcher.getDragLayer(),
+                    activity.getDragLayer(),
                     false);
         }
 
@@ -754,7 +748,7 @@
 
     /** Gets the {@link WidgetsRecyclerView} which shows all widgets in {@link WidgetsFullSheet}. */
     @VisibleForTesting
-    public static WidgetsRecyclerView getWidgetsView(Launcher launcher) {
+    public static WidgetsRecyclerView getWidgetsView(BaseActivity launcher) {
         return launcher.findViewById(R.id.primary_widgets_list_view);
     }
 
@@ -803,7 +797,7 @@
             mOrientation = newConfig.orientation;
             if (mDeviceProfile.isTablet && !mDeviceProfile.isTwoPanels) {
                 handleClose(false);
-                show(Launcher.getLauncher(getContext()), false);
+                show(BaseActivity.fromContext(getContext()), false);
             } else {
                 reset();
             }
diff --git a/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java b/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java
index d71bf84..41e3ea1 100644
--- a/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java
@@ -23,8 +23,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.view.KeyEvent;
-import android.view.View;
-import android.view.WindowInsets;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -33,6 +31,7 @@
 import com.android.launcher3.tapl.HomeAllApps;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.launcher3.views.ActivityContext;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -102,11 +101,8 @@
 
             executeOnLauncher(launcher -> launcher.getAppsView().getSearchUiManager().getEditText()
                     .hideKeyboard(/* clearFocus= */ false));
-            waitForLauncherCondition("Keyboard still visible.", launcher -> {
-                View root = launcher.getDragLayer();
-                WindowInsets insets = root.getRootWindowInsets();
-                return insets != null && !insets.isVisible(WindowInsets.Type.ime());
-            });
+            waitForLauncherCondition("Keyboard still visible.",
+                    ActivityContext::isSoftwareKeyboardHidden);
 
             mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0);
             mLauncher.unpressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0);