auto import from //branches/cupcake/...@130745
diff --git a/src/com/android/launcher/AddAdapter.java b/src/com/android/launcher/AddAdapter.java
index 8eebe39..1410708 100644
--- a/src/com/android/launcher/AddAdapter.java
+++ b/src/com/android/launcher/AddAdapter.java
@@ -16,456 +16,111 @@
 
 package com.android.launcher;
 
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.BaseAdapter;
 import android.widget.TextView;
-import android.widget.BaseExpandableListAdapter;
-import android.graphics.drawable.Drawable;
-import android.provider.LiveFolders;
 
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
 
 /**
- * Shows a list of all the items that can be added to the workspace.
+ * Adapter showing the types of items that can be added to a {@link Workspace}.
  */
-public final class AddAdapter extends BaseExpandableListAdapter {
-    private static final int GROUP_APPLICATIONS = 0;
-    private static final int GROUP_SHORTCUTS = 1;
-    private static final int GROUP_WIDGETS = 2;
-    private static final int GROUP_LIVE_FOLDERS = 3;
-    private static final int GROUP_WALLPAPERS = 4;
-
-    private final Intent mCreateShortcutIntent;
-    private final Intent mCreateLiveFolderIntent;
-    private Intent mSetWallpaperIntent;
+public class AddAdapter extends BaseAdapter {
+    
+    private final Launcher mLauncher;
     private final LayoutInflater mInflater;
-    private Launcher mLauncher;
-    private Group[] mGroups;
-
-    /**
-     * Abstract class representing one thing that can be added
-     */
-    public abstract class AddAction implements Runnable {
-        protected final Context mContext;
-
-        AddAction(Context context) {
-            mContext = context;
-        }
-
-        Drawable getIcon(int resource) {
-            return mContext.getResources().getDrawable(resource);
-        }
-
-        public abstract void bindView(View v);
-    }
-
-    /**
-     * Class representing an action that will create set the wallpaper.
-     */
-    public class SetWallpaperAction extends CreateShortcutAction {
-        SetWallpaperAction(Context context, ResolveInfo info) {
-            super(context, info);
-        }
-
-        public void run() {
-            Intent intent = new Intent(mSetWallpaperIntent);
-            ActivityInfo activityInfo = mInfo.activityInfo;
-            intent.setComponent(new ComponentName(activityInfo.applicationInfo.packageName,
-                    activityInfo.name));
-            mLauncher.startActivity(intent);
-        }
-    }
+    
+    private final ArrayList<ListItem> mItems = new ArrayList<ListItem>();
+    
+    public static final int ITEM_APPLICATION = 0;
+    public static final int ITEM_SHORTCUT = 1;
+    public static final int ITEM_SEARCH = 2;
+    public static final int ITEM_GADGET = 3;
+    public static final int ITEM_LIVE_FOLDER = 4;
+    public static final int ITEM_FOLDER = 5;
+    public static final int ITEM_WALLPAPER = 6;
     
     /**
-     * Class representing an action that will create a specific type
-     * of shortcut
+     * Specific item in our list.
      */
-    public class CreateShortcutAction extends AddAction {
+    public class ListItem {
+        public final CharSequence text;
+        public final Drawable image;
+        public final int actionTag;
         
-        ResolveInfo mInfo;
-        private CharSequence mLabel;
-        private Drawable mIcon;
-
-        CreateShortcutAction(Context context, ResolveInfo info) {
-            super(context);
-            mInfo = info;
-        }
-
-        @Override
-        public void bindView(View view) {
-            ResolveInfo info = mInfo;
-            TextView text = (TextView) view;
-
-            PackageManager pm = mLauncher.getPackageManager();
-
-            if (mLabel == null) {
-                mLabel = info.loadLabel(pm);
-                if (mLabel == null) {
-                    mLabel = info.activityInfo.name;
-                }
+        public ListItem(Resources res, int textResourceId, int imageResourceId, int actionTag) {
+            text = res.getString(textResourceId);
+            if (imageResourceId != -1) {
+                image = res.getDrawable(imageResourceId);
+            } else {
+                image = null;
             }
-
-            if (mIcon == null) {
-                mIcon = Utilities.createIconThumbnail(info.loadIcon(pm), mContext);
-            }
-
-            text.setText(mLabel);
-            text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
-        }
-
-        public void run() {
-            Intent intent = new Intent(mCreateShortcutIntent);
-            ActivityInfo activityInfo = mInfo.activityInfo;
-            intent.setComponent(new ComponentName(activityInfo.applicationInfo.packageName,
-                    activityInfo.name));
-            mLauncher.addShortcut(intent);
-        }
-    }
-
-    /**
-     * Class representing an action that will create a specific type
-     * of live folder
-     */
-    public class CreateLiveFolderAction extends CreateShortcutAction {
-        CreateLiveFolderAction(Context context, ResolveInfo info) {
-            super(context, info);
-        }
-
-        @Override
-        public void run() {
-            Intent intent = new Intent(mCreateLiveFolderIntent);
-            ActivityInfo activityInfo = mInfo.activityInfo;
-            intent.setComponent(new ComponentName(activityInfo.applicationInfo.packageName,
-                    activityInfo.name));
-            mLauncher.addLiveFolder(intent);
-        }
-    }
-
-    /**
-     * Class representing an action that will add a folder
-     */
-    public class CreateFolderAction extends AddAction {
-        private Drawable mIcon;
-
-        CreateFolderAction(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void bindView(View view) {
-            TextView text = (TextView) view;
-            text.setText(R.string.add_folder);
-            if (mIcon == null) mIcon = getIcon(R.drawable.ic_launcher_folder);
-            text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
-        }
-
-        public void run() {
-            mLauncher.addFolder();
-        }
-    }
-
-    /**
-     * Class representing an action that will add a folder
-     */
-    public class CreateClockAction extends AddAction {
-
-        CreateClockAction(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void bindView(View view) {
-            TextView text = (TextView) view;
-            text.setText(R.string.add_clock);
-            Drawable icon = getIcon(R.drawable.ic_launcher_alarmclock);
-            text.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
-        }
-
-        public void run() {
-            mLauncher.addClock();
-        }
-    }
-
-    /**
-     * Class representing an action that will add a PhotoFrame
-     */
-    public class CreatePhotoFrameAction extends AddAction {
-        private Drawable mIcon;
-
-        CreatePhotoFrameAction(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void bindView(View view) {
-            TextView text = (TextView) view;
-            text.setText(R.string.add_photo_frame);
-            if (mIcon == null) mIcon = getIcon(R.drawable.ic_launcher_gallery);
-            text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
-        }
-
-        public void run() {
-            mLauncher.getPhotoForPhotoFrame();
-        }
-    }
-
-
-    /**
-     * Class representing an action that will add a Search widget
-     */
-    public class CreateSearchAction extends AddAction {
-        private Drawable mIcon;
-
-        CreateSearchAction(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void bindView(View view) {
-            TextView text = (TextView) view;
-            text.setText(R.string.add_search);
-            if (mIcon == null) mIcon = getIcon(R.drawable.ic_search_gadget);
-            text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
-        }
-
-        public void run() {
-            mLauncher.addSearch();
+            this.actionTag = actionTag;
         }
     }
     
-    private class Group {
-        private String mName;
-        private ArrayList<AddAction> mList;
-
-        Group(String name) {
-            mName = name;
-            mList = new ArrayList<AddAction>();
-        }
-
-        void add(AddAction action) {
-            mList.add(action);
-        }
-
-        int size() {
-            return mList.size();
-        }
-
-        String getName() {
-            return mName;
-        }
-
-        void run(int position) {
-            mList.get(position).run();
-        }
-
-        void bindView(int childPosition, View view) {
-            mList.get(childPosition).bindView(view);
-        }
-
-        public Object get(int childPosition) {
-            return mList.get(childPosition);
-        }
-    }
-
-    private class ApplicationsGroup extends Group {
-        private final Launcher mLauncher;
-        private final ArrayList<ApplicationInfo> mApplications;
-
-        ApplicationsGroup(Launcher launcher, String name) {
-            super(name);
-            mLauncher = launcher;
-            mApplications = Launcher.getModel().getApplications();
-        }
-
-        @Override
-        int size() {
-            return mApplications == null ? 0 : mApplications.size();
-        }
-
-        @Override
-        void add(AddAction action) {
-        }
-
-        @Override
-        void run(int position) {
-            final ApplicationInfo info = mApplications.get(position);
-            mLauncher.addApplicationShortcut(info);
-        }
-
-        @Override
-        void bindView(int childPosition, View view) {
-            TextView text = (TextView) view.findViewById(R.id.title);
-
-            final ApplicationInfo info = mApplications.get(childPosition);
-            text.setText(info.title);
-            if (!info.filtered) {
-                info.icon = Utilities.createIconThumbnail(info.icon, mLauncher);
-                info.filtered = true;
-            }
-            text.setCompoundDrawablesWithIntrinsicBounds(info.icon, null, null, null);
-        }
-
-        @Override
-        public Object get(int childPosition) {
-            return mApplications.get(childPosition);
-        }
-    }
-
-    public AddAdapter(Launcher launcher, boolean forFolder) {
-        mCreateShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
-        mCreateShortcutIntent.setComponent(null);
-
-        mCreateLiveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
-        mCreateLiveFolderIntent.setComponent(null);
-
-        mSetWallpaperIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
-        mSetWallpaperIntent.setComponent(null);
-
+    public AddAdapter(Launcher launcher) {
+        super();
+        
         mLauncher = launcher;
-        mInflater = (LayoutInflater) launcher.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        mInflater = (LayoutInflater) mLauncher.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        
+        // Create default actions
+        Resources res = launcher.getResources();
+        
+        mItems.add(new ListItem(res, R.string.group_applications,
+                R.drawable.ic_launcher_application, ITEM_APPLICATION));
+        
+        mItems.add(new ListItem(res, R.string.group_shortcuts,
+                R.drawable.ic_launcher_empty, ITEM_SHORTCUT));
+        
+        mItems.add(new ListItem(res, R.string.group_search,
+                R.drawable.ic_search_gadget, ITEM_SEARCH));
+        
+        mItems.add(new ListItem(res, R.string.group_gadgets,
+                R.drawable.ic_launcher_gadget, ITEM_GADGET));
+        
+        mItems.add(new ListItem(res, R.string.group_live_folders,
+                R.drawable.ic_launcher_empty, ITEM_LIVE_FOLDER));
+        
+        mItems.add(new ListItem(res, R.string.group_folder,
+                R.drawable.ic_launcher_folder, ITEM_FOLDER));
+        
+        mItems.add(new ListItem(res, R.string.group_wallpapers,
+                R.drawable.ic_launcher_gallery, ITEM_WALLPAPER));
 
-        mGroups = new Group[forFolder ? 2 : 5];
-        final Group[] groups = mGroups;
-        groups[GROUP_APPLICATIONS] = new ApplicationsGroup(mLauncher,
-                mLauncher.getString(R.string.group_applications));
-        groups[GROUP_SHORTCUTS] = new Group(mLauncher.getString(R.string.group_shortcuts));
-        groups[GROUP_LIVE_FOLDERS] = new Group(mLauncher.getString(R.string.group_live_folders));
+    }
 
-        if (!forFolder) {
-            groups[GROUP_WALLPAPERS] = new Group(mLauncher.getString(R.string.group_wallpapers));
-            groups[GROUP_SHORTCUTS].add(new CreateFolderAction(launcher));
-            groups[GROUP_WIDGETS] = new Group(mLauncher.getString(R.string.group_widgets));
-
-            final Group widgets = groups[GROUP_WIDGETS];
-            widgets.add(new CreateClockAction(launcher));
-            widgets.add(new CreatePhotoFrameAction(launcher));
-            widgets.add(new CreateSearchAction(launcher));
+    public View getView(int position, View convertView, ViewGroup parent) {
+        ListItem item = (ListItem) getItem(position);
+        
+        if (convertView == null) {
+            convertView = mInflater.inflate(R.layout.add_list_item, parent, false);
         }
         
-        PackageManager packageManager = launcher.getPackageManager();
-
-        List<ResolveInfo> list = findTargetsForIntent(mCreateShortcutIntent, packageManager);
-        if (list != null && list.size() > 0) {
-            int count = list.size();
-            final Group shortcuts = groups[GROUP_SHORTCUTS];
-            for (int i = 0; i < count; i++) {
-                ResolveInfo resolveInfo = list.get(i);
-                shortcuts.add(new CreateShortcutAction(launcher, resolveInfo));
-            }
-        }
-
-        list = findTargetsForIntent(mCreateLiveFolderIntent, packageManager);
-        if (list != null && list.size() > 0) {
-            int count = list.size();
-            final Group shortcuts = groups[GROUP_LIVE_FOLDERS];
-            for (int i = 0; i < count; i++) {
-                ResolveInfo resolveInfo = list.get(i);
-                shortcuts.add(new CreateLiveFolderAction(launcher, resolveInfo));
-            }
-        }
-
-        list = findTargetsForIntent(mSetWallpaperIntent, packageManager);
-        if (list != null && list.size() > 0) {
-            int count = list.size();
-            final Group shortcuts = groups[GROUP_WALLPAPERS];
-            for (int i = 0; i < count; i++) {
-                ResolveInfo resolveInfo = list.get(i);
-                shortcuts.add(new SetWallpaperAction(launcher, resolveInfo));
-            }
-        }
+        TextView textView = (TextView) convertView;
+        textView.setTag(item);
+        textView.setText(item.text);
+        textView.setCompoundDrawablesWithIntrinsicBounds(item.image, null, null, null);
+        
+        return convertView;
     }
 
-    private List<ResolveInfo> findTargetsForIntent(Intent intent, PackageManager packageManager) {
-        List<ResolveInfo> list = packageManager.queryIntentActivities(intent,
-                PackageManager.MATCH_DEFAULT_ONLY);
-        if (list != null) {
-            int count = list.size();
-            if (count > 1) {
-                // Only display the first matches that are either of equal
-                // priority or have asked to be default options.
-                ResolveInfo firstInfo = list.get(0);
-                for (int i=1; i<count; i++) {
-                    ResolveInfo resolveInfo = list.get(i);
-                    if (firstInfo.priority != resolveInfo.priority ||
-                        firstInfo.isDefault != resolveInfo.isDefault) {
-                        while (i < count) {
-                            list.remove(i);
-                            count--;
-                        }
-                    }
-                }
-                Collections.sort(list, new ResolveInfo.DisplayNameComparator(packageManager));
-            }
-        }
-        return list;
+    public int getCount() {
+        return mItems.size();
     }
 
-    public int getGroupCount() {
-        return mGroups.length;
+    public Object getItem(int position) {
+        return mItems.get(position);
     }
 
-    public int getChildrenCount(int groupPosition) {
-        return mGroups[groupPosition].size();
+    public long getItemId(int position) {
+        return position;
     }
-
-    public Object getGroup(int groupPosition) {
-        return mGroups[groupPosition].getName();
-    }
-
-    public Object getChild(int groupPosition, int childPosition) {
-        return mGroups[groupPosition].get(childPosition);
-    }
-
-    public long getGroupId(int groupPosition) {
-        return groupPosition;
-    }
-
-    public long getChildId(int groupPosition, int childPosition) {
-        return (groupPosition << 16) | childPosition;
-    }
-
-    public boolean hasStableIds() {
-        return true;
-    }
-
-    public View getGroupView(int groupPosition, boolean isExpanded,
-            View convertView, ViewGroup parent) {
-        View view;
-        if (convertView == null) {
-            view = mInflater.inflate(R.layout.create_shortcut_group_item, parent, false);
-        } else {
-            view = convertView;
-        }
-        ((TextView) view).setText(mGroups[groupPosition].getName());
-        return view;
-    }
-
-    public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
-            View convertView, ViewGroup parent) {
-        View view;
-        if (convertView == null) {
-            view = mInflater.inflate(R.layout.create_shortcut_list_item, parent, false);
-        } else {
-            view = convertView;
-        }
-        mGroups[groupPosition].bindView(childPosition, view);
-        return view;
-    }
-
-    public boolean isChildSelectable(int groupPosition, int childPosition) {
-        return true;
-    }
-
-    void performAction(int groupPosition, int childPosition) {
-        mGroups[groupPosition].run(childPosition);
-    }
+    
 }
diff --git a/src/com/android/launcher/BubbleTextView.java b/src/com/android/launcher/BubbleTextView.java
index f2c31e9..3782454 100644
--- a/src/com/android/launcher/BubbleTextView.java
+++ b/src/com/android/launcher/BubbleTextView.java
@@ -40,6 +40,9 @@
 
     private boolean mBackgroundSizeChanged;
     private Drawable mBackground;
+    private float mCornerRadius;
+    private float mPaddingH;
+    private float mPaddingV;
 
     public BubbleTextView(Context context) {
         super(context);
@@ -64,6 +67,12 @@
 
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mPaint.setColor(getContext().getResources().getColor(R.color.bubble_dark_background));
+
+        final float scale = getContext().getResources().getDisplayMetrics().density;
+        mCornerRadius = CORNER_RADIUS * scale;
+        mPaddingH = PADDING_H * scale;
+        //noinspection PointlessArithmeticExpression
+        mPaddingV = PADDING_V * scale;
     }
 
     @Override
@@ -114,11 +123,11 @@
         final int left = getCompoundPaddingLeft();
         final int top = getExtendedPaddingTop();
 
-        rect.set(left + layout.getLineLeft(0) - PADDING_H,
-                top + layout.getLineTop(0) - PADDING_V,
-                Math.min(left + layout.getLineRight(0) + PADDING_H, mScrollX + mRight - mLeft),
-                top + layout.getLineBottom(0) + PADDING_V);
-        canvas.drawRoundRect(rect, CORNER_RADIUS, CORNER_RADIUS, mPaint);
+        rect.set(left + layout.getLineLeft(0) - mPaddingH,
+                top + layout.getLineTop(0) -  mPaddingV,
+                Math.min(left + layout.getLineRight(0) + mPaddingH, mScrollX + mRight - mLeft),
+                top + layout.getLineBottom(0) + mPaddingV);
+        canvas.drawRoundRect(rect, mCornerRadius, mCornerRadius, mPaint);
 
         super.draw(canvas);
     }
diff --git a/src/com/android/launcher/CellLayout.java b/src/com/android/launcher/CellLayout.java
index 02646bf..3e5a74a 100644
--- a/src/com/android/launcher/CellLayout.java
+++ b/src/com/android/launcher/CellLayout.java
@@ -56,6 +56,8 @@
 
     private RectF mDragRect = new RectF();
 
+    private boolean mDirtyTag;
+
     public CellLayout(Context context) {
         this(context, null);
     }
@@ -157,6 +159,7 @@
                         cellInfo.spanY = lp.cellVSpan;
                         cellInfo.valid = true;
                         found = true;
+                        mDirtyTag = false;
                         break;
                     }
                 }
@@ -181,10 +184,12 @@
                 cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
                         cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
 
-                if (cellInfo.valid) {
-                    findIntersectingVacantCells(cellInfo, cellXY[0], cellXY[1],
-                            xCount, yCount, occupied);
-                }
+                // Instead of finding the interesting vacant cells here, wait until a
+                // caller invokes getTag() to retrieve the result. Finding the vacant
+                // cells is a bit expensive and can generate many new objects, it's
+                // therefore better to defer it until we know we actually need it.
+
+                mDirtyTag = true;
             }
             setTag(cellInfo);
         } else if (action == MotionEvent.ACTION_UP) {
@@ -194,12 +199,31 @@
             cellInfo.spanX = 0;
             cellInfo.spanY = 0;
             cellInfo.valid = false;
+            mDirtyTag = false;
             setTag(cellInfo);
         }
 
         return false;
     }
 
+    @Override
+    public CellInfo getTag() {
+        final CellInfo info = (CellInfo) super.getTag();
+        if (mDirtyTag && info.valid) {
+            final boolean portrait = mPortrait;
+            final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
+            final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
+
+            final boolean[][] occupied = mOccupied;
+            findOccupiedCells(xCount, yCount, occupied);
+
+            findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
+
+            mDirtyTag = false;
+        }
+        return info;
+    }
+
     private static void findIntersectingVacantCells(CellInfo cellInfo, int x, int y,
             int xCount, int yCount, boolean[][] occupied) {
 
@@ -207,14 +231,15 @@
         cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
         cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
         cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
-        cellInfo.vacantCells = new ArrayList<CellInfo.VacantCell>();
+        cellInfo.clearVacantCells();
 
         if (occupied[x][y]) {
             return;
         }
 
-        Rect current = new Rect(x, y, x, y);
-        findVacantCell(current, xCount, yCount, occupied, cellInfo);
+        cellInfo.current.set(x, y, x, y);
+
+        findVacantCell(cellInfo.current, xCount, yCount, occupied, cellInfo);
     }
 
     private static void findVacantCell(Rect current, int xCount, int yCount, boolean[][] occupied,
@@ -256,7 +281,7 @@
     }
 
     private static void addVacantCell(Rect current, CellInfo cellInfo) {
-        CellInfo.VacantCell cell = new CellInfo.VacantCell();
+        CellInfo.VacantCell cell = CellInfo.VacantCell.acquire();
         cell.cellX = current.left;
         cell.cellY = current.top;
         cell.spanX = current.right - current.left + 1;
@@ -317,10 +342,9 @@
         cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
         cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
         cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
-        cellInfo.vacantCells = new ArrayList<CellInfo.VacantCell>();
         cellInfo.screen = mCellInfo.screen;
 
-        Rect current = new Rect();
+        Rect current = cellInfo.current;
 
         for (int x = 0; x < xCount; x++) {
             for (int y = 0; y < yCount; y++) {
@@ -634,6 +658,26 @@
         
         dragRect.set(x, y, x + width, y + height);
     }
+    
+    /**
+     * Computes the required horizontal and vertical cell spans to always 
+     * fit the given rectangle.
+     *  
+     * @param width Width in pixels
+     * @param height Height in pixels
+     * @param cellInfo {@link CellInfo} to fill with calculated span parameters
+     */
+    public void rectToCell(int width, int height, CellInfo cellInfo) {
+        // Always assume we're working with the smallest span to make sure we
+        // reserve enough space in both orientations.
+        int actualWidth = mCellWidth + mWidthGap;
+        int actualHeight = mCellHeight + mHeightGap;
+        int smallerSize = Math.min(actualWidth, actualHeight);
+        
+        // Always round up to next largest cell
+        cellInfo.spanX = (width + smallerSize) / smallerSize;
+        cellInfo.spanY = (height + smallerSize) / smallerSize;
+    }
 
     /**
      * Find the first vacant cell, if there is one.
@@ -811,12 +855,54 @@
     }
 
     static final class CellInfo implements ContextMenu.ContextMenuInfo {
+        /**
+         * See View.AttachInfo.InvalidateInfo for futher explanations about
+         * the recycling mechanism. In this case, we recycle the vacant cells
+         * instances because up to several hundreds can be instanciated when
+         * the user long presses an empty cell.
+         */
         static final class VacantCell {
             int cellX;
             int cellY;
             int spanX;
             int spanY;
 
+            // We can create up to 523 vacant cells on a 4x4 grid, 100 seems
+            // like a reasonable compromise given the size of a VacantCell and
+            // the fact that the user is not likely to touch an empty 4x4 grid
+            // very often 
+            private static final int POOL_LIMIT = 100;
+            private static final Object sLock = new Object();
+
+            private static int sAcquiredCount = 0;
+            private static VacantCell sRoot;
+
+            private VacantCell next;
+
+            static VacantCell acquire() {
+                synchronized (sLock) {
+                    if (sRoot == null) {
+                        return new VacantCell();
+                    }
+
+                    VacantCell info = sRoot;
+                    sRoot = info.next;
+                    sAcquiredCount--;
+
+                    return info;
+                }
+            }
+
+            void release() {
+                synchronized (sLock) {
+                    if (sAcquiredCount < POOL_LIMIT) {
+                        sAcquiredCount++;
+                        next = sRoot;
+                        sRoot = this;
+                    }
+                }
+            }
+
             @Override
             public String toString() {
                 return "VacantCell[x=" + cellX + ", y=" + cellY + ", spanX=" + spanX +
@@ -832,17 +918,27 @@
         int screen;
         boolean valid;
 
-        ArrayList<VacantCell> vacantCells;
+        final ArrayList<VacantCell> vacantCells = new ArrayList<VacantCell>(VacantCell.POOL_LIMIT);
         int maxVacantSpanX;
         int maxVacantSpanXSpanY;
         int maxVacantSpanY;
         int maxVacantSpanYSpanX;
+        final Rect current = new Rect();
+
+        private void clearVacantCells() {
+            final ArrayList<VacantCell> list = vacantCells;
+            final int count = list.size();
+
+            for (int i = 0; i < count; i++) list.get(i).release();
+
+            list.clear();
+        }
 
         void findVacantCellsFromOccupied(boolean[] occupied, int xCount, int yCount) {
             if (cellX < 0 || cellY < 0) {
                 maxVacantSpanX = maxVacantSpanXSpanY = Integer.MIN_VALUE;
                 maxVacantSpanY = maxVacantSpanYSpanX = Integer.MIN_VALUE;
-                vacantCells = new ArrayList<VacantCell>();
+                clearVacantCells();
                 return;
             }
 
@@ -855,26 +951,40 @@
             CellLayout.findIntersectingVacantCells(this, cellX, cellY, xCount, yCount, unflattened);
         }
 
+        /**
+         * This method can be called only once! Calling #findVacantCellsFromOccupied will
+         * restore the ability to call this method.
+         *
+         * Finds the upper-left coordinate of the first rectangle in the grid that can
+         * hold a cell of the specified dimensions.
+         *
+         * @param cellXY The array that will contain the position of a vacant cell if such a cell
+         *               can be found.
+         * @param spanX The horizontal span of the cell we want to find.
+         * @param spanY The vertical span of the cell we want to find.
+         *
+         * @return True if a vacant cell of the specified dimension was found, false otherwise.
+         */
         boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
-            if (vacantCells == null) {
-                return false;
-            }
+            final ArrayList<VacantCell> list = vacantCells;
+            final int count = list.size();
+
+            boolean found = false;
 
             if (this.spanX >= spanX && this.spanY >= spanY) {
                 cellXY[0] = cellX;
                 cellXY[1] = cellY;
-                return true;
+                found = true;
             }
 
-            final ArrayList<VacantCell> list = vacantCells;
-            final int count = list.size();
             // Look for an exact match first
             for (int i = 0; i < count; i++) {
                 VacantCell cell = list.get(i);
                 if (cell.spanX == spanX && cell.spanY == spanY) {
                     cellXY[0] = cell.cellX;
                     cellXY[1] = cell.cellY;
-                    return true;
+                    found = true;
+                    break;
                 }
             }
 
@@ -884,11 +994,14 @@
                 if (cell.spanX >= spanX && cell.spanY >= spanY) {
                     cellXY[0] = cell.cellX;
                     cellXY[1] = cell.cellY;
-                    return true;
+                    found = true;
+                    break;
                 }
             }
 
-            return false;
+            clearVacantCells();
+
+            return found;
         }
 
         @Override
diff --git a/src/com/android/launcher/DeleteZone.java b/src/com/android/launcher/DeleteZone.java
index 798cf0d..6f67884 100644
--- a/src/com/android/launcher/DeleteZone.java
+++ b/src/com/android/launcher/DeleteZone.java
@@ -97,6 +97,12 @@
             final UserFolderInfo userFolderInfo = (UserFolderInfo)item;
             LauncherModel.deleteUserFolderContentsFromDatabase(mLauncher, userFolderInfo);
             model.removeUserFolder(userFolderInfo);
+        } else if (item instanceof LauncherGadgetInfo) {
+            final LauncherGadgetInfo launcherGadgetInfo = (LauncherGadgetInfo)item;
+            final LauncherGadgetHost gadgetHost = mLauncher.getGadgetHost();
+            if (gadgetHost != null) {
+                gadgetHost.deleteGadgetId(launcherGadgetInfo.gadgetId);
+            }
         }
         LauncherModel.deleteItemFromDatabase(mLauncher, item);
     }
diff --git a/src/com/android/launcher/DragLayer.java b/src/com/android/launcher/DragLayer.java
index 56140dd..aa6615a 100644
--- a/src/com/android/launcher/DragLayer.java
+++ b/src/com/android/launcher/DragLayer.java
@@ -320,40 +320,33 @@
 
             break;
         case MotionEvent.ACTION_MOVE:
-            if (Launcher.sOpenGlEnabled) {
-                mLastMotionX = x;
-                mLastMotionY = y;
+            final int scrollX = mScrollX;
+            final int scrollY = mScrollY;
 
-                invalidate();
-            } else {
-                final int scrollX = mScrollX;
-                final int scrollY = mScrollY;
+            final float touchX = mTouchOffsetX;
+            final float touchY = mTouchOffsetY;
 
-                final float touchX = mTouchOffsetX;
-                final float touchY = mTouchOffsetY;
+            final int offsetX = mBitmapOffsetX;
+            final int offsetY = mBitmapOffsetY;
 
-                final int offsetX = mBitmapOffsetX;
-                final int offsetY = mBitmapOffsetY;
+            int left = (int) (scrollX + mLastMotionX - touchX - offsetX);
+            int top = (int) (scrollY + mLastMotionY - touchY - offsetY);
 
-                int left = (int) (scrollX + mLastMotionX - touchX - offsetX);
-                int top = (int) (scrollY + mLastMotionY - touchY - offsetY);
+            final Bitmap dragBitmap = mDragBitmap;
+            final int width = dragBitmap.getWidth();
+            final int height = dragBitmap.getHeight();
 
-                final Bitmap dragBitmap = mDragBitmap;
-                final int width = dragBitmap.getWidth();
-                final int height = dragBitmap.getHeight();
+            final Rect rect = mRect;
+            rect.set(left - 1, top - 1, left + width + 1, top + height + 1);
 
-                final Rect rect = mRect;
-                rect.set(left - 1, top - 1, left + width + 1, top + height + 1);
+            mLastMotionX = x;
+            mLastMotionY = y;
 
-                mLastMotionX = x;
-                mLastMotionY = y;
+            left = (int) (scrollX + x - touchX - offsetX);
+            top = (int) (scrollY + y - touchY - offsetY);
 
-                left = (int) (scrollX + x - touchX - offsetX);
-                top = (int) (scrollY + y - touchY - offsetY);
-
-                rect.union(left - 1, top - 1, left + width + 1, top + height + 1);
-                invalidate(rect);
-            }
+            rect.union(left - 1, top - 1, left + width + 1, top + height + 1);
+            invalidate(rect);
 
             final int[] coordinates = mDropCoordinates;
             DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
diff --git a/src/com/android/launcher/ItemInfo.java b/src/com/android/launcher/ItemInfo.java
index 61745dd..8899f44 100644
--- a/src/com/android/launcher/ItemInfo.java
+++ b/src/com/android/launcher/ItemInfo.java
@@ -38,10 +38,8 @@
     /**
      * One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
      * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_USER_FOLDER},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_WIDGET_CLOCK},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_WIDGET_SEARCH} or
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_WIDGET_PHOTO_FRAME},
+     * {@link LauncherSettings.Favorites#ITEM_TYPE_USER_FOLDER}, or
+     * {@link LauncherSettings.Favorites#ITEM_TYPE_GADGET}.
      */
     int itemType;
     
diff --git a/src/com/android/launcher/Launcher.java b/src/com/android/launcher/Launcher.java
index 465400b..293abe8 100644
--- a/src/com/android/launcher/Launcher.java
+++ b/src/com/android/launcher/Launcher.java
@@ -24,16 +24,21 @@
 import android.app.StatusBarManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.gadget.GadgetInfo;
+import android.gadget.GadgetManager;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -64,16 +69,15 @@
 import android.view.WindowManager;
 import android.view.View.OnLongClickListener;
 import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
 import android.widget.EditText;
-import android.widget.ExpandableListView;
-import android.widget.ImageView;
+import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.Toast;
 import android.widget.GridView;
+import android.widget.SlidingDrawer;
 import android.app.IWallpaperService;
 
-import com.android.internal.widget.SlidingDrawer;
-
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
@@ -86,8 +90,6 @@
     private static final boolean PROFILE_STARTUP = false;
     private static final boolean DEBUG_USER_INTERFACE = false;
 
-    private static final boolean REMOVE_SHORTCUT_ON_PACKAGE_REMOVE = false;    
-
     private static final int WALLPAPER_SCREENS_SPAN = 2;
 
     private static final int MENU_GROUP_ADD = 1;
@@ -98,9 +100,12 @@
     private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1;
 
     private static final int REQUEST_CREATE_SHORTCUT = 1;
-    private static final int REQUEST_CHOOSE_PHOTO = 2;
-    private static final int REQUEST_UPDATE_PHOTO = 3;
     private static final int REQUEST_CREATE_LIVE_FOLDER = 4;
+    private static final int REQUEST_CREATE_GADGET = 5;
+    private static final int REQUEST_PICK_APPLICATION = 6;
+    private static final int REQUEST_PICK_SHORTCUT = 7;
+    private static final int REQUEST_PICK_LIVE_FOLDER = 8;
+    private static final int REQUEST_PICK_GADGET = 9;
 
     static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
 
@@ -148,10 +153,6 @@
 
     private static Bitmap sWallpaper;
 
-    // Indicates whether the OpenGL pipeline was enabled, either through
-    // USE_OPENGL_BY_DEFAULT or the system property launcher.opengl
-    static boolean sOpenGlEnabled;
-
     private static final Object sLock = new Object();
     private static int sScreen = DEFAULT_SCREN;
 
@@ -164,7 +165,12 @@
 
     private DragLayer mDragLayer;
     private Workspace mWorkspace;
-
+    
+    private GadgetManager mGadgetManager;
+    private LauncherGadgetHost mGadgetHost;
+    
+    private static final int GADGET_HOST_ID = 1024;
+    
     private CellLayout.CellInfo mAddItemCellInfo;
     private CellLayout.CellInfo mMenuAddInfo;
     private final int[] mCellCoordinates = new int[2];
@@ -189,6 +195,11 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mInflater = getLayoutInflater();
+        
+        mGadgetManager = GadgetManager.getInstance(this);
+        mGadgetHost = new LauncherGadgetHost(this, GADGET_HOST_ID);
+        
+        // TODO: figure out if this is first launch and correctly clear GadgetHost database
 
         if (PROFILE_STARTUP) {
             android.os.Debug.startMethodTracing("/sdcard/launcher");
@@ -223,6 +234,18 @@
         Selection.setSelection(mDefaultKeySsb, 0);
     }
     
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mGadgetHost.startListening();
+    }
+    
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mGadgetHost.stopListening();
+    }
+    
     private void checkForLocaleChange() {
         final SharedPreferences preferences = getSharedPreferences(PREFERENCES, MODE_PRIVATE);
         final Configuration configuration = getResources().getConfiguration();
@@ -283,20 +306,42 @@
 
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        // The pattern used here is that a user PICKs a specific application,
+        // which, depending on the target, might need to CREATE the actual target.
+        
+        // For example, the user would PICK_SHORTCUT for "Music playlist", and we
+        // launch over to the Music app to actually CREATE_SHORTCUT.
+        
         if (resultCode == RESULT_OK && mAddItemCellInfo != null) {
             switch (requestCode) {
+                case REQUEST_PICK_APPLICATION:
+                    completeAddApplication(this, data, mAddItemCellInfo);
+                    break;
+                case REQUEST_PICK_SHORTCUT:
+                    addShortcut(data);
+                    break;
                 case REQUEST_CREATE_SHORTCUT:
                     completeAddShortcut(data, mAddItemCellInfo, !mDesktopLocked);
                     break;
-                case REQUEST_CHOOSE_PHOTO:
-                    completeAddPhotoFrame(data, mAddItemCellInfo);
-                    break;
-                case REQUEST_UPDATE_PHOTO:
-                    completeUpdatePhotoFrame(data, mAddItemCellInfo);
+                case REQUEST_PICK_LIVE_FOLDER:
+                    addLiveFolder(data);
                     break;
                 case REQUEST_CREATE_LIVE_FOLDER:
                     completeAddLiveFolder(data, mAddItemCellInfo, !mDesktopLocked);
                     break;
+                case REQUEST_PICK_GADGET:
+                    addGadget(data);
+                    break;
+                case REQUEST_CREATE_GADGET:
+                    completeAddGadget(data, mAddItemCellInfo, !mDesktopLocked);
+                    break;
+            }
+        } else if (requestCode == REQUEST_PICK_GADGET &&
+                resultCode == RESULT_CANCELED && data != null) {
+            // Clean up the gadgetId if we canceled
+            int gadgetId = data.getIntExtra(GadgetManager.EXTRA_GADGET_ID, -1);
+            if (gadgetId != -1) {
+                mGadgetHost.deleteGadgetId(gadgetId);
             }
         }
         mWaitingForResult = false;
@@ -398,7 +443,6 @@
             mRestoring = true;
         }
 
-
         boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
         if (renameFolder) {
             long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
@@ -439,10 +483,6 @@
         grid.setTextFilterEnabled(true);
         grid.setDragger(dragLayer);
         grid.setLauncher(this);
-        if (sOpenGlEnabled) {
-            grid.setScrollingCacheEnabled(false);
-            grid.setFadingEdgeLength(0);
-        }
 
         workspace.setOnLongClickListener(this);
         workspace.setDragger(dragLayer);
@@ -456,18 +496,6 @@
         dragLayer.setIgnoredDropTarget(grid);
         dragLayer.setDragScoller(workspace);
         dragLayer.setDragListener(deleteZone);
-
-        if (DEBUG_USER_INTERFACE) {
-            android.widget.Button finishButton = new android.widget.Button(this);
-            finishButton.setText("Finish");
-            workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
-
-            finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
-                public void onClick(View v) {
-                    finish();
-                }
-            });
-        }
     }
 
     /**
@@ -507,11 +535,42 @@
         return favorite;
     }
 
-    void addApplicationShortcut(ApplicationInfo info) {
-        mAddItemCellInfo.screen = mWorkspace.getCurrentScreen();
-        mWorkspace.addApplicationShortcut(info, mAddItemCellInfo);
-    }
+    /**
+     * Add an application shortcut to the workspace.
+     *
+     * @param data The intent describing the application.
+     * @param cellInfo The position on screen where to create the shortcut.
+     */
+    void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo) {
+        cellInfo.screen = mWorkspace.getCurrentScreen();
 
+        // Find details for this application
+        ComponentName component = data.getComponent();
+        PackageManager packageManager = context.getPackageManager();
+        ActivityInfo activityInfo = null;
+        try {
+            activityInfo = packageManager.getActivityInfo(component, 0 /* no flags */);
+        } catch (NameNotFoundException e) {
+            Log.e(LOG_TAG, "Couldn't find ActivityInfo for selected application", e);
+        }
+        
+        if (activityInfo != null) {
+            ApplicationInfo itemInfo = new ApplicationInfo();
+            
+            itemInfo.title = activityInfo.loadLabel(packageManager);
+            if (itemInfo.title == null) {
+                itemInfo.title = activityInfo.name;
+            }
+            
+            itemInfo.setActivity(component, Intent.FLAG_ACTIVITY_NEW_TASK |
+                    Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+            itemInfo.icon = activityInfo.loadIcon(packageManager);
+            itemInfo.container = ItemInfo.NO_ID;
+
+            mWorkspace.addApplicationShortcut(itemInfo, mAddItemCellInfo);
+        }
+    }
+    
     /**
      * Add a shortcut to the workspace.
      *
@@ -535,6 +594,74 @@
         }
     }
 
+    
+    /**
+     * Add a gadget to the workspace.
+     *
+     * @param data The intent describing the gadgetId.
+     * @param cellInfo The position on screen where to create the shortcut.
+     * @param insertAtFirst
+     */
+    private void completeAddGadget(Intent data, CellLayout.CellInfo cellInfo,
+            boolean insertAtFirst) {
+
+        Bundle extras = data.getExtras();
+        int gadgetId = extras.getInt(GadgetManager.EXTRA_GADGET_ID, -1);
+        
+        Log.d(LOG_TAG, "dumping extras content="+extras.toString());
+        
+        GadgetInfo gadgetInfo = mGadgetManager.getGadgetInfo(gadgetId);
+        
+        // Calculate the grid spans needed to fit this gadget
+        CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
+        layout.rectToCell(gadgetInfo.minWidth, gadgetInfo.minHeight, cellInfo);
+
+        // Try finding open space on Launcher screen
+        final int[] xy = mCellCoordinates;
+        if (!findSlot(cellInfo, xy, cellInfo.spanX, cellInfo.spanY)) return;
+
+        // Build Launcher-specific Gadget info and save to database
+        LauncherGadgetInfo launcherInfo = new LauncherGadgetInfo(gadgetId);
+        launcherInfo.spanX = cellInfo.spanX;
+        launcherInfo.spanY = cellInfo.spanY;
+        
+        LauncherModel.addItemToDatabase(this, launcherInfo,
+                LauncherSettings.Favorites.CONTAINER_DESKTOP,
+                mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
+
+        if (!mRestoring) {
+            sModel.addDesktopItem(launcherInfo);
+            
+            // Perform actual inflation because we're live
+            launcherInfo.hostView = mGadgetHost.createView(this, gadgetId, gadgetInfo);
+            
+            launcherInfo.hostView.setGadget(gadgetId, gadgetInfo);
+            launcherInfo.hostView.setTag(launcherInfo);
+            
+            mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
+                    launcherInfo.spanX, launcherInfo.spanY, insertAtFirst);
+        } else if (sModel.isDesktopLoaded()) {
+            sModel.addDesktopItem(launcherInfo);
+        }
+        
+        // Request fresh update if we needed to config this gadget to
+        // remove the stale UPDATE from the initial bind
+        if (!extras.containsKey(SKIP_CONFIG) || true) {
+            // TODO: move this down into GadgetManager to prevent abuse? (anyone
+            // could force a specific gadget into the ground through flooding)
+            Log.d(LOG_TAG, "requesting new gadget update because we had a config step");
+            Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION);
+            intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, new int[] { gadgetId });
+            intent.setComponent(gadgetInfo.provider);
+            sendBroadcast(intent);
+        }
+
+    }
+    
+    public LauncherGadgetHost getGadgetHost() {
+        return mGadgetHost;
+    }
+    
     static ApplicationInfo addShortcut(Context context, Intent data,
             CellLayout.CellInfo cellInfo, boolean notify) {
 
@@ -584,107 +711,6 @@
         return info;
     }
 
