Enable Dragging on predicted hybrid hotseat items

Bug: 142753423
Test: Manual
Change-Id: I2d54f82129d30bd289d05039e009a10543d55f34
diff --git a/quickstep/recents_ui_overrides/res/drawable/predicted_icon_background.xml b/quickstep/recents_ui_overrides/res/drawable/predicted_icon_background.xml
deleted file mode 100644
index cfc6d48..0000000
--- a/quickstep/recents_ui_overrides/res/drawable/predicted_icon_background.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:inset="@dimen/predicted_icon_background_inset">
-    <shape>
-        <solid android:color="?attr/folderFillColor" />
-        <corners android:radius="@dimen/predicted_icon_background_corner_radius" />
-    </shape>
-</inset>
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
index f7e71f3..b94142a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -61,7 +62,7 @@
 public class HotseatPredictionController implements DragController.DragListener,
         View.OnAttachStateChangeListener, SystemShortcut.Factory<QuickstepLauncher>,
         InvariantDeviceProfile.OnIDPChangeListener, AllAppsStore.OnUpdateListener,
-        IconCache.ItemInfoUpdateReceiver {
+        IconCache.ItemInfoUpdateReceiver, DragSource {
 
     private static final String TAG = "PredictiveHotseat";
     private static final boolean DEBUG = false;
@@ -72,6 +73,9 @@
     private static final String APP_LOCATION_HOTSEAT = "hotseat";
     private static final String APP_LOCATION_WORKSPACE = "workspace";
 
+    private static final String BUNDLE_KEY_HOTSEAT = "hotseat_apps";
+    private static final String BUNDLE_KEY_WORKSPACE = "workspace_apps";
+
     private static final String PREDICTION_CLIENT = "hotseat";
 
     private DropTarget.DragObject mDragObject;
@@ -79,7 +83,7 @@
     private int mPredictedSpotsCount = 0;
 
     private Launcher mLauncher;
-    private Hotseat mHotseat;
+    private final Hotseat mHotseat;
 
     private List<ComponentKeyMapper> mComponentKeyMappers = new ArrayList<>();
 
@@ -87,10 +91,18 @@
 
     private AppPredictor mAppPredictor;
     private AllAppsStore mAllAppsStore;
+    private AnimatorSet mIconRemoveAnimators;
+
 
     private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
 
-    private static HotseatPredictionController sInstance;
+    private final View.OnLongClickListener mPredictionLongClickListener = v -> {
+        if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
+        if (mLauncher.getWorkspace().isSwitchingState()) return false;
+        // Start the drag
+        mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions());
+        return false;
+    };
 
     public HotseatPredictionController(Launcher launcher) {
         mLauncher = launcher;
@@ -101,7 +113,9 @@
         mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
         launcher.getDeviceProfile().inv.addOnChangeListener(this);
         mHotseat.addOnAttachStateChangeListener(this);
-        sInstance = this;
+        if (mHotseat.isAttachedToWindow()) {
+            onViewAttachedToWindow(mHotseat);
+        }
     }
 
     @Override
@@ -125,6 +139,17 @@
         List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers);
         int predictionIndex = 0;
         ArrayList<WorkspaceItemInfo> newItems = new ArrayList<>();
+        // make sure predicted icon removal and filling predictions don't step on each other
+        if (mIconRemoveAnimators != null && mIconRemoveAnimators.isRunning()) {
+            mIconRemoveAnimators.addListener(new AnimationSuccessListener() {
+                @Override
+                public void onAnimationSuccess(Animator animator) {
+                    fillGapsWithPrediction(animate, callback);
+                    mIconRemoveAnimators.removeListener(this);
+                }
+            });
+            return;
+        }
         for (int rank = 0; rank < mHotSeatItemsCount; rank++) {
             View child = mHotseat.getChildAt(
                     mHotseat.getCellXFromOrder(rank),
@@ -140,12 +165,11 @@
                 }
                 continue;
             }
-
             WorkspaceItemInfo predictedItem = predictedApps.get(predictionIndex++);
             if (isPredictedIcon(child) && child.isEnabled()) {
                 PredictedAppIcon icon = (PredictedAppIcon) child;
                 icon.applyFromWorkspaceItem(predictedItem);
-                icon.finishBinding();
+                icon.finishBinding(mPredictionLongClickListener);
             } else {
                 newItems.add(predictedItem);
             }
@@ -160,7 +184,7 @@
         for (WorkspaceItemInfo item : itemsToAdd) {
             PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item);
             mLauncher.getWorkspace().addInScreenFromBind(icon, item);
-            icon.finishBinding();
+            icon.finishBinding(mPredictionLongClickListener);
             if (animate) {
                 animationSet.play(ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 0.2f, 1));
             }
