Allow user to place PendingAddShortcutInfo in opened Folder.

Current implementation only allowed dropping PendingAddShortcutInfo
on a FolderIcon.

Bug: 37814579
Change-Id: Ice42421c34665b0ebf199945761c5c86614573a4
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 0041bb4..21254ab 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -65,9 +65,17 @@
      * @param item
      */
     public void add(ShortcutInfo item, boolean animate) {
-        contents.add(item);
+        add(item, contents.size(), animate);
+    }
+
+    /**
+     * Add an app or shortcut for a specified rank.
+     */
+    public void add(ShortcutInfo item, int rank, boolean animate) {
+        rank = Utilities.boundToRange(rank, 0, contents.size());
+        contents.add(rank, item);
         for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onAdd(item);
+            listeners.get(i).onAdd(item, rank);
         }
         itemsChanged(animate);
     }
@@ -121,7 +129,7 @@
     }
 
     public interface FolderListener {
-        public void onAdd(ShortcutInfo item);
+        public void onAdd(ShortcutInfo item, int rank);
         public void onRemove(ShortcutInfo item);
         public void onTitleChanged(CharSequence title);
         public void onItemsChanged(boolean animate);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4be25bb..c5241d5 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -84,6 +84,7 @@
 
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsTransitionController;
@@ -158,14 +159,15 @@
 
     private static final int REQUEST_CREATE_SHORTCUT = 1;
     private static final int REQUEST_CREATE_APPWIDGET = 5;
+
     private static final int REQUEST_PICK_APPWIDGET = 9;
     private static final int REQUEST_PICK_WALLPAPER = 10;
 
     private static final int REQUEST_BIND_APPWIDGET = 11;
-    private static final int REQUEST_BIND_PENDING_APPWIDGET = 14;
-    private static final int REQUEST_RECONFIGURE_APPWIDGET = 12;
+    private static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
+    private static final int REQUEST_RECONFIGURE_APPWIDGET = 13;
 
-    private static final int REQUEST_PERMISSION_CALL_PHONE = 13;
+    private static final int REQUEST_PERMISSION_CALL_PHONE = 14;
 
     private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
 
@@ -1407,20 +1409,20 @@
     }
 
     /**
-     * Add a shortcut to the workspace.
+     * Add a shortcut to the workspace or to a Folder.
      *
      * @param data The intent describing the shortcut.
      */
     private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
             int cellY, PendingRequestArgs args) {
-        int[] cellXY = mTmpAddItemCellCoordinates;
-        CellLayout layout = getCellLayout(container, screenId);
-
-        if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT ||
-                args.getPendingIntent().getComponent() == null) {
+        if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT
+                || args.getPendingIntent().getComponent() == null) {
             return;
         }
 
+        int[] cellXY = mTmpAddItemCellCoordinates;
+        CellLayout layout = getCellLayout(container, screenId);
+
         ShortcutInfo info = null;
         if (Utilities.isAtLeastO()) {
             info = LauncherAppsCompat.createShortcutInfoFromPinItemRequest(
@@ -1443,36 +1445,55 @@
             }
         }
 
-        final View view = createShortcut(info);
-        boolean foundCellSpan = false;
-        // First we check if we already know the exact location where we want to add this item.
-        if (cellX >= 0 && cellY >= 0) {
-            cellXY[0] = cellX;
-            cellXY[1] = cellY;
-            foundCellSpan = true;
+        if (container < 0) {
+            // Adding a shortcut to the Workspace.
+            final View view = createShortcut(info);
+            boolean foundCellSpan = false;
+            // First we check if we already know the exact location where we want to add this item.
+            if (cellX >= 0 && cellY >= 0) {
+                cellXY[0] = cellX;
+                cellXY[1] = cellY;
+                foundCellSpan = true;
 
-            // If appropriate, either create a folder or add to an existing folder
-            if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
-                    true, null,null)) {
+                // If appropriate, either create a folder or add to an existing folder
+                if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
+                        true, null, null)) {
+                    return;
+                }
+                DragObject dragObject = new DragObject();
+                dragObject.dragInfo = info;
+                if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
+                        true)) {
+                    return;
+                }
+            } else {
+                foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
+            }
+
+            if (!foundCellSpan) {
+                mWorkspace.onNoCellFound(layout);
                 return;
             }