-    /**
-     * Add a PhotFrame to the workspace.
-     *
-     * @param data The intent describing the photo.
-     * @param cellInfo The position on screen where to create the shortcut.
-     */
-    private void completeAddPhotoFrame(Intent data, CellLayout.CellInfo cellInfo) {
-        final Bundle extras = data.getExtras();
-        if (extras != null) {
-            Bitmap photo = extras.getParcelable("data");
-
-            Widget info = Widget.makePhotoFrame();
-            info.photo = photo;
-
-            final int[] xy = mCellCoordinates;
-            if (!findSlot(cellInfo, xy, info.spanX, info.spanY)) return;
-
-            LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                    mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
-
-            if (!mRestoring) {
-                sModel.addDesktopItem(info);
-
-                final PhotoFrame view = (PhotoFrame) mInflater.inflate(info.layoutResource, null);
-                view.setImageBitmap(photo);
-                view.setTag(info);
-
-                mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, info.spanY);
-            } else if (sModel.isDesktopLoaded()) {
-                sModel.addDesktopItem(info);
-            }
-        }
-    }
-
-    /**
-     * Updates a workspace PhotoFrame.
-     *
-     * @param data The intent describing the photo.
-     * @param cellInfo The position on screen of the PhotoFrame to update.
-     */
-    private void completeUpdatePhotoFrame(Intent data, CellLayout.CellInfo cellInfo) {
-        final Bundle extras = data.getExtras();
-        if (extras != null) {
-            Widget info;
-            Bitmap photo = extras.getParcelable("data");
-
-            if (!mRestoring) {
-                final CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
-                final PhotoFrame view = (PhotoFrame) layout.findCell(cellInfo.cellX, cellInfo.cellY,
-                        cellInfo.spanX, cellInfo.spanY, null);
-                view.setImageBitmap(photo);
-                info = (Widget) view.getTag();
-            } else {
-                info = LauncherModel.getPhotoFrameInfo(this, cellInfo.screen,
-                        cellInfo.cellX, cellInfo.cellY);
-            }
-
-            info.photo = photo;
-            LauncherModel.updateItemInDatabase(this, info);
-        }
-    }
-
-    /**
-     * Starts a new Intent to let the user update the PhotoFrame defined by the
-     * specified Widget.
-     *
-     * @param widget The Widget info defining which PhotoFrame to update.
-     */
-    void updatePhotoFrame(Widget widget) {
-        CellLayout.CellInfo info = new CellLayout.CellInfo();
-        info.screen = widget.screen;
-        info.cellX = widget.cellX;
-        info.cellY = widget.cellY;
-        info.spanX = widget.spanX;
-        info.spanY = widget.spanY;
-        mAddItemCellInfo = info;
-
-        startActivityForResult(createPhotoPickIntent(), Launcher.REQUEST_UPDATE_PHOTO);
-    }
-
-    /**
-     * Creates an Intent used to let the user pick a photo for a PhotoFrame.
-     *
-     * @return The Intent to pick a photo suited for a PhotoFrame.
-     */
-    private static Intent createPhotoPickIntent() {
-        // TODO: Move this method to PhotoFrame?
-        // TODO: get these values from constants somewhere
-        // TODO: Adjust the PhotoFrame's image size to avoid on the fly scaling
-        Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
-        intent.setType("image/*");
-        intent.putExtra("crop", "true");
-        intent.putExtra("aspectX", 1);
-        intent.putExtra("aspectY", 1);
-        intent.putExtra("outputX", 192);
-        intent.putExtra("outputY", 192);
-        intent.putExtra("noFaceDetection", true);
-        intent.putExtra("return-data", true);
-        return intent;
-    }
-
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
@@ -871,10 +897,51 @@
 
     private void removeShortcutsForPackage(String packageName) {
         if (packageName != null && packageName.length() > 0) {
-            android.util.Log.d(LOG_TAG, packageName);
             mWorkspace.removeShortcutsForPackage(packageName);
         }
     }