@@ -215,9 +239,9 @@
 
     private Bundle getAppPredictionContextExtra() {
         Bundle bundle = new Bundle();
-        bundle.putParcelableArrayList(APP_LOCATION_HOTSEAT,
+        bundle.putParcelableArrayList(BUNDLE_KEY_HOTSEAT,
                 getPinnedAppTargetsInViewGroup((mHotseat.getShortcutsAndWidgets())));
-        bundle.putParcelableArrayList(APP_LOCATION_WORKSPACE, getPinnedAppTargetsInViewGroup(
+        bundle.putParcelableArrayList(BUNDLE_KEY_WORKSPACE, getPinnedAppTargetsInViewGroup(
                 mLauncher.getWorkspace().getScreenWithId(
                         Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets()));
         return bundle;
@@ -285,9 +309,12 @@
             ItemInfoWithIcon info = mapper.getApp(allAppsStore);
             if (info instanceof AppInfo) {
                 WorkspaceItemInfo predictedApp = new WorkspaceItemInfo((AppInfo) info);
+                predictedApp.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
                 predictedApps.add(predictedApp);
             } else if (info instanceof WorkspaceItemInfo) {
-                predictedApps.add(new WorkspaceItemInfo((WorkspaceItemInfo) info));
+                WorkspaceItemInfo predictedApp = new WorkspaceItemInfo((WorkspaceItemInfo) info);
+                predictedApp.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+                predictedApps.add(predictedApp);
             } else {
                 if (DEBUG) {
                     Log.e(TAG, "Predicted app not found: " + mapper);
@@ -313,13 +340,27 @@
         return icons;
     }
 
-    private void removePredictedApps(List<PredictedAppIcon.PredictedIconOutlineDrawing> outlines) {
+    private void removePredictedApps(List<PredictedAppIcon.PredictedIconOutlineDrawing> outlines,
+            ItemInfo draggedInfo) {
+        if (mIconRemoveAnimators != null) {
+            mIconRemoveAnimators.end();
+        }
+        mIconRemoveAnimators = new AnimatorSet();
+        removeOutlineDrawings();
         for (PredictedAppIcon icon : getPredictedIcons()) {
+            if (!icon.isEnabled()) {
+                continue;
+            }
+            if (icon.getTag().equals(draggedInfo)) {
+                mHotseat.removeView(icon);
+                continue;
+            }
             int rank = ((WorkspaceItemInfo) icon.getTag()).rank;
             outlines.add(new PredictedAppIcon.PredictedIconOutlineDrawing(
                     mHotseat.getCellXFromOrder(rank), mHotseat.getCellYFromOrder(rank), icon));
             icon.setEnabled(false);
-            icon.animate().scaleY(0).scaleX(0).setListener(new AnimationSuccessListener() {
+            ObjectAnimator animator = ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 0);
+            animator.addListener(new AnimationSuccessListener() {
                 @Override
                 public void onAnimationSuccess(Animator animator) {
                     if (icon.getParent() != null) {
@@ -327,10 +368,11 @@
                     }
                 }
             });
+            mIconRemoveAnimators.play(animator);
         }
+        mIconRemoveAnimators.start();
     }
 
-
     private void notifyItemAction(AppTarget target, String location, int action) {
         if (mAppPredictor != null) {
             mAppPredictor.notifyAppTargetEvent(new AppTargetEvent.Builder(target,
@@ -340,7 +382,7 @@
 
     @Override
     public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
-        removePredictedApps(mOutlineDrawings);
+        removePredictedApps(mOutlineDrawings, dragObject.dragInfo);
         mDragObject = dragObject;
         if (mOutlineDrawings.isEmpty()) return;
         for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
@@ -354,14 +396,25 @@
         if (mDragObject == null) {
             return;
         }
+
         ItemInfo dragInfo = mDragObject.dragInfo;
-        if (dragInfo instanceof WorkspaceItemInfo && dragInfo.getTargetComponent() != null) {
+        ViewGroup hotseatVG = mHotseat.getShortcutsAndWidgets();
+        ViewGroup firstScreenVG = mLauncher.getWorkspace().getScreenWithId(
+                Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets();
+
+        if (dragInfo instanceof WorkspaceItemInfo
+                && dragInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+                && dragInfo.getTargetComponent() != null) {
             AppTarget appTarget = getAppTargetFromItemInfo(dragInfo);
             if (!isInHotseat(dragInfo) && isInHotseat(mDragObject.originalDragInfo)) {
-                notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, APPTARGET_ACTION_UNPIN);
+                if (!getPinnedAppTargetsInViewGroup(hotseatVG).contains(appTarget)) {
+                    notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, APPTARGET_ACTION_UNPIN);
+                }
             }
             if (!isInFirstPage(dragInfo) && isInFirstPage(mDragObject.originalDragInfo)) {
-                notifyItemAction(appTarget, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
+                if (!getPinnedAppTargetsInViewGroup(firstScreenVG).contains(appTarget)) {
+                    notifyItemAction(appTarget, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
+                }
             }
             if (isInHotseat(dragInfo) && !isInHotseat(mDragObject.originalDragInfo)) {
                 notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
@@ -371,14 +424,7 @@
             }
         }
         mDragObject = null;
-        fillGapsWithPrediction(true, () -> {
-            if (mOutlineDrawings.isEmpty()) return;
-            for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
-                mHotseat.removeDelegatedCellDrawing(outlineDrawing);
-            }
-            mHotseat.invalidate();
-            mOutlineDrawings.clear();
-        });
+        fillGapsWithPrediction(true, this::removeOutlineDrawings);
     }
 
     @Nullable
@@ -394,11 +440,20 @@
     private void preparePredictionInfo(WorkspaceItemInfo itemInfo, int rank) {
         itemInfo.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
         itemInfo.rank = rank;
-        itemInfo.cellX = rank;
-        itemInfo.cellY = mHotSeatItemsCount - rank - 1;
+        itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
+        itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
         itemInfo.screenId = rank;
     }
 
+    private void removeOutlineDrawings() {
+        if (mOutlineDrawings.isEmpty()) return;
+        for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
+            mHotseat.removeDelegatedCellDrawing(outlineDrawing);
+        }
+        mHotseat.invalidate();
+        mOutlineDrawings.clear();
+    }
+
     @Override
     public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
         this.mHotSeatItemsCount = profile.numHotseatIcons;
@@ -415,6 +470,17 @@
 
     }
 
+    @Override
+    public void onDropCompleted(View target, DropTarget.DragObject d, boolean success) {
+        //Does nothing
+    }
+
+    @Override
+    public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
+            LauncherLogProto.Target targetParent) {
+        mHotseat.fillInLogContainerData(v, info, target, targetParent);
+    }
+
     private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
 
         private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
@@ -435,18 +501,22 @@
      */
     public static void fillInHybridHotseatRank(
             @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
-        if (sInstance == null || itemInfo.getTargetComponent() == null
+        QuickstepLauncher launcher = QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
+        if (launcher == null || launcher.getHotseatPredictionController() == null
+                || itemInfo.getTargetComponent() == null
                 || itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
             return;
         }
+        HotseatPredictionController controller = launcher.getHotseatPredictionController();
+
         final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
 
-        final List<ComponentKeyMapper> predictedApps = sInstance.mComponentKeyMappers;
+        final List<ComponentKeyMapper> predictedApps = controller.mComponentKeyMappers;
         IntStream.range(0, predictedApps.size())
                 .filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
                 .findFirst()
                 .ifPresent((rank) -> target.predictedRank =
-                        Integer.parseInt(sInstance.mPredictedSpotsCount + "0" + rank));
+                        Integer.parseInt(controller.mPredictedSpotsCount + "0" + rank));
     }
 
     private static boolean isPredictedIcon(View view) {
@@ -461,8 +531,7 @@
         }
         ItemInfo info = (ItemInfo) view.getTag();
         return info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION && (
-                info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
-                        || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
+                info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION);
     }
 
     private static boolean isInHotseat(ItemInfo itemInfo) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 1dcbffb..27ac284 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -29,7 +29,6 @@
 
 import androidx.core.graphics.ColorUtils;
 
-import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
@@ -37,7 +36,6 @@
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.icons.IconNormalizer;
-import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.views.DoubleShadowBubbleTextView;
@@ -47,14 +45,13 @@
  */
 public class PredictedAppIcon extends DoubleShadowBubbleTextView {
 
-    private static final float RING_EFFECT_RATIO = 0.12f;
+    private static final float RING_EFFECT_RATIO = 0.11f;
 
     private DeviceProfile mDeviceProfile;
     private final Paint mIconRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private boolean mIsPinned = false;
     private int mNormalizedIconRadius;
 
-
     public PredictedAppIcon(Context context) {
         this(context, null, 0);
     }
@@ -105,14 +102,8 @@
     /**
      * prepares prediction icon for usage after bind
      */
-    public void finishBinding() {
-        setOnLongClickListener((v) -> {
-            PopupContainerWithArrow.showForIcon((BubbleTextView) v);
-            if (getParent() != null) {
-                getParent().requestDisallowInterceptTouchEvent(true);
-            }
-            return true;
-        });
+    public void finishBinding(OnLongClickListener longClickListener) {
+        setOnLongClickListener(longClickListener);
         ((CellLayout.LayoutParams) getLayoutParams()).canReorder = false;
         setTextVisibility(false);
         verifyHighRes();
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 37a3929..cae01ae 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -178,6 +178,13 @@
     }
 
     /**
+     * Returns Prediction controller for hybrid hotseat
+     */
+    public HotseatPredictionController getHotseatPredictionController() {
+        return mHotseatPredictionController;
+    }
+
+    /**
      * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
      */
     private void onStateOrResumeChanged() {