Changing folders to use CellLayouts

Change-Id: Ic80bece18855a4f5d2476c224ef66f035ac7c97b
diff --git a/res/layout-land/user_folder.xml b/res/layout-land/user_folder.xml
index 5da4aa5..ca437bf 100644
--- a/res/layout-land/user_folder.xml
+++ b/res/layout-land/user_folder.xml
@@ -14,7 +14,9 @@
      limitations under the License.
 -->
 
-<com.android.launcher2.UserFolder xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher2.UserFolder
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
     android:orientation="vertical">
     
     <Button
@@ -29,17 +31,24 @@
 
     <FrameLayout
          android:layout_width="match_parent"
-	   android:layout_height="0dip"
+         android:layout_height="0dip"
          android:layout_weight="1"
          android:background="@drawable/box_launcher_bottom">
 
-        <GridView
+        <com.android.launcher2.CellLayout
              android:id="@id/folder_content"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
 
              android:cacheColorHint="#ff333333"
 
+             launcher:cellWidth="@dimen/folder_cell_width"
+             launcher:cellHeight="@dimen/folder_cell_height"
+             launcher:xAxisStartPadding="0dip"
+             launcher:xAxisEndPadding="0dip"
+             launcher:yAxisStartPadding="0dip"
+             launcher:yAxisEndPadding="0dip"
+
              android:scrollbarAlwaysDrawVerticalTrack="true"
              android:scrollbarStyle="insideInset"
              android:drawSelectorOnTop="false"
diff --git a/res/layout-port/user_folder.xml b/res/layout-port/user_folder.xml
index 0e6df66..c00b548 100644
--- a/res/layout-port/user_folder.xml
+++ b/res/layout-port/user_folder.xml
@@ -14,7 +14,9 @@
      limitations under the License.
 -->
 
-<com.android.launcher2.UserFolder xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher2.UserFolder
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
     android:orientation="vertical">
         
     <Button
@@ -29,16 +31,23 @@
 
     <FrameLayout
          android:layout_width="match_parent"
-	   android:layout_height="0dip"
+         android:layout_height="0dip"
          android:layout_weight="1"
          android:background="@drawable/box_launcher_bottom">
-        <GridView
+        <com.android.launcher2.CellLayout
              android:id="@id/folder_content"
              android:layout_width="match_parent"
-	       android:layout_height="match_parent"
+             android:layout_height="match_parent"
 
              android:cacheColorHint="#ff333333"
 
+             launcher:cellWidth="@dimen/folder_cell_width"
+             launcher:cellHeight="@dimen/folder_cell_height"
+             launcher:xAxisStartPadding="0dip"
+             launcher:xAxisEndPadding="0dip"
+             launcher:yAxisStartPadding="0dip"
+             launcher:yAxisEndPadding="0dip"
+
              android:scrollbarAlwaysDrawVerticalTrack="true"
              android:scrollbarStyle="insideInset"
              android:drawSelectorOnTop="false"
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 159cbb4..9cb549b 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -17,5 +17,7 @@
 <resources>
     <dimen name="workspace_cell_width">106dip</dimen>
     <dimen name="workspace_cell_height">74dip</dimen>
+    <dimen name="folder_cell_width">100dip</dimen>
+    <dimen name="folder_cell_height">74dip</dimen>
     <dimen name="button_bar_height">62dip</dimen>
 </resources>
diff --git a/res/values-port/dimens.xml b/res/values-port/dimens.xml
index 65a3fd3..ad5922d 100644
--- a/res/values-port/dimens.xml
+++ b/res/values-port/dimens.xml
@@ -17,4 +17,6 @@
 <resources>
     <dimen name="workspace_cell_width">80dip</dimen>
     <dimen name="workspace_cell_height">100dip</dimen>
+    <dimen name="folder_cell_width">74dip</dimen>
+    <dimen name="folder_cell_height">86dip</dimen>
 </resources>
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();
     }