+    
+    static final String SKIP_CONFIG = "skip_config";
+
+    void addGadget(Intent data) {
+        int gadgetId = data.getIntExtra(GadgetManager.EXTRA_GADGET_ID, -1);
+        GadgetInfo gadget = mGadgetManager.getGadgetInfo(gadgetId);
+
+        if (gadget.configure != null) {
+            // Launch over to configure gadget, if needed
+            Intent intent = new Intent(GadgetManager.GADGET_CONFIGURE_ACTION);
+            intent.setComponent(gadget.configure);
+            intent.putExtra(GadgetManager.EXTRA_GADGET_ID, gadgetId);
+
+            startActivityForResult(intent, REQUEST_CREATE_GADGET);
+        } else {
+            // Otherwise just add it
+            Log.d(LOG_TAG, "dumping extras content="+data.getExtras().toString());
+            data.putExtra(SKIP_CONFIG, true);
+            Log.d(LOG_TAG, "dumping extras content="+data.getExtras().toString());
+            onActivityResult(REQUEST_CREATE_GADGET, Activity.RESULT_OK, data);
+        }
+    }
+    
+    void addSearch() {
+        final Widget info = Widget.makeSearch();
+        final CellLayout.CellInfo cellInfo = mAddItemCellInfo;
+        
+        final int[] xy = mCellCoordinates;
+        final int spanX = info.spanX;
+        final int spanY = info.spanY;
+    
+        if (!findSlot(cellInfo, xy, spanX, spanY)) return;
+    
+        sModel.addDesktopItem(info);
+        LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
+        mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
+    
+        final View view = mInflater.inflate(info.layoutResource, null);
+        view.setTag(info);
+    
+        mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY);
+    }
 
     void addShortcut(Intent intent) {
         startActivityForResult(intent, REQUEST_CREATE_SHORTCUT);
@@ -964,39 +1031,6 @@
         return info;
     }
 
