Merge "Add shortcut drag/drop support to the taskbar."
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index b3a9f8d..3315f8c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -51,6 +51,8 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ClipDescriptionCompat;
 import com.android.systemui.shared.system.LauncherAppsCompat;
@@ -88,6 +90,21 @@
      * @return Whether {@link View#startDragAndDrop} started successfully.
      */
     protected boolean startDragOnLongClick(View view) {
+        return startDragOnLongClick(view, null, null);
+    }
+
+    protected boolean startDragOnLongClick(
+            DeepShortcutView shortcutView, Point iconShift) {
+        return startDragOnLongClick(
+                shortcutView.getBubbleText(),
+                new ShortcutDragPreviewProvider(shortcutView.getIconView(), iconShift),
+                iconShift);
+    }
+
+    private boolean startDragOnLongClick(
+            View view,
+            @Nullable DragPreviewProvider dragPreviewProvider,
+            @Nullable Point iconShift) {
         if (!(view instanceof BubbleTextView)) {
             return false;
         }
@@ -96,7 +113,10 @@
 
         mActivity.setTaskbarWindowFullscreen(true);
         btv.post(() -> {
-            startInternalDrag(btv);
+            DragView dragView = startInternalDrag(btv, dragPreviewProvider);
+            if (iconShift != null) {
+                dragView.animateShift(-iconShift.x, -iconShift.y);
+            }
             btv.getIcon().setIsDisabled(true);
             mControllers.taskbarAutohideSuspendController.updateFlag(
                     TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, true);
@@ -104,7 +124,8 @@
         return true;
     }
 
-    private void startInternalDrag(BubbleTextView btv) {
+    private DragView startInternalDrag(
+            BubbleTextView btv, @Nullable DragPreviewProvider dragPreviewProvider) {
         float iconScale = btv.getIcon().getAnimatedScale();
 
         // Clear the pressed state if necessary
@@ -112,7 +133,8 @@
         btv.setPressed(false);
         btv.clearPressedBackground();
 
-        final DragPreviewProvider previewProvider = new DragPreviewProvider(btv);
+        final DragPreviewProvider previewProvider = dragPreviewProvider == null
+                ? new DragPreviewProvider(btv) : dragPreviewProvider;
         final Drawable drawable = previewProvider.createDrawable();
         final float scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
         int dragLayerX = mTempXY[0];
@@ -149,7 +171,7 @@
             }
         }
 
-        startDrag(
+        return startDrag(
                 drawable,
                 /* view = */ null,
                 /* originalView = */ btv,
@@ -241,7 +263,8 @@
 
             @Override
             public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
-                shadowSize.set(mDragIconSize, mDragIconSize);
+                int iconSize = Math.max(mDragIconSize, btv.getWidth());
+                shadowSize.set(iconSize, iconSize);
                 // The registration point was taken before the icon scaled to mDragIconSize, so
                 // offset the registration to where the touch is on the new size.
                 int offsetX = (mDragIconSize - mDragObject.dragView.getDragRegionWidth()) / 2;
@@ -273,6 +296,12 @@
                     });
             intent = new Intent();
             if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                intent.putExtra(ClipDescriptionCompat.EXTRA_PENDING_INTENT,
+                        launcherApps.getShortcutIntent(
+                                item.getIntent().getPackage(),
+                                item.getDeepShortcutId(),
+                                null,
+                                item.user));
                 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, item.getIntent().getPackage());
                 intent.putExtra(Intent.EXTRA_SHORTCUT_ID, item.getDeepShortcutId());
             } else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 06c75fc..2dee506 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -15,6 +15,10 @@
  */
 package com.android.launcher3.taskbar;
 
+import android.graphics.Point;
+import android.view.MotionEvent;
+import android.view.View;
+
 import androidx.annotation.NonNull;
 
 import com.android.launcher3.BubbleTextView;
@@ -30,6 +34,7 @@
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.popup.PopupLiveUpdateHandler;
 import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.PackageUserKey;
@@ -131,8 +136,14 @@
                 (PopupContainerWithArrow) context.getLayoutInflater().inflate(
                         R.layout.popup_container, context.getDragLayer(), false);
         container.addOnAttachStateChangeListener(
-                new PopupLiveUpdateHandler<>(mContext, container));
+                new PopupLiveUpdateHandler<TaskbarActivityContext>(mContext, container) {
+                    @Override
+                    protected void showPopupContainerForIcon(BubbleTextView originalIcon) {
+                        showForIcon(originalIcon);
+                    }
+                });
         // TODO (b/198438631): configure for taskbar/context
+        container.setPopupItemDragHandler(new TaskbarPopupItemDragHandler());
 
         container.populateAndShow(icon,
                 mPopupDataProvider.getShortcutCountForItem(item),
@@ -145,4 +156,43 @@
         container.requestFocus();
         return container;
     }
