Adding animation when destroying folder due to single item remaining

Change-Id: I079af9a50d7d6f63092b725ecd941ab1e298032d
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 1163f9e..4669918 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -110,6 +110,8 @@
     private static String sHintText;
     private ObjectAnimator mOpenCloseAnimator;
 
+    private boolean mDestroyed;
+
     /**
      * Used to inflate the Workspace from XML.
      *
@@ -943,34 +945,45 @@
     }
 
     private void replaceFolderWithFinalItem() {
-        ItemInfo finalItem = null;
-
-        if (getItemCount() == 1) {
-            finalItem = mInfo.contents.get(0);
-        }
-
-        // Remove the folder completely
-        CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container, mInfo.screen);
-        cellLayout.removeView(mFolderIcon);
-        if (mFolderIcon instanceof DropTarget) {
-            mDragController.removeDropTarget((DropTarget) mFolderIcon);
-        }
-        mLauncher.removeFolder(mInfo);
-
-        if (finalItem != null) {
-            LauncherModel.addOrMoveItemInDatabase(mLauncher, finalItem, mInfo.container,
-                    mInfo.screen, mInfo.cellX, mInfo.cellY);
-        }
-        LauncherModel.deleteItemFromDatabase(mLauncher, mInfo);
-
         // Add the last remaining child to the workspace in place of the folder
-        if (finalItem != null) {
-            View child = mLauncher.createShortcut(R.layout.application, cellLayout,
-                    (ShortcutInfo) finalItem);
+        Runnable onCompleteRunnable = new Runnable() {
+            @Override
+            public void run() {
+                CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container, mInfo.screen);
 
-            mLauncher.getWorkspace().addInScreen(child, mInfo.container, mInfo.screen, mInfo.cellX,
-                    mInfo.cellY, mInfo.spanX, mInfo.spanY);
+                if (getItemCount() <= 1) {
+                    // Remove the folder
+                    LauncherModel.deleteItemFromDatabase(mLauncher, mInfo);
+                    cellLayout.removeView(mFolderIcon);
+                    if (mFolderIcon instanceof DropTarget) {
+                        mDragController.removeDropTarget((DropTarget) mFolderIcon);
+                    }
+                    mLauncher.removeFolder(mInfo);
+                }
+
+                // Move the item from the folder to the workspace, in the position of the folder
+                if (getItemCount() == 1) {
+                    ShortcutInfo finalItem = mInfo.contents.get(0);
+
+                    final View child = mLauncher.createShortcut(R.layout.application, cellLayout,
+                            finalItem);
+                    LauncherModel.addOrMoveItemInDatabase(mLauncher, finalItem, mInfo.container,
+                            mInfo.screen, mInfo.cellX, mInfo.cellY);
+                    mLauncher.getWorkspace().addInScreen(child, mInfo.container, mInfo.screen,
+                            mInfo.cellX, mInfo.cellY, mInfo.spanX, mInfo.spanY);
+                }
+
+            }
+        };
+        View finalChild = getItemAt(0);
+        if (finalChild != null) {
+            mFolderIcon.performDestroyAnimation(finalChild, onCompleteRunnable);
         }
+        mDestroyed = true;
+    }
+
+    boolean isDestroyed() {
+        return mDestroyed;
     }
 
     // This method keeps track of the last item in the folder for the purposes
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index 4919b57..76f3114 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -50,8 +50,8 @@
  */
 public class FolderIcon extends LinearLayout implements FolderListener {
     private Launcher mLauncher;
-    Folder mFolder;
-    FolderInfo mInfo;
+    private Folder mFolder;
+    private FolderInfo mInfo;
     private static boolean sStaticValuesDirty = true;
 
     private CheckLongPressHelper mLongPressHelper;
@@ -61,6 +61,7 @@
     private static final int CONSUMPTION_ANIMATION_DURATION = 100;
     private static final int DROP_IN_ANIMATION_DURATION = 400;
     private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
+    private static final int FINAL_ITEM_ANIMATION_DURATION = 200;
 
     // The degree to which the inner ring grows when accepting drop
     private static final float INNER_RING_GROWTH_FACTOR = 0.15f;
@@ -93,8 +94,10 @@
     private int mPreviewOffsetY;
     private float mMaxPerspectiveShift;
     boolean mAnimating = false;
+
     private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0);
     private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+    private ArrayList<ShortcutInfo> mHiddenItems = new ArrayList<ShortcutInfo>();
 
     public FolderIcon(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -278,6 +281,14 @@
         }
     }
 
+    Folder getFolder() {
+        return mFolder;
+    }
+
+    FolderInfo getFolderInfo() {
+        return mInfo;
+    }
+
     private boolean willAcceptItem(ItemInfo item) {
         final int itemType = item.itemType;
         return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
@@ -287,7 +298,7 @@
 
     public boolean acceptDrop(Object dragInfo) {
         final ItemInfo item = (ItemInfo) dragInfo;
-        return willAcceptItem(item);
+        return !mFolder.isDestroyed() && willAcceptItem(item);
     }
 
     public void addItem(ShortcutInfo item) {
@@ -296,7 +307,7 @@
     }
 
     public void onDragEnter(Object dragInfo) {
-        if (!willAcceptItem((ItemInfo) dragInfo)) return;
+        if (mFolder.isDestroyed() || !willAcceptItem((ItemInfo) dragInfo)) return;
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
         CellLayout layout = (CellLayout) getParent().getParent();
         mFolderRingAnimator.setCell(lp.cellX, lp.cellY);
@@ -312,16 +323,29 @@
             final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
             float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
 
+        // These correspond two the drawable and view that the icon was dropped _onto_
         Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
-        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), destView.getMeasuredWidth());
-
-        // This will animate the dragView (srcView) into the new folder
-        onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, null);
+        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
+                destView.getMeasuredWidth());
 
         // This will animate the first item from it's position as an icon into its
         // position as the first item in the preview