-    void getPhotoForPhotoFrame() {
-        startActivityForResult(createPhotoPickIntent(), REQUEST_CHOOSE_PHOTO);
-    }
-
-    void addClock() {
-        final Widget info = Widget.makeClock();
-        addWidget(info);
-    }
-
-    void addSearch() {
-        final Widget info = Widget.makeSearch();
-        addWidget(info);
-    }
-    
-    private void addWidget(final Widget info) {
-        final CellLayout.CellInfo cellInfo = mAddItemCellInfo;
-
-        final int[] xy = mCellCoordinates;
-        final int spanX = info.spanX;
-        final int spanY = info.spanY;
-
-        if (!findSlot(cellInfo, xy, spanX, spanY)) return;
-
-        sModel.addDesktopItem(info);
-        LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
-
-        final View view = mInflater.inflate(info.layoutResource, null);
-        view.setTag(info);
-
-        mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY);
-    }
-
     private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
         if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
             boolean[] occupied = mSavedState != null ?
@@ -1136,6 +1170,18 @@
         for (int i = 0; i < count; i++) {
             ((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout();
         }
+        
+        if (DEBUG_USER_INTERFACE) {
+            android.widget.Button finishButton = new android.widget.Button(this);
+            finishButton.setText("Finish");
+            workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
+
+            finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
+                public void onClick(View v) {
+                    finish();
+                }
+            });
+        }
 
         count = shortcuts.size();
 