-            DragObject dragObject = new DragObject();
-            dragObject.dragInfo = info;
-            if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
-                    true)) {
-                return;
-            }
+
+            getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
+            mWorkspace.addInScreen(view, info);
         } else {
-            foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
-        }
+            // Adding a shortcut to a Folder.
+            final long folderIconId = container;
+            FolderIcon folderIcon = (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() {
+                @Override
+                public boolean evaluate(ItemInfo info, View view) {
+                    return info != null && info.id == folderIconId;
+                }
+            });
 
-        if (!foundCellSpan) {
-            mWorkspace.onNoCellFound(layout);
-            return;
+            if (folderIcon != null) {
+                FolderInfo folderInfo = (FolderInfo) folderIcon.getTag();
+                folderInfo.add(info, args.rank, false);
+            } else {
+                Log.e(TAG, "Could not find folder with id " + folderIconId + " to add shortcut.");
+            }
         }
-
-        getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
-        mWorkspace.addInScreen(view, info);
     }
 
     /**
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index c6bf3a1..8dcf5c9 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -60,6 +60,7 @@
 import com.android.launcher3.LogDecelerateInterpolator;
 import com.android.launcher3.OnAlarmListener;
 import com.android.launcher3.PagedView;
+import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.UninstallDropTarget.DropTargetSource;
@@ -77,6 +78,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -792,7 +794,6 @@
             }
         }
 
-
         if (mRearrangeOnClose) {
             rearrangeChildren();
             mRearrangeOnClose = false;
@@ -1344,53 +1345,68 @@
         }
         mContent.completePendingPageChanges();
 
-        View currentDragView;
-        final ShortcutInfo si;
-        if (d.dragInfo instanceof AppInfo) {
-            // Came from all apps -- make a copy.
-            si = ((AppInfo) d.dragInfo).makeShortcut();
-        } else {
-            // ShortcutInfo
-            si = (ShortcutInfo) d.dragInfo;
-        }
-        if (mIsExternalDrag) {
-            currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
-            // Actually move the item in the database if it was an external drag. Call this
-            // before creating the view, so that ShortcutInfo is updated appropriately.
-            mLauncher.getModelWriter().addOrMoveItemInDatabase(
-                    si, mInfo.id, 0, si.cellX, si.cellY);
+        if (d.dragInfo instanceof PendingAddShortcutInfo) {
+            PendingAddShortcutInfo pasi = (PendingAddShortcutInfo) d.dragInfo;
+            pasi.container = mInfo.id;
+            pasi.rank = mEmptyCellRank;
 
-            // We only need to update the locations if it doesn't get handled in #onDropCompleted.
-            if (d.dragSource != this) {
-                updateItemLocationsInDatabaseBatch();
-            }
-            mIsExternalDrag = false;
-        } else {
-            currentDragView = mCurrentDragView;
-            mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
-        }
-
-        if (d.dragView.hasDrawn()) {
-
-            // Temporarily reset the scale such that the animation target gets calculated correctly.
-            float scaleX = getScaleX();
-            float scaleY = getScaleY();
-            setScaleX(1.0f);
-            setScaleY(1.0f);
-            mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView,
-                    cleanUpRunnable, null);
-            setScaleX(scaleX);
-            setScaleY(scaleY);
-        } else {
+            mLauncher.addPendingItem(pasi, pasi.container, pasi.screenId, null, pasi.spanX,
+                    pasi.spanY);
             d.deferDragViewCleanupPostAnimation = false;
-            currentDragView.setVisibility(VISIBLE);
-        }
-        mItemsInvalidated = true;
-        rearrangeChildren();
+            mRearrangeOnClose = true;
+        } else {
+            final ShortcutInfo si;
+            if (d.dragInfo instanceof AppInfo) {
+                // Came from all apps -- make a copy.
+                si = ((AppInfo) d.dragInfo).makeShortcut();
+            } else {
+                // ShortcutInfo
+                si = (ShortcutInfo) d.dragInfo;
+            }
 
-        // Temporarily suppress the listener, as we did all the work already here.
-        try (SuppressInfoChanges s = new SuppressInfoChanges()) {
-            mInfo.add(si, false);
+            View currentDragView;
+            if (mIsExternalDrag) {
+                currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
+
+                // Actually move the item in the database if it was an external drag. Call this
+                // before creating the view, so that ShortcutInfo is updated appropriately.
+                mLauncher.getModelWriter().addOrMoveItemInDatabase(
+                        si, mInfo.id, 0, si.cellX, si.cellY);
+
+                // We only need to update the locations if it doesn't get handled in
+                // #onDropCompleted.
+                if (d.dragSource != this) {
+                    updateItemLocationsInDatabaseBatch();
+                }
+                mIsExternalDrag = false;
+            } else {
+                currentDragView = mCurrentDragView;
+                mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
+            }
+
+            if (d.dragView.hasDrawn()) {
+                // Temporarily reset the scale such that the animation target gets calculated
+                // correctly.
+                float scaleX = getScaleX();
+                float scaleY = getScaleY();
+                setScaleX(1.0f);
+                setScaleY(1.0f);
+                mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView,
+                        cleanUpRunnable, null);
+                setScaleX(scaleX);
+                setScaleY(scaleY);
+            } else {
+                d.deferDragViewCleanupPostAnimation = false;
+                currentDragView.setVisibility(VISIBLE);
+            }
+
+            mItemsInvalidated = true;
+            rearrangeChildren();
+
+            // Temporarily suppress the listener, as we did all the work already here.
+            try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+                mInfo.add(si, false);
+            }
         }
 
         // Clear the drag info, as it is no longer being dragged.
@@ -1419,11 +1435,12 @@
     }
 
     @Override
-    public void onAdd(ShortcutInfo item) {
-        mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem());
+    public void onAdd(ShortcutInfo item, int rank) {
+        View view = mContent.createAndAddViewForRank(item, rank);
+        ArrayList<View> items = new ArrayList<>(getItemsInReadingOrder());
+        items.add(rank, view);
+        mContent.arrangeChildren(items, items.size());
         mItemsInvalidated = true;
-        mLauncher.getModelWriter().addOrMoveItemInDatabase(
-                item, mInfo.id, 0, item.cellX, item.cellY);
     }
 
     public void onRemove(ShortcutInfo item) {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 4548792..d795c56 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -76,6 +76,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -251,11 +252,9 @@
         mBackground.animateToAccept(cl, lp.cellX, lp.cellY);
         mOpenAlarm.setOnAlarmListener(mOnOpenListener);
         if (SPRING_LOADING_ENABLED &&
-                ((dragInfo instanceof AppInfo) || (dragInfo instanceof ShortcutInfo))) {
-            // TODO: we currently don't support spring-loading for PendingAddShortcutInfos even
-            // though widget-style shortcuts can be added to folders. The issue is that we need
-            // to deal with configuration activities which are currently handled in
-            // Workspace#onDropExternal.
+                ((dragInfo instanceof AppInfo)
+                        || (dragInfo instanceof ShortcutInfo)
+                        || (dragInfo instanceof PendingAddShortcutInfo))) {
             mOpenAlarm.setAlarm(ON_OPEN_DELAY);
         }
     }
@@ -1107,7 +1106,7 @@
     }
 
     @Override
-    public void onAdd(ShortcutInfo item) {
+    public void onAdd(ShortcutInfo item, int rank) {
         boolean wasBadged = mBadgeInfo.hasBadge();
         mBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
         boolean isBadged = mBadgeInfo.hasBadge();
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 2a6007a..19a16b1 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -199,21 +199,26 @@
         return extra;
     }
 
+    public void allocateSpaceForRank(int rank) {
+        ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
+        views.add(rank, null);
+        arrangeChildren(views, views.size(), false);
+    }
+
     /**
      * Create space for a new item at the end, and returns the rank for that item.
      * Also sets the current page to the last page.
      */
     public int allocateRankForNewItem() {
         int rank = getItemCount();
-        ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
-        views.add(rank, null);
-        arrangeChildren(views, views.size(), false);
+        allocateSpaceForRank(rank);
         setCurrentPage(rank / mMaxItemsPerPage);
         return rank;
     }
 
     public View createAndAddViewForRank(ShortcutInfo item, int rank) {
         View icon = createNewView(item);
+        allocateSpaceForRank(rank);
         addViewForRank(icon, item, rank);
         return icon;
     }