-        animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION);
+        animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION, false, null);
         addItem(destInfo);
+
+        // This will animate the dragView (srcView) into the new folder
+        onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, null);
+    }
+
+    public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
+        Drawable animateDrawable = ((TextView) finalView).getCompoundDrawables()[1];
+        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), 
+                finalView.getMeasuredWidth());
+
+        // This will animate the first item from it's position as an icon into its
+        // position as the first item in the preview
+        animateFirstItem(animateDrawable, FINAL_ITEM_ANIMATION_DURATION, true,
+                onCompleteRunnable);
     }
 
     public void onDragExit(Object dragInfo) {
@@ -377,9 +401,12 @@
                     1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
                     new DecelerateInterpolator(2), new AccelerateInterpolator(2),
                     postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
+            addItem(item);
+            mHiddenItems.add(item);
             postDelayed(new Runnable() {
                 public void run() {
-                    addItem(item);
+                    mHiddenItems.remove(item);
+                    invalidate();
                 }
             }, DROP_IN_ANIMATION_DURATION);
         } else {
@@ -526,19 +553,20 @@
         if (!mAnimating) {
             for (int i = nItemsInPreview - 1; i >= 0; i--) {
                 v = (TextView) items.get(i);
-                d = v.getCompoundDrawables()[1];
-
-                mParams = computePreviewItemDrawingParams(i, mParams);
-                mParams.drawable = d;
-                drawPreviewItem(canvas, mParams);
+                if (!mHiddenItems.contains(v.getTag())) {
+                    d = v.getCompoundDrawables()[1];
+                    mParams = computePreviewItemDrawingParams(i, mParams);
+                    mParams.drawable = d;
+                    drawPreviewItem(canvas, mParams);
+                }
             }
         } else {
             drawPreviewItem(canvas, mAnimParams);
         }
     }
 
-    private void animateFirstItem(final Drawable d, int duration) {
-        computePreviewDrawingParams(d);
+    private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
+            final Runnable onCompleteRunnable) {
         final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null);
 
         final float scale0 = 1.0f;
@@ -550,6 +578,10 @@
         va.addUpdateListener(new AnimatorUpdateListener(){
             public void onAnimationUpdate(ValueAnimator animation) {
                 float progress = (Float) animation.getAnimatedValue();
+                if (reverse) {
+                    progress = 1 - progress;
+                    mPreviewBackground.setAlpha(progress);
+                }
 
                 mAnimParams.transX = transX0 + progress * (finalParams.transX - transX0);
                 mAnimParams.transY = transY0 + progress * (finalParams.transY - transY0);
@@ -565,6 +597,9 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mAnimating = false;
+                if (onCompleteRunnable != null) {
+                    onCompleteRunnable.run();
+                }
             }
         });
         va.setDuration(duration);
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 28dfd65..5ef7317 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -1989,7 +1989,7 @@
     }
 
     private void handleFolderClick(FolderIcon folderIcon) {
-        final FolderInfo info = folderIcon.mInfo;
+        final FolderInfo info = folderIcon.getFolderInfo();
         Folder openFolder = mWorkspace.getFolderForTag(info);
 
         // If the folder info reports that the associated folder is open, then verify that
@@ -2000,7 +2000,7 @@
             info.opened = false;
         }
 
-        if (!info.opened) {
+        if (!info.opened && !folderIcon.getFolder().isDestroyed()) {
             // Close any open folder
             closeFolder();
             // Open the requested folder
@@ -2057,9 +2057,9 @@
         mFolderIconCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
         fi.draw(mFolderIconCanvas);
         mFolderIconImageView.setImageBitmap(mFolderIconBitmap);
-        if (fi.mFolder != null) {
-            mFolderIconImageView.setPivotX(fi.mFolder.getPivotXForIconAnimation());
-            mFolderIconImageView.setPivotY(fi.mFolder.getPivotYForIconAnimation());
+        if (fi.getFolder() != null) {
+            mFolderIconImageView.setPivotX(fi.getFolder().getPivotXForIconAnimation());
+            mFolderIconImageView.setPivotY(fi.getFolder().getPivotYForIconAnimation());
         }
         // Just in case this image view is still in the drag layer from a previous animation,
         // we remove it and re-add it.
@@ -2067,8 +2067,8 @@
             mDragLayer.removeView(mFolderIconImageView);
         }
         mDragLayer.addView(mFolderIconImageView, lp);
-        if (fi.mFolder != null) {
-            fi.mFolder.bringToFront();
+        if (fi.getFolder() != null) {
+            fi.getFolder().bringToFront();
         }
     }
 
@@ -2131,7 +2131,7 @@
      * @param folderInfo The FolderInfo describing the folder to open.
      */
     public void openFolder(FolderIcon folderIcon) {
-        Folder folder = folderIcon.mFolder;
+        Folder folder = folderIcon.getFolder();
         FolderInfo info = folder.mInfo;
 
         info.opened = true;