@@ -1176,11 +1222,41 @@
                     workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
                             !desktopLocked);
                     break;
-                default:
+                case LauncherSettings.Favorites.ITEM_TYPE_GADGET:
+                    final LauncherGadgetInfo launcherInfo = (LauncherGadgetInfo) item;
+                    
+                    final int gadgetId = launcherInfo.gadgetId;
+                    GadgetInfo gadgetInfo = mGadgetManager.getGadgetInfo(gadgetId);
+                    launcherInfo.hostView = mGadgetHost.createView(this, gadgetId, gadgetInfo);
+                    
+                    Log.d(LOG_TAG, "about to setGadget during desktop bind");
+                    launcherInfo.hostView.setGadget(gadgetId, gadgetInfo);
+                    launcherInfo.hostView.setTag(launcherInfo);
+                    
+                    workspace.addInScreen(launcherInfo.hostView, item.screen, item.cellX,
+                            item.cellY, item.spanX, item.spanY, !desktopLocked);
+
+                    // Now that we've bound the item, request an update for it
+                    if (gadgetInfo != null) {
+                        if (gadgetInfo.provider != null) {
+                            Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION);
+                            intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, new int[] { gadgetId });
+                            intent.setComponent(gadgetInfo.provider);
+                            sendBroadcast(intent);
+                        }
+                    }
+                    
+                    break;
+                case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
+                    final int screen = workspace.getCurrentScreen();
+                    final View view = mInflater.inflate(R.layout.widget_search,
+                            (ViewGroup) workspace.getChildAt(screen), false);
+                    
                     final Widget widget = (Widget) item;
