Changing folders to use CellLayouts

Change-Id: Ic80bece18855a4f5d2476c224ef66f035ac7c97b
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index ac1a4bd..636b041 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -1520,6 +1520,14 @@
         }
     }
 
+    public boolean isOccupied(int x, int y) {
+        if (x < mCountX && y < mCountY) {
+            return mOccupied[x][y];
+        } else {
+            throw new RuntimeException("Position exceeds the bound of this CellLayout");
+        }
+    }
+
     @Override
     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
         return new CellLayout.LayoutParams(getContext(), attrs);
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 059e73d..33a1cd3 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -37,7 +37,6 @@
 public class Folder extends LinearLayout implements DragSource, OnItemLongClickListener,
         OnItemClickListener, OnClickListener, View.OnLongClickListener {
 
-    protected AbsListView mContent;
     protected DragController mDragController;
     
     protected Launcher mLauncher;
@@ -66,10 +65,6 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mContent = (AbsListView) findViewById(R.id.folder_content);
-        mContent.setOnItemClickListener(this);
-        mContent.setOnItemLongClickListener(this);
-        
         mCloseButton = (Button) findViewById(R.id.folder_close);
         mCloseButton.setOnClickListener(this);
         mCloseButton.setOnLongClickListener(this);
@@ -121,18 +116,7 @@
     public void onDragViewVisible() {
     }
 
-    /**
-     * Sets the adapter used to populate the content area. The adapter must only
-     * contains ShortcutInfo items.
-     *
-     * @param adapter The list of applications to display in the folder.
-     */
-    void setContentAdapter(BaseAdapter adapter) {
-        mContent.setAdapter(adapter);
-    }
-
     void notifyDataSetChanged() {
-        ((BaseAdapter) mContent.getAdapter()).notifyDataSetChanged();
     }
 
     void setLauncher(Launcher launcher) {
@@ -146,10 +130,7 @@
         return mInfo;
     }
 
-    // When the folder opens, we need to refresh the GridView's selection by
-    // forcing a layout
     void onOpen() {
-        mContent.requestLayout();
     }
 
     void onClose() {
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index a64b550..1122d77 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -91,6 +91,8 @@
         } else {
             item = (ShortcutInfo)dragInfo;
         }
+        item.cellX = -1;
+        item.cellY = -1;
         addItem(item);
     }
 
diff --git a/src/com/android/launcher2/LiveFolder.java b/src/com/android/launcher2/LiveFolder.java
index 07a295f..b692953 100644
--- a/src/com/android/launcher2/LiveFolder.java
+++ b/src/com/android/launcher2/LiveFolder.java
@@ -22,10 +22,12 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.AdapterView;
+import android.widget.BaseAdapter;
 import android.net.Uri;
 import android.provider.LiveFolders;
 import android.os.AsyncTask;
 import android.database.Cursor;
+import android.widget.GridView;
 
 import java.lang.ref.WeakReference;
 
@@ -33,6 +35,7 @@
 
 public class LiveFolder extends Folder {
     private AsyncTask<LiveFolderInfo,Void,Cursor> mLoadingTask;
+    protected GridView mContent;
 
     public LiveFolder(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -44,6 +47,15 @@
         return (LiveFolder) LayoutInflater.from(context).inflate(layout, null);
     }
 
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mContent = (GridView) findViewById(R.id.folder_content);
+        mContent.setOnItemClickListener(this);
+        mContent.setOnItemLongClickListener(this);
+    }
+
     private static boolean isDisplayModeList(FolderInfo folderInfo) {
         return ((LiveFolderInfo) folderInfo).displayMode ==
                 LiveFolders.DISPLAY_MODE_LIST;
@@ -68,6 +80,21 @@
         }
     }
 
+    /**
+     * Sets the adapter used to populate the content area. The adapter must only
+     * contains ShortcutInfo items.
+     *
+     * @param adapter The list of applications to display in the folder.
+     */
+    void setContentAdapter(BaseAdapter adapter) {
+        mContent.setAdapter(adapter);
+    }
+
+    @Override
+    void notifyDataSetChanged() {
+        ((BaseAdapter) mContent.getAdapter()).notifyDataSetChanged();
+    }
+
     @Override
     public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
         return false;
@@ -84,6 +111,9 @@
     @Override
     void onOpen() {
         super.onOpen();
+        // When the folder opens, we need to refresh the GridView's selection by
+        // forcing a layout
+        mContent.requestLayout();
         requestFocus();
     }
 
diff --git a/src/com/android/launcher2/UserFolder.java b/src/com/android/launcher2/UserFolder.java
index 251b3f9..b800b07 100644
--- a/src/com/android/launcher2/UserFolder.java
+++ b/src/com/android/launcher2/UserFolder.java
@@ -1,11 +1,18 @@
 package com.android.launcher2;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.GridView;
+import android.widget.TextView;
 
 import com.android.launcher.R;
+import java.util.ArrayList;
 
 /**
  * Folder which contains applications or shortcuts chosen by the user.
@@ -14,8 +21,21 @@
 public class UserFolder extends Folder implements DropTarget {
     private static final String TAG = "Launcher.UserFolder";
 
+    protected CellLayout mContent;
+    private final LayoutInflater mInflater;
+    private final IconCache mIconCache;
+
     public UserFolder(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mInflater = LayoutInflater.from(context);
+        mIconCache = ((LauncherApplication)context.getApplicationContext()).getIconCache();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mContent = (CellLayout) findViewById(R.id.folder_content);
     }
     
     /**
@@ -29,6 +49,48 @@
         return (UserFolder) LayoutInflater.from(context).inflate(R.layout.user_folder, null);
     }
 
+    @Override
+    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 void onClick(View v) {
+        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 {
+            super.onClick(v);
+        }
+    }
+
+    public boolean onLongClick(View v) {
+        Object tag = v.getTag();
+        if (tag instanceof ShortcutInfo) {
+         // refactor this code from Folder
+            ShortcutInfo item = (ShortcutInfo) tag;
+            if (!v.isInTouchMode()) {
+                return false;
+            }
+
+            mDragController.startDrag(v, this, item, DragController.DRAG_ACTION_COPY);
+            mLauncher.closeFolder(this);
+            mDragItem = item;
+
+            return true;
+        } else {
+            return super.onLongClick(v);
+        }
+    }
+
     public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
         final ItemInfo item = (ItemInfo) dragInfo;
@@ -44,11 +106,45 @@
         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;
         }
-        ((ShortcutsAdapter)mContent.getAdapter()).add(item);
-        LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, 0, 0);
+        findAndSetEmptyCells(item);
+        ((UserFolderInfo)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,
@@ -66,8 +162,7 @@
     @Override
     public void onDropCompleted(View target, Object dragInfo, boolean success) {
         if (success) {
-            ShortcutsAdapter adapter = (ShortcutsAdapter)mContent.getAdapter();
-            adapter.remove(mDragItem);
+            ((UserFolderInfo)mInfo).remove(mDragItem);
         }
     }
 
@@ -77,14 +172,24 @@
 
     void bind(FolderInfo info) {
         super.bind(info);
-        setContentAdapter(new ShortcutsAdapter(mContext, ((UserFolderInfo) info).contents));
+        ArrayList<ShortcutInfo> children = ((UserFolderInfo)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));
+        }
     }
 
-    // When the folder opens, we need to refresh the GridView's selection by
-    // forcing a layout
     @Override
     void onOpen() {
         super.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();
     }