+
+    private class TaskbarPopupItemDragHandler implements
+            PopupContainerWithArrow.PopupItemDragHandler {
+
+        protected final Point mIconLastTouchPos = new Point();
+
+        TaskbarPopupItemDragHandler() {}
+
+        @Override
+        public boolean onTouch(View view, MotionEvent ev) {
+            // Touched a shortcut, update where it was touched so we can drag from there on
+            // long click.
+            switch (ev.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                case MotionEvent.ACTION_MOVE:
+                    mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY());
+                    break;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean onLongClick(View v) {
+            // Return early if not the correct view
+            if (!(v.getParent() instanceof DeepShortcutView)) return false;
+
+            DeepShortcutView sv = (DeepShortcutView) v.getParent();
+            sv.setWillDrawIcon(false);
+
+            // Move the icon to align with the center-top of the touch point
+            Point iconShift = new Point();
+            iconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
+            iconShift.y = mIconLastTouchPos.y - mContext.getDeviceProfile().iconSizePx;
+
+            mControllers.taskbarDragController.startDragOnLongClick(sv, iconShift);
+
+            return false;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java b/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java
index 731f439..3e3f633 100644
--- a/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java
+++ b/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java
@@ -18,6 +18,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.model.data.ItemInfo;
@@ -86,4 +87,9 @@
             }
         }
     }
+
+    @Override
+    protected void showPopupContainerForIcon(BubbleTextView originalIcon) {
+        PopupContainerWithArrow.showForIcon(originalIcon);
+    }
 }
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index aa8c70b..0097705 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -152,6 +152,10 @@
         };
     }
 
+    public void setPopupItemDragHandler(PopupItemDragHandler popupItemDragHandler) {
+        mPopupItemDragHandler = popupItemDragHandler;
+    }
+
     public PopupItemDragHandler getItemDragHandler() {
         return mPopupItemDragHandler;
     }
diff --git a/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java b/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
index 194c22f..c5d5452 100644
--- a/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
+++ b/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.view.View;
 
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.notification.NotificationContainer;
@@ -36,7 +37,7 @@
  *
  * @param <T> The activity on which the popup shows
  */
-public class PopupLiveUpdateHandler<T extends Context & ActivityContext> implements
+public abstract class PopupLiveUpdateHandler<T extends Context & ActivityContext> implements
         PopupDataProvider.PopupDataChangeListener, View.OnAttachStateChangeListener {
 
     protected final T mContext;
@@ -103,6 +104,8 @@
     @Override
     public void onSystemShortcutsUpdated() {
         mPopupContainerWithArrow.close(true);
-        PopupContainerWithArrow.showForIcon(mPopupContainerWithArrow.getOriginalIcon());
+        showPopupContainerForIcon(mPopupContainerWithArrow.getOriginalIcon());
     }
+
+    protected abstract void showPopupContainerForIcon(BubbleTextView originalIcon);
 }
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index cecbb0d..c166bfc 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -23,12 +23,12 @@
 import android.graphics.drawable.Drawable;
 import android.view.View;
 
-import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.views.ActivityContext;
 
 /**
  * Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
@@ -45,7 +45,8 @@
     @Override
     public Drawable createDrawable() {
         if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
-            int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
+            int size = ActivityContext.lookupContext(mView.getContext())
+                    .getDeviceProfile().iconSizePx;
             return new FastBitmapDrawable(
                     BitmapRenderer.createHardwareBitmap(
                             size + blurSizeOutline,
@@ -59,7 +60,7 @@
     private Bitmap createDragBitmapLegacy() {
         Drawable d = mView.getBackground();
         Rect bounds = getDrawableBounds(d);
-        int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
+        int size = ActivityContext.lookupContext(mView.getContext()).getDeviceProfile().iconSizePx;
         final Bitmap b = Bitmap.createBitmap(
                 size + blurSizeOutline,
                 size + blurSizeOutline,
@@ -84,9 +85,9 @@
 
     @Override
     public float getScaleAndPosition(Drawable preview, int[] outPos) {
-        Launcher launcher = Launcher.getLauncher(mView.getContext());
+        ActivityContext context = ActivityContext.lookupContext(mView.getContext());
         int iconSize = getDrawableBounds(mView.getBackground()).width();
-        float scale = launcher.getDragLayer().getLocationInDragLayer(mView, outPos);
+        float scale = context.getDragLayer().getLocationInDragLayer(mView, outPos);
 
         int iconLeft = mView.getPaddingStart();
         if (Utilities.isRtl(mView.getResources())) {
@@ -98,7 +99,7 @@
                         + mPositionShift.x);
         outPos[1] += Math.round((scale * mView.getHeight() - preview.getIntrinsicHeight()) / 2
                 + mPositionShift.y);
-        float size = launcher.getDeviceProfile().iconSizePx;
+        float size = context.getDeviceProfile().iconSizePx;
         return scale * iconSize / size;
     }
 }