-                    final View view = createWidget(mInflater, widget);
                     view.setTag(widget);
+                    
                     workspace.addWidget(view, widget, !desktopLocked);
+                    break;
             }
         }
 
@@ -1226,17 +1302,6 @@
         mDrawer.unlock();
     }
 
-    private View createWidget(LayoutInflater inflater, Widget widget) {
-        final Workspace workspace = mWorkspace;
-        final int screen = workspace.getCurrentScreen();
-        View v = inflater.inflate(widget.layoutResource,
-                (ViewGroup) workspace.getChildAt(screen), false);
-        if (widget.itemType == LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
-            ((ImageView)v).setImageBitmap(widget.photo);
-        }
-        return v;
-    }
-
     DragController getDragController() {
         return mDragLayer;
     }
@@ -1263,6 +1328,11 @@
             startActivity(intent);
         } catch (ActivityNotFoundException e) {
             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+        } catch (SecurityException e) {
+            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+            Log.e(LOG_TAG, "Launcher does not have the permission to launch " + intent +
+                    ". Make sure to create a MAIN intent-filter for the corresponding activity " +
+                    "or use the exported attribute for this activity.", e);
         }
     }
 
@@ -1508,24 +1578,24 @@
      * Displays the shortcut creation dialog and launches, if necessary, the
      * appropriate activity.
      */
-    private class CreateShortcut implements ExpandableListView.OnChildClickListener,
-            DialogInterface.OnCancelListener, ExpandableListView.OnGroupExpandListener {
+    private class CreateShortcut implements AdapterView.OnItemClickListener,
+            DialogInterface.OnCancelListener {
         private AddAdapter mAdapter;
-        private ExpandableListView mList;
-
+        private ListView mList;
+        
         Dialog createDialog() {
             mWaitingForResult = true;
-            mAdapter = new AddAdapter(Launcher.this, false);
+            
+            mAdapter = new AddAdapter(Launcher.this);
             
             final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
             builder.setTitle(getString(R.string.menu_item_add_item));
             builder.setIcon(0);
 
-            mList = (ExpandableListView)
+            mList = (ListView)
                     View.inflate(Launcher.this, R.layout.create_shortcut_list, null);
             mList.setAdapter(mAdapter);
-            mList.setOnChildClickListener(this);
-            mList.setOnGroupExpandListener(this);
+            mList.setOnItemClickListener(this);
             builder.setView(mList);
             builder.setInverseBackgroundForced(true);
 
@@ -1539,13 +1609,6 @@
             return dialog;
         }
 
-        public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
-                int childPosition, long id) {
-            mAdapter.performAction(groupPosition, childPosition);
-            cleanup();
-            return true;
-        }
-
         public void onCancel(DialogInterface dialog) {
             mWaitingForResult = false;
             cleanup();
@@ -1556,10 +1619,76 @@
             dismissDialog(DIALOG_CREATE_SHORTCUT);
         }
 
-        public void onGroupExpand(int groupPosition) {
-            long packged = ExpandableListView.getPackedPositionForGroup(groupPosition);
-            int position = mList.getFlatListPosition(packged);
-            mList.setSelectionFromTop(position, 0);
+        public void onItemClick(AdapterView parent, View view, int position, long id) {
+            // handle which item was clicked based on position
+            // this will launch off pick intent
+            
+            Object tag = view.getTag();
+            if (tag instanceof AddAdapter.ListItem) {
+                AddAdapter.ListItem item = (AddAdapter.ListItem) tag;
+                cleanup();
+                switch (item.actionTag) {
+                    case AddAdapter.ITEM_APPLICATION: {
+                        Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+                        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+                        Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
+                        pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
+                        startActivityForResult(pickIntent, REQUEST_PICK_APPLICATION);
+                        break;
+                    }
+
+                    case AddAdapter.ITEM_SHORTCUT: {
+                        Intent shortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+
+                        Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
+                        pickIntent.putExtra(Intent.EXTRA_INTENT, shortcutIntent);
+                        pickIntent.putExtra(Intent.EXTRA_TITLE,
+                                getText(R.string.title_select_shortcut));
+                        startActivityForResult(pickIntent, REQUEST_PICK_SHORTCUT);
+                        break;
+                    }
+                    
+                    case AddAdapter.ITEM_SEARCH: {
+                        addSearch();
+                        break;
+                    }
+                    
+                    case AddAdapter.ITEM_GADGET: {
+                        int gadgetId = Launcher.this.mGadgetHost.allocateGadgetId();
+                        
+                        Intent pickIntent = new Intent(GadgetManager.GADGET_PICK_ACTION);
+                        pickIntent.putExtra(GadgetManager.EXTRA_HOST_ID, GADGET_HOST_ID);
+                        pickIntent.putExtra(GadgetManager.EXTRA_GADGET_ID, gadgetId);
+                        startActivityForResult(pickIntent, REQUEST_PICK_GADGET);
+                        break;
+                    }
+                    
+                    case AddAdapter.ITEM_LIVE_FOLDER: {
+                        Intent liveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
+
+                        Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
+                        pickIntent.putExtra(Intent.EXTRA_INTENT, liveFolderIntent);
+                        pickIntent.putExtra(Intent.EXTRA_TITLE,
+                                getText(R.string.title_select_live_folder));
+                        startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER);
+                        break;
+                    }
+
+                    case AddAdapter.ITEM_FOLDER: {
+                        addFolder();
+                        dismissDialog(DIALOG_CREATE_SHORTCUT);
+                        break;
+                    }
+
+                    case AddAdapter.ITEM_WALLPAPER: {
+                        startWallpaper();
+                        break;
+                    }
+
+                }                
+                
+            }
         }
     }
 
@@ -1569,13 +1698,20 @@
     private class ApplicationsIntentReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            //noinspection ConstantConditions
-            if (REMOVE_SHORTCUT_ON_PACKAGE_REMOVE &&
-                    Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
-                removeShortcutsForPackage(intent.getData().getSchemeSpecificPart());
+            boolean reloadWorkspace = false;
+            if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    removeShortcutsForPackage(intent.getData().getSchemeSpecificPart());
+                } else {
+                    reloadWorkspace = true;
+                }
             }
             removeDialog(DIALOG_CREATE_SHORTCUT);
-            sModel.loadApplications(false, Launcher.this, false);
+            if (!reloadWorkspace) {
+                sModel.loadApplications(false, Launcher.this, false);
+            } else {
+                sModel.loadUserItems(false, Launcher.this, false, true);
+            }
         }
     }
 
