Stripping LiveFolders from Launcher

Change-Id: Ic105fbeb1289e43469d5975b7d977b2fc7900c0b
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 7eb3bb1..4c8d03a 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -16,14 +16,25 @@
 
 package com.android.launcher2;
 
+import java.util.ArrayList;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.AdapterView;
 import android.widget.Button;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.AdapterView.OnItemLongClickListener;
 
@@ -33,14 +44,14 @@
  * Represents a set of icons chosen by the user or generated by the system.
  */
 public class Folder extends LinearLayout implements DragSource, OnItemLongClickListener,
-        OnItemClickListener, OnClickListener, View.OnLongClickListener {
+        OnItemClickListener, OnClickListener, View.OnLongClickListener, DropTarget {
 
     protected DragController mDragController;
-    
+
     protected Launcher mLauncher;
 
     protected Button mCloseButton;
-    
+
     protected FolderInfo mInfo;
     
     /**
@@ -48,6 +59,19 @@
      */
     protected ShortcutInfo mDragItem;
 
+    private static final String TAG = "Launcher.Folder";
+
+    static final int STATE_NONE = -1;
+    static final int STATE_SMALL = 0;
+    static final int STATE_ANIMATING = 1;
+    static final int STATE_OPEN = 2;
+
+    private int mExpandDuration;
+    protected CellLayout mContent;
+    private final LayoutInflater mInflater;
+    private final IconCache mIconCache;
+    private int mState = STATE_NONE;
+
     /**
      * Used to inflate the Workspace from XML.
      *
@@ -57,6 +81,9 @@
     public Folder(Context context, AttributeSet attrs) {
         super(context, attrs);
         setAlwaysDrawnWithCacheEnabled(false);
+        mInflater = LayoutInflater.from(context);
+        mIconCache = ((LauncherApplication)context.getApplicationContext()).getIconCache();
+        mExpandDuration = getResources().getInteger(R.integer.config_folderAnimDuration);
     }
 
     @Override
@@ -66,6 +93,7 @@
         mCloseButton = (Button) findViewById(R.id.folder_close);
         mCloseButton.setOnClickListener(this);
         mCloseButton.setOnLongClickListener(this);
+        mContent = (CellLayout) findViewById(R.id.folder_content);
     }
     
     public void onItemClick(AdapterView parent, View v, int position, long id) {
@@ -78,12 +106,38 @@
     }
 
     public void onClick(View v) {
-        mLauncher.closeFolder(this);
+        Object tag = v.getTag();
+        if (tag instanceof ShortcutInfo) {
+            // refactor this code from Folder
+            ShortcutInfo item = (ShortcutInfo) tag;
+            int[] pos = new int[2];
+            v.getLocationOnScreen(pos);
+            item.intent.setSourceBounds(new Rect(pos[0], pos[1],
+                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));
+            mLauncher.startActivitySafely(item.intent, item);
+        } else {
+            mLauncher.closeFolder(this);
+        }
     }
 
     public boolean onLongClick(View v) {
-        mLauncher.closeFolder(this);
-        mLauncher.showRenameDialog(mInfo);
+        Object tag = v.getTag();
+        if (tag instanceof ShortcutInfo) {
+         // refactor this code from Folder
+            ShortcutInfo item = (ShortcutInfo) tag;
+            if (!v.isInTouchMode()) {
+                return false;
+            }
+
+            mLauncher.getWorkspace().onDragStartedWithItem(v);
+            mDragController.startDrag(v, this, item, DragController.DRAG_ACTION_COPY);
+
+            mLauncher.closeFolder(this);
+            mDragItem = item;
+        } else {
+            mLauncher.closeFolder(this);
+            mLauncher.showRenameDialog(mInfo);
+        }
         return true;
     }
 
@@ -105,17 +159,9 @@
         mDragController = dragController;
     }
 
-    @Override
-    public void onDropCompleted(View target, Object dragInfo, boolean success) {
-    }
-
-    @Override
     public void onDragViewVisible() {
     }
 
-    void notifyDataSetChanged() {
-    }
-
     void setLauncher(Launcher launcher) {
         mLauncher = launcher;
     }
@@ -128,6 +174,11 @@
     }
 
     void onOpen() {
+        // When the folder opens, we need to refresh the GridView's selection by
+        // forcing a layout
+        // TODO: find out if this is still necessary
+        mContent.requestLayout();
+        requestFocus();
     }
 
     void onClose() {
@@ -138,5 +189,224 @@
     void bind(FolderInfo info) {
         mInfo = info;
         mCloseButton.setText(info.title);
+        ArrayList<ShortcutInfo> children = info.contents;
+        for (int i = 0; i < children.size(); i++) {
+            ShortcutInfo child = (ShortcutInfo) children.get(i);
+            if ((child.cellX == -1 && child.cellY == -1) ||
+                    mContent.isOccupied(child.cellX, child.cellY)) {
+                findAndSetEmptyCells(child);
+            }
+            createAndAddShortcut((ShortcutInfo) children.get(i));
+        }
+    }
+
+    /**
+     * Creates a new UserFolder, inflated from R.layout.user_folder.
+     *
+     * @param context The application's context.
+     *
+     * @return A new UserFolder.
+     */
+    static Folder fromXml(Context context) {
+        return (Folder) LayoutInflater.from(context).inflate(R.layout.user_folder, null);
+    }
+
+    /**
+     * This method is intended to make the UserFolder to be visually identical in size and position
+     * to its associated FolderIcon. This allows for a seamless transition into the expanded state.
+     */
+    private void positionAndSizeAsIcon() {
+        if (!(getParent() instanceof CellLayoutChildren)) return;
+
+        CellLayoutChildren clc = (CellLayoutChildren) getParent();
+        CellLayout cellLayout = (CellLayout) clc.getParent();
+
+        FolderIcon fi = (FolderIcon) cellLayout.getChildAt(mInfo.cellX, mInfo.cellY);
+        CellLayout.LayoutParams iconLp = (CellLayout.LayoutParams) fi.getLayoutParams();
+        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
+
+        lp.width = iconLp.width;
+        lp.height = iconLp.height;
+        lp.x = iconLp.x;
+        lp.y = iconLp.y;
+
+        mContent.setAlpha(0f);
+        mState = STATE_SMALL;
+    }
+
+    public void animateOpen() {
+        if (mState != STATE_SMALL) {
+            positionAndSizeAsIcon();
+        }
+        if (!(getParent() instanceof CellLayoutChildren)) return;
+
+        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
+
+        CellLayoutChildren clc = (CellLayoutChildren) getParent();
+        CellLayout cellLayout = (CellLayout) clc.getParent();
+        Rect r = cellLayout.getContentRect(null);
+
+        PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", r.width());
+        PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", r.height());
+        PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", 0);
+        PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", 0);
+
+        ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp, width, height, x, y);
+        oa.addUpdateListener(new AnimatorUpdateListener() {
+            public void onAnimationUpdate(ValueAnimator animation) {
+                requestLayout();
+            }
+        });
+
+        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f);
+        ObjectAnimator oaContentAlpha = ObjectAnimator.ofPropertyValuesHolder(mContent, alpha);
+
+        AnimatorSet set = new AnimatorSet();
+        set.playTogether(oa, oaContentAlpha);
+        set.setDuration(mExpandDuration);
+        set.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mState = STATE_ANIMATING;
+            }
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mState = STATE_SMALL;
+            }
+        });
+        set.start();
+    }
+
+    public void animateClosed() {
+        if (!(getParent() instanceof CellLayoutChildren)) return;
+
+        CellLayoutChildren clc = (CellLayoutChildren) getParent();
+        final CellLayout cellLayout = (CellLayout) clc.getParent();
+
+        FolderIcon fi = (FolderIcon) cellLayout.getChildAt(mInfo.cellX, mInfo.cellY);
+        CellLayout.LayoutParams iconLp = (CellLayout.LayoutParams) fi.getLayoutParams();
+        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
+
+        PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", iconLp.width);
+        PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", iconLp.height);
+        PropertyValuesHolder x = PropertyValuesHolder.ofInt("x",iconLp.x);
+        PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", iconLp.y);
+
+        ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp, width, height, x, y);
+        oa.addUpdateListener(new AnimatorUpdateListener() {
+            public void onAnimationUpdate(ValueAnimator animation) {
+                requestLayout();
+            }
+        });
+
+        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
+        ObjectAnimator oaContentAlpha = ObjectAnimator.ofPropertyValuesHolder(mContent, alpha);
+
+        AnimatorSet set = new AnimatorSet();
+        set.playTogether(oa, oaContentAlpha);
+        set.setDuration(mExpandDuration);
+
+        set.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                cellLayout.removeViewWithoutMarkingCells(Folder.this);
+                mState = STATE_OPEN;
+            }
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mState = STATE_ANIMATING;
+            }
+        });
+        set.start();
+    }
+
+    void notifyDataSetChanged() {
+        // recreate all the children if the data set changes under us. We may want to do this more
+        // intelligently (ie just removing the views that should no longer exist)
+        mContent.removeAllViewsInLayout();
+        bind(mInfo);
+    }
+
+    public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+        final ItemInfo item = (ItemInfo) dragInfo;
+        final int itemType = item.itemType;
+        return (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
+                    itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT)
+                && item.container != mInfo.id;
+    }
+
+    public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+        ShortcutInfo item;
+        if (dragInfo instanceof ApplicationInfo) {
+            // Came from all apps -- make a copy
+            item = ((ApplicationInfo)dragInfo).makeShortcut();
+            item.spanX = 1;
+            item.spanY = 1;
+        } else {
+            item = (ShortcutInfo)dragInfo;
+        }
+        findAndSetEmptyCells(item);
+        mInfo.add(item);
+        createAndAddShortcut(item);
+        LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
+    }
+
+    protected boolean findAndSetEmptyCells(ShortcutInfo item) {
+        int[] emptyCell = new int[2];
+        if (mContent.findCellForSpan(emptyCell, item.spanX, item.spanY)) {
+            item.cellX = emptyCell[0];
+            item.cellY = emptyCell[1];
+            LauncherModel.addOrMoveItemInDatabase(
+                    mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    protected void createAndAddShortcut(ShortcutInfo item) {
+        final TextView textView =
+            (TextView) mInflater.inflate(R.layout.application_boxed, this, false);
+        textView.setCompoundDrawablesWithIntrinsicBounds(null,
+                new FastBitmapDrawable(item.getIcon(mIconCache)), null, null);
+        textView.setText(item.title);
+        textView.setTag(item);
+
+        textView.setOnClickListener(this);
+        textView.setOnLongClickListener(this);
+
+        CellLayout.LayoutParams lp =
+            new CellLayout.LayoutParams(item.cellX, item.cellY, item.spanX, item.spanY);
+        boolean insert = false;
+        mContent.addViewToCellLayout(textView, insert ? 0 : -1, (int)item.id, lp, true);
+    }
+
+    public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+    }
+
+    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+    }
+
+    public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+    }
+
+    public void onDropCompleted(View target, Object dragInfo, boolean success) {
+        if (success) {
+            mInfo.remove(mDragItem);
+        }
+    }
+
+    public boolean isDropEnabled() {
+        return true;
+    }
+
+    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+        return null;
     }
 }