diff --git a/src/com/android/launcher/LauncherGadgetHost.java b/src/com/android/launcher/LauncherGadgetHost.java
new file mode 100644
index 0000000..4f7e8f2
--- /dev/null
+++ b/src/com/android/launcher/LauncherGadgetHost.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher;
+
+import android.content.Context;
+import android.gadget.GadgetHost;
+import android.gadget.GadgetHostView;
+import android.gadget.GadgetInfo;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.TextView;
+
+/**
+ * Specific {@link GadgetHost} that creates our {@link LauncherGadgetHostView} which correctly
+ * captures all long-press events.  This ensures that users can always pick up and move gadgets. 
+ */
+public class LauncherGadgetHost extends GadgetHost {
+    public LauncherGadgetHost(Context context, int hostId) {
+        super(context, hostId);
+    }
+    
+    protected GadgetHostView onCreateView(Context context, int gadgetId, GadgetInfo gadget) {
+        return new LauncherGadgetHostView(context);
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public class LauncherGadgetHostView extends GadgetHostView {
+        static final String TAG = "LauncherGadgetHostView";
+
+        private boolean mHasPerformedLongPress;
+        
+        private CheckForLongPress mPendingCheckForLongPress;
+        
+        private LayoutInflater mInflater;
+        
+        public LauncherGadgetHostView(Context context) {
+            super(context);
+            
+            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            
+            // Prepare our default transition animations
+            setAnimateFirstView(true);
+            setInAnimation(context, android.R.anim.fade_in);
+            setOutAnimation(context, android.R.anim.fade_out);
+        }
+        
+        @Override
+        protected View getErrorView() {
+            return mInflater.inflate(R.layout.gadget_error, this, false);
+        }
+
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            
+            // Consume any touch events for ourselves after longpress is triggered
+            if (mHasPerformedLongPress) {
+                mHasPerformedLongPress = false;
+                return true;
+            }
+                
+            // Watch for longpress events at this level to make sure
+            // users can always pick up this Gadget
+            switch (ev.getAction()) {
+                case MotionEvent.ACTION_DOWN: {
+                    postCheckForLongClick();
+                    break;
+                }
+                
+                case MotionEvent.ACTION_UP: {
+                    mHasPerformedLongPress = false;
+                    if (mPendingCheckForLongPress != null) {
+                        removeCallbacks(mPendingCheckForLongPress);
+                    }
+                    break;
+                }
+            }
+            
+            // Otherwise continue letting touch events fall through to children
+            return false;
+        }
+        
+        class CheckForLongPress implements Runnable {
+            private int mOriginalWindowAttachCount;
+
+            public void run() {
+                if ((mParent != null) && hasWindowFocus()
+                        && mOriginalWindowAttachCount == getWindowAttachCount()
+                        && !mHasPerformedLongPress) {
+                    if (performLongClick()) {
+                        mHasPerformedLongPress = true;
+                    }
+                }
+            }
+
+            public void rememberWindowAttachCount() {
+                mOriginalWindowAttachCount = getWindowAttachCount();
+            }
+        }
+
+        private void postCheckForLongClick() {
+            mHasPerformedLongPress = false;
+
+            if (mPendingCheckForLongPress == null) {
+                mPendingCheckForLongPress = new CheckForLongPress();
+            }
+            mPendingCheckForLongPress.rememberWindowAttachCount();
+            postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout());
+        }
+
+    }
+
+}
+
diff --git a/src/com/android/launcher/LauncherGadgetInfo.java b/src/com/android/launcher/LauncherGadgetInfo.java
new file mode 100644
index 0000000..65fa774
--- /dev/null
+++ b/src/com/android/launcher/LauncherGadgetInfo.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher;
+
+import android.content.ContentValues;
+import android.gadget.GadgetHostView;
+
+/**
+ * Represents a gadget, which just contains an identifier.
+ */
+class LauncherGadgetInfo extends ItemInfo {
+
+    /**
+     * Identifier for this gadget when talking with {@link GadgetManager} for updates.
+     */
+    int gadgetId;
+    
+    /**
+     * View that holds this gadget after it's been created.  This view isn't created
+     * until Launcher knows it's needed.
+     */
+    GadgetHostView hostView = null;
+
+    LauncherGadgetInfo(int gadgetId) {
+        itemType = LauncherSettings.Favorites.ITEM_TYPE_GADGET;
+        this.gadgetId = gadgetId;
+    }
+    
+    @Override
+    void onAddToDatabase(ContentValues values) {
+        super.onAddToDatabase(values);
+        values.put(LauncherSettings.Favorites.GADGET_ID, gadgetId);
+    }
+
+    @Override
+    public String toString() {
+        return Integer.toString(gadgetId);
+    }
+}
diff --git a/src/com/android/launcher/LauncherModel.java b/src/com/android/launcher/LauncherModel.java
index 314a502..5d01796 100644
--- a/src/com/android/launcher/LauncherModel.java
+++ b/src/com/android/launcher/LauncherModel.java
@@ -272,7 +272,8 @@
         try {
             while (c.moveToNext()) {
                 try {
-                    if (c.getInt(itemTypeIndex) != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                    if (c.getInt(itemTypeIndex) !=
+                            LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                         continue;
                     }
 
@@ -374,15 +375,19 @@
                 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
                 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
                 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
+                final int gadgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.GADGET_ID);
                 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
                 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
                 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
+                final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
+                final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
                 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
                 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
 
                 ApplicationInfo info;
                 String intentDescription;
                 Widget widgetInfo = null;
+                LauncherGadgetInfo gadgetInfo = null;
                 int container;
                 long id;
                 Intent intent;
@@ -494,41 +499,44 @@
                                     break;
                             }
                             break;
-                        case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK:
                         case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
-                        case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME:
-                            switch (itemType) {
-                            case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK:
-                                widgetInfo = Widget.makeClock();
-                                break;
-                            case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
-                                widgetInfo = Widget.makeSearch();
-                                break;
-                            case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME:
-                                widgetInfo = Widget.makePhotoFrame();
-                                byte[] data = c.getBlob(iconIndex);
-                                if (data != null) {
-                                    widgetInfo.photo =
-                                            BitmapFactory.decodeByteArray(data, 0, data.length);
-                                }
-                                break;
-                            }
+                            widgetInfo = Widget.makeSearch();
 
-                            if (widgetInfo != null) {
-                                container = c.getInt(containerIndex);
-                                if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                                    Log.e(Launcher.LOG_TAG, "Widget found where container "
-                                            + "!= CONTAINER_DESKTOP -- ignoring!");
-                                    continue;
-                                }
-                                widgetInfo.id = c.getLong(idIndex);
-                                widgetInfo.screen = c.getInt(screenIndex);
-                                widgetInfo.container = container;
-                                widgetInfo.cellX = c.getInt(cellXIndex);
-                                widgetInfo.cellY = c.getInt(cellYIndex);
-
-                                desktopItems.add(widgetInfo);
+                            container = c.getInt(containerIndex);
+                            if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                                Log.e(Launcher.LOG_TAG, "Widget found where container "
+                                        + "!= CONTAINER_DESKTOP  ignoring!");
+                                continue;
                             }
+                            
+                            widgetInfo.id = c.getLong(idIndex);
+                            widgetInfo.screen = c.getInt(screenIndex);
+                            widgetInfo.container = container;
+                            widgetInfo.cellX = c.getInt(cellXIndex);
+                            widgetInfo.cellY = c.getInt(cellYIndex);
+
+                            desktopItems.add(widgetInfo);
+                            break;
+                        case LauncherSettings.Favorites.ITEM_TYPE_GADGET:
+                            // Read all Launcher-specific gadget details
+                            int gadgetId = c.getInt(gadgetIdIndex);
+                            gadgetInfo = new LauncherGadgetInfo(gadgetId);
+                            gadgetInfo.id = c.getLong(idIndex);
+                            gadgetInfo.screen = c.getInt(screenIndex);
+                            gadgetInfo.cellX = c.getInt(cellXIndex);
+                            gadgetInfo.cellY = c.getInt(cellYIndex);
+                            gadgetInfo.spanX = c.getInt(spanXIndex);
+                            gadgetInfo.spanY = c.getInt(spanYIndex);
+
+                            container = c.getInt(containerIndex);
+                            if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                                Log.e(Launcher.LOG_TAG, "Gadget found where container "
+                                        + "!= CONTAINER_DESKTOP -- ignoring!");
+                                continue;
+                            }
+                            gadgetInfo.container = c.getInt(containerIndex);
+                            
+                            desktopItems.add(gadgetInfo);
                             break;
                         }
                     } catch (Exception e) {
@@ -972,7 +980,7 @@
         final ContentResolver cr = context.getContentResolver();
 
         cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
-        cr.delete(LauncherSettings.Favorites.CONTENT_URI, LauncherSettings.Favorites.CONTAINER + "=" + info.id,
-                null);
+        cr.delete(LauncherSettings.Favorites.CONTENT_URI,
+                LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
     }
 }
diff --git a/src/com/android/launcher/LauncherProvider.java b/src/com/android/launcher/LauncherProvider.java
index a3e529d..47db647 100644
--- a/src/com/android/launcher/LauncherProvider.java
+++ b/src/com/android/launcher/LauncherProvider.java
@@ -29,6 +29,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.database.Cursor;
+import android.database.SQLException;
 import android.util.Log;
 import android.util.Xml;
 import android.net.Uri;
@@ -49,7 +50,8 @@
     private static final String LOG_TAG = "LauncherSettingsProvider";
 
     private static final String DATABASE_NAME = "launcher.db";
-    private static final int DATABASE_VERSION = 1;
+    
+    private static final int DATABASE_VERSION = 2;
 
     static final String AUTHORITY = "com.android.launcher.settings";
 
@@ -187,6 +189,7 @@
                     "spanX INTEGER," +
                     "spanY INTEGER," +
                     "itemType INTEGER," +
+                    "gadgetId INTEGER NOT NULL DEFAULT -1," +
                     "isShortcut INTEGER," +
                     "iconType INTEGER," +
                     "iconPackage TEXT," +
@@ -196,6 +199,11 @@
                     "displayMode INTEGER" +
                     ");");
 
+            // TODO: During first database creation, trigger wipe of any gadgets that
+            // might have been left around during a wipe-data.
+//            GadgetManager gadgetManager = GadgetManager.getInstance(mContext);
+
+
             if (!convertDatabase(db)) {
                 // Populate favorites table with initial favorites
                 loadFavorites(db, DEFAULT_FAVORITES_PATH);
@@ -206,7 +214,7 @@
             boolean converted = false;
 
             final Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
-                    "/favorites?notify=true");
+                    "/old_favorites?notify=true");
             final ContentResolver resolver = mContext.getContentResolver();
             Cursor cursor = null;
 
@@ -261,6 +269,7 @@
                 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
                 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
                 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
+                values.put(LauncherSettings.Favorites.GADGET_ID, -1);
                 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
                 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
                 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
@@ -290,11 +299,31 @@
 
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            Log.w(LOG_TAG, "Upgrading database from version " + oldVersion + " to " +
-                    newVersion + ", which will destroy all old data");
-
-            db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
-            onCreate(db);
+            int version = oldVersion;
+            if (version == 1) {
+                // upgrade 1 -> 2 added gadgetId column
+                db.beginTransaction();
+                try {
+                    // TODO: convert any existing widgets for search and clock
+                    // this might involve a FORCE_ADD_GADGET permission in GadgetManager that
+                    // Launcher could then use to add these gadgets without user interaction
+                    db.execSQL("ALTER TABLE favorites " +
+                            "ADD COLUMN gadgetId INTEGER NOT NULL DEFAULT -1;");
+                    db.setTransactionSuccessful();
+                    version = 2;
+                } catch (SQLException ex) {
+                    // Old version remains, which means we wipe old data
+                    Log.e(LOG_TAG, ex.getMessage(), ex);
+                } finally {
+                    db.endTransaction();
+                }
+            }
+            
+            if (version != DATABASE_VERSION) {
+                Log.w(LOG_TAG, "Destroying all old data.");
+                db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
+                onCreate(db);
+            }
         }
 
 
@@ -370,20 +399,7 @@
             } catch (IOException e) {
                 Log.w(LOG_TAG, "Got exception parsing favorites.", e);
             }
-
-            // Add a clock
-            values.clear();
-            values.put(LauncherSettings.Favorites.CONTAINER,
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP);
-            values.put(LauncherSettings.Favorites.ITEM_TYPE,
-                    LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK);
-            values.put(LauncherSettings.Favorites.SCREEN, 1);
-            values.put(LauncherSettings.Favorites.CELLX, 1);
-            values.put(LauncherSettings.Favorites.CELLY, 0);
-            values.put(LauncherSettings.Favorites.SPANX, 2);
-            values.put(LauncherSettings.Favorites.SPANY, 2);
-            db.insert(TABLE_FAVORITES, null, values);
-
+            
             // Add a search box
             values.clear();
             values.put(LauncherSettings.Favorites.CONTAINER,
@@ -396,6 +412,9 @@
             values.put(LauncherSettings.Favorites.SPANX, 4);
             values.put(LauncherSettings.Favorites.SPANY, 1);
             db.insert(TABLE_FAVORITES, null, values);
+            
+            // TODO: automatically add clock and search gadget to
+            // default locations.  this might need the FORCE permission mentioned above
 
             return i;
         }
diff --git a/src/com/android/launcher/LauncherSettings.java b/src/com/android/launcher/LauncherSettings.java
index c5dfd1f..77bdc73 100644
--- a/src/com/android/launcher/LauncherSettings.java
+++ b/src/com/android/launcher/LauncherSettings.java
@@ -147,6 +147,11 @@
         static final int ITEM_TYPE_LIVE_FOLDER = 3;
 
         /**
+         * The favorite is a gadget
+         */
+        static final int ITEM_TYPE_GADGET = 4;
+
+        /**
          * The favorite is a clock
          */
         static final int ITEM_TYPE_WIDGET_CLOCK = 1000;
@@ -162,6 +167,13 @@
         static final int ITEM_TYPE_WIDGET_PHOTO_FRAME = 1002;
 
         /**
+         * The gadgetId of the gadget
+         *
+         * <P>Type: INTEGER</P>
+         */
+        static final String GADGET_ID = "gadgetId";
+        
+        /**
          * Indicates whether this favorite is an application-created shortcut or not.
          * If the value is 0, the favorite is not an application-created shortcut, if the
          * value is 1, it is an application-created shortcut.
diff --git a/src/com/android/launcher/PhotoFrame.java b/src/com/android/launcher/PhotoFrame.java
deleted file mode 100644
index 1151322..0000000
--- a/src/com/android/launcher/PhotoFrame.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.ImageView;
-
-
-/**
- * Desktop widget that holds a user folder
- *
- */
-public class PhotoFrame extends ImageView implements OnClickListener {
-    
-    public PhotoFrame(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setClickable(true);
-        setOnClickListener(this);
-        setWillNotCacheDrawing(true);
-    }
-    
-    public void onClick(View v) {
-        ((Launcher) mContext).updatePhotoFrame((Widget) getTag());
-    }
-}
diff --git a/src/com/android/launcher/Search.java b/src/com/android/launcher/Search.java
index 41d6562..449caf9 100644
--- a/src/com/android/launcher/Search.java
+++ b/src/com/android/launcher/Search.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher;
 
-import java.util.List;
-
 import android.app.ISearchManager;
 import android.app.SearchManager;
 import android.content.ActivityNotFoundException;
@@ -59,6 +57,8 @@
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.AdapterView.OnItemSelectedListener;
 
+import java.util.List;
+
 public class Search extends LinearLayout implements OnClickListener, OnKeyListener,
         OnLongClickListener, TextWatcher, OnItemClickListener, OnItemSelectedListener {
 
@@ -66,6 +66,7 @@
 
     private AutoCompleteTextView mSearchText;
     private ImageButton mGoButton;
+    private ImageButton mVoiceButton;
     private OnLongClickListener mLongClickListener;
     
     // Support for suggestions
@@ -76,6 +77,9 @@
     private String mSuggestionQuery = null;
     private int mItemSelected = -1;
     
+    // For voice searching
+    private Intent mVoiceSearchIntent;
+
     private Rect mTempRect = new Rect();
 
     /**
@@ -86,6 +90,10 @@
      */
     public Search(Context context, AttributeSet attrs) {
         super(context, attrs);
+        
+        mVoiceSearchIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+        mVoiceSearchIntent.putExtra(android.speech.RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+                android.speech.RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
     }
     
     /**
@@ -94,6 +102,14 @@
     public void onClick(View v) {
         if (v == mGoButton) {
             query();
+        } else if (v == mVoiceButton) {
+            try {
+                getContext().startActivity(mVoiceSearchIntent);
+            } catch (ActivityNotFoundException ex) {
+                // Should not happen, since we check the availability of
+                // voice search before showing the button. But just in case...
+                Log.w(TAG, "Could not find voice search activity");
+            }
         }
     }
 
@@ -206,7 +222,7 @@
                     return true;
                 }
             }
-        } else if (v == mGoButton) {
+        } else if (v == mGoButton || v == mVoiceButton) {
             boolean handled = false;
             if (!event.isSystem() && 
                     (keyCode != KeyEvent.KEYCODE_DPAD_UP) &&
@@ -243,7 +259,14 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        requestFocusFromTouch();
+        // Request focus unless the user tapped on the voice search button
+        final int x = (int) ev.getX();
+        final int y = (int) ev.getY();
+        final Rect frame = mTempRect;
+        mVoiceButton.getHitRect(frame);
+        if (!frame.contains(x, y)) {
+            requestFocusFromTouch();
+        }
         return super.onInterceptTouchEvent(ev);
     }
     
@@ -268,26 +291,29 @@
         mSearchText.addTextChangedListener(this);
 
         mGoButton = (ImageButton) findViewById(R.id.search_go_btn);
+        mVoiceButton = (ImageButton) findViewById(R.id.search_voice_btn);
         mGoButton.setOnClickListener(this);
+        mVoiceButton.setOnClickListener(this);
         mGoButton.setOnKeyListener(this);
+        mVoiceButton.setOnKeyListener(this);
         
         mSearchText.setOnLongClickListener(this);
         mGoButton.setOnLongClickListener(this);
+        mVoiceButton.setOnLongClickListener(this);
         
         // disable the button since we start out w/empty input
         mGoButton.setEnabled(false);
         mGoButton.setFocusable(false);
         
+        configureSearchableInfo();
         configureSuggestions();
+        configureVoiceSearchButton();
     }
     
-    /** The rest of the class deals with providing search suggestions */
-    
     /**
-     * Set up the suggestions provider mechanism
+     * Read the searchable info from the search manager
      */
-    private void configureSuggestions() {
-        // get SearchableInfo
+    private void configureSearchableInfo() {
         ISearchManager sms;
         SearchableInfo searchable;
         sms = ISearchManager.Stub.asInterface(ServiceManager.getService(Context.SEARCH_SERVICE));
@@ -303,6 +329,36 @@
             return;
         }
         mSearchable = searchable;
+    }
+    
+    /**
+     * If appropriate & available, configure voice search
+     * 
+     * Note:  Because the home screen search widget is always web search, we only check for
+     * getVoiceSearchLaunchWebSearch() modes.  We don't support the alternate form of app-specific
+     * voice search.
+     */
+    private void configureVoiceSearchButton() {
+        boolean voiceSearchVisible = false;
+        if (mSearchable.getVoiceSearchEnabled() && mSearchable.getVoiceSearchLaunchWebSearch()) {
+            // Enable the voice search button if there is an activity that can handle it
+            PackageManager pm = getContext().getPackageManager();
+            List<ResolveInfo> list = pm.queryIntentActivities(mVoiceSearchIntent,
+                    PackageManager.MATCH_DEFAULT_ONLY);
+            voiceSearchVisible = list.size() > 0;
+        }
+        
+        // finally, set visible state of voice search button, as appropriate
+        mVoiceButton.setVisibility(voiceSearchVisible ? View.VISIBLE : View.GONE);
+    }
+     
+    /** The rest of the class deals with providing search suggestions */
+    
+    /**
+     * Set up the suggestions provider mechanism
+     */
+    private void configureSuggestions() {
+        // get SearchableInfo
         
         mSearchText.setOnItemClickListener(this);
         mSearchText.setOnItemSelectedListener(this);
diff --git a/src/com/android/launcher/WallpaperChooser.java b/src/com/android/launcher/WallpaperChooser.java
index afbe6f3..1eb8d0c 100644
--- a/src/com/android/launcher/WallpaperChooser.java
+++ b/src/com/android/launcher/WallpaperChooser.java
@@ -123,9 +123,6 @@
         final String[] extras = resources.getStringArray(R.array.extra_wallpapers);
         final String packageName = getApplication().getPackageName();
 
-        final ArrayList<Integer> images = mImages;
-        final ArrayList<Integer> thumbs = mThumbs;
-
         for (String extra : extras) {
             int res = resources.getIdentifier(extra, "drawable", packageName);
             if (res != 0) {
@@ -133,8 +130,8 @@
                         "drawable", packageName);
 
                 if (thumbRes != 0) {
-                    images.add(res);
-                    thumbs.add(res);
+                    mThumbs.add(res);
+                    mImages.add(res);
                 }
             }
         }
@@ -148,7 +145,7 @@
 
     public void onItemSelected(AdapterView parent, View v, int position, long id) {
         final ImageView view = mImageView;
-        Bitmap b = BitmapFactory.decodeResource(getResources(), IMAGE_IDS[position], mOptions);
+        Bitmap b = BitmapFactory.decodeResource(getResources(), mImages.get(position), mOptions);
         view.setImageBitmap(b);
 
         // Help the GC
diff --git a/src/com/android/launcher/Workspace.java b/src/com/android/launcher/Workspace.java
index 0ca1b5a..510dad4 100644
--- a/src/com/android/launcher/Workspace.java
+++ b/src/com/android/launcher/Workspace.java
@@ -89,6 +89,8 @@
     private boolean mAllowLongPress;
     private boolean mLocked;
 
+    private int mTouchSlop;
+
     /**
      * Used to inflate the Workspace from XML.
      *
@@ -126,6 +128,8 @@
 
         mPaint = new Paint();
         mPaint.setDither(false);
+
+        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
     }
 
     /**
@@ -626,8 +630,8 @@
                  */
                 final int xDiff = (int) Math.abs(x - mLastMotionX);
                 final int yDiff = (int) Math.abs(y - mLastMotionY);
-                final int touchSlop = ViewConfiguration.getTouchSlop();
-                
+
+                final int touchSlop = mTouchSlop;
                 boolean xMoved = xDiff > touchSlop;
                 boolean yMoved = yDiff > touchSlop;
                 
@@ -1155,6 +1159,10 @@
             }
         }
     }
+    
+    // TODO: remove gadgets when gadgetmanager tells us they're gone
+//    void removeGadgetsForProvider() {
+//    }
 
     void moveToDefaultScreen() {
         snapToScreen(mDefaultScreen);