Redo the launcher loading code and put the real app icons into rollo.
diff --git a/src/com/android/launcher2/ActivityPicker.java b/src/com/android/launcher2/ActivityPicker.java
index f502e27..b6da08e 100644
--- a/src/com/android/launcher2/ActivityPicker.java
+++ b/src/com/android/launcher2/ActivityPicker.java
@@ -402,4 +402,4 @@
             return icon;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher2/AllAppsList.java b/src/com/android/launcher2/AllAppsList.java
new file mode 100644
index 0000000..4d3ee77
--- /dev/null
+++ b/src/com/android/launcher2/AllAppsList.java
@@ -0,0 +1,216 @@
+/*
+ * 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.launcher2;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.util.Log;
+import android.os.Process;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+
+/**
+ * Stores the list of all applications for the all apps view.
+ */
+class AllAppsList {
+    public static final int DEFAULT_APPLICATIONS_NUMBER = 42;
+    
+    /** The list off all apps. */
+    public ArrayList<ApplicationInfo> data = new ArrayList(DEFAULT_APPLICATIONS_NUMBER);
+    /** The list of apps that have been added since the last notify() call. */
+    public ArrayList<ApplicationInfo> added = new ArrayList(DEFAULT_APPLICATIONS_NUMBER);
+    /** The list of apps that have been removed since the last notify() call. */
+    public ArrayList<ApplicationInfo> removed = new ArrayList();
+    /** The list of apps that have been modified since the last notify() call. */
+    public ArrayList<ApplicationInfo> modified = new ArrayList();
+
+    /**
+     * Boring constructor.
+     */
+    public AllAppsList() {
+    }
+
+    /**
+     * Add the supplied ApplicationInfo objects to the list, and enqueue it into the
+     * list to broadcast when notify() is called.
+     */
+    public void add(ApplicationInfo info) {
+        data.add(info);
+        added.add(info);
+    }
+    
+    public void clear() {
+        data.clear();
+        // TODO: do we clear these too?
+        added.clear();
+        removed.clear();
+        modified.clear();
+    }
+
+    public int size() {
+        return data.size();
+    }
+
+    public ApplicationInfo get(int index) {
+        return data.get(index);
+    }
+
+    /**
+     * Add the icons for the supplied apk called packageName.
+     */
+    public void addPackage(Context context, String packageName) {
+        final List<ResolveInfo> matches = findActivitiesForPackage(context, packageName);
+
+        if (matches.size() > 0) {
+            Utilities.BubbleText bubble = new Utilities.BubbleText(context);
+            for (ResolveInfo info : matches) {
+                ApplicationInfo item = AppInfoCache.cache(info, context, bubble);
+                data.add(item);
+                added.add(item);
+            }
+        }
+    }
+
+    /**
+     * Remove the apps for the given apk identified by packageName.
+     */
+    public void removePackage(String packageName) {
+        final List<ApplicationInfo> data = this.data;
+        for (int i=data.size()-1; i>=0; i--) {
+            ApplicationInfo info = data.get(i);
+            final ComponentName component = info.intent.getComponent();
+            if (packageName.equals(component.getPackageName())) {
+                removed.add(info);
+                data.remove(i);
+            }
+        }
+        // This is more aggressive than it needs to be.
+        AppInfoCache.flush();
+    }
+    
+    /**
+     * Add and remove icons for this package which has been updated.
+     */
+    public void updatePackage(Context context, String packageName) {
+        final List<ResolveInfo> matches = findActivitiesForPackage(context, packageName);
+        if (matches.size() > 0) {
+            // Find disabled/removed activities and remove them from data and add them
+            // to the removed list.
+            for (int i=data.size()-1; i>=0; i--) {
+                final ApplicationInfo applicationInfo = data.get(i);
+                final ComponentName component = applicationInfo.intent.getComponent();
+                if (packageName.equals(component.getPackageName())) {
+                    if (!findActivity(matches, component)) {
+                        removed.add(applicationInfo);
+                        AppInfoCache.remove(component);
+                        data.remove(i);
+                    }
+                }
+            }
+
+            // Find enabled activities and add them to the adapter
+            // Also updates existing activities with new labels/icons
+            Utilities.BubbleText bubble = new Utilities.BubbleText(context);
+            int count = matches.size();
+            for (int i=0; i<count; i++) {
+                final ResolveInfo info = matches.get(i);
+                ApplicationInfo applicationInfo = findApplicationInfoLocked(
+                        info.activityInfo.applicationInfo.packageName,
+                        info.activityInfo.name);
+                if (applicationInfo == null) {
+                    applicationInfo = AppInfoCache.cache(info, context, bubble);
+                    data.add(applicationInfo);
+                    added.add(applicationInfo);
+                } else {
+                    AppInfoCache.update(info, applicationInfo, context);
+                    modified.add(applicationInfo);
+                }
+            }
+        }
+    }
+
+    /**
+     * Query the package manager for MAIN/LAUNCHER activities in the supplied package.
+     */
+    private static List<ResolveInfo> findActivitiesForPackage(Context context, String packageName) {
+        final PackageManager packageManager = context.getPackageManager();
+
+        final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+        final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
+        final List<ResolveInfo> matches = new ArrayList<ResolveInfo>();
+
+        if (apps != null) {
+            // Find all activities that match the packageName
+            int count = apps.size();
+            for (int i = 0; i < count; i++) {
+                final ResolveInfo info = apps.get(i);
+                final ActivityInfo activityInfo = info.activityInfo;
+                if (packageName.equals(activityInfo.packageName)) {
+                    matches.add(info);
+                }
+            }
+        }
+
+        return matches;
+    }
+
+    /**
+     * Returns whether <em>apps</em> contains <em>component</em>.
+     */
+    private static boolean findActivity(List<ResolveInfo> apps, ComponentName component) {
+        final String className = component.getClassName();
+        for (ResolveInfo info : apps) {
+            final ActivityInfo activityInfo = info.activityInfo;
+            if (activityInfo.name.equals(className)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Find an ApplicationInfo object for the given packageName and className.
+     */
+    private ApplicationInfo findApplicationInfoLocked(String packageName, String className) {
+        for (ApplicationInfo info: data) {
+            final ComponentName component = info.intent.getComponent();
+            if (packageName.equals(component.getPackageName())
+                    && className.equals(component.getClassName())) {
+                return info;
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/com/android/launcher2/AllAppsView.java b/src/com/android/launcher2/AllAppsView.java
index 755ba65..dbb7564 100644
--- a/src/com/android/launcher2/AllAppsView.java
+++ b/src/com/android/launcher2/AllAppsView.java
@@ -59,8 +59,11 @@
 
 
 public class AllAppsView extends RSSurfaceView {
+    private static final String TAG = "Launcher.AllAppsView";
+
     private RenderScript mRS;
     private RolloRS mRollo;
+    private ArrayList<ApplicationInfo> mAllAppsList;
 
     private ViewConfiguration mConfig;
     private VelocityTracker mVelocity;
@@ -97,6 +100,9 @@
         mRS = createRenderScript();
         mRollo = new RolloRS();
         mRollo.init(getResources(), w, h);
+        if (mAllAppsList != null) {
+            mRollo.setApps(mAllAppsList);
+        }
     }
 
     @Override
@@ -109,7 +115,6 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev)
     {
-        Log.d(Launcher.LOG_TAG, "onTouchEvent " + ev);
         int x = (int)ev.getX();
         int deltaX;
         switch (ev.getAction()) {
@@ -163,10 +168,17 @@
 
     DataSetObserver mIconObserver = new DataSetObserver() {
         public void onChanged() {
-            Log.d(Launcher.LOG_TAG, "new icons arrived! now have " + mAdapter.getCount());
+            Log.d(TAG, "new icons arrived! now have " + mAdapter.getCount());
         }
     };
 
+    public void setApps(ArrayList<ApplicationInfo> list) {
+        mAllAppsList = list;
+        if (mRollo != null) {
+            mRollo.setApps(list);
+        }
+    }
+
     public class RolloRS {
 
         // Allocations ======
@@ -316,22 +328,9 @@
         }
         
         private void initData() {
-            final int count = 100;
             mParams = new Params(mRS);
             mState = new State(mRS);
 
-            mIcons = new Allocation[count];
-            mAllocIconIDBuf = new int[count];
-            mAllocIconID = Allocation.createSized(mRS,
-                Element.USER_I32, mAllocIconIDBuf.length);
-
-            mLabels = new Allocation[count];
-            mAllocLabelIDBuf = new int[mLabels.length];
-            mAllocLabelID = Allocation.createSized(mRS,
-                Element.USER_I32, mLabels.length);
-
-            Element ie8888 = Element.RGBA_8888;
-
             final Utilities.BubbleText bubble = new Utilities.BubbleText(getContext());
 
             mParams.bubbleWidth = bubble.getBubbleWidth();
@@ -339,25 +338,10 @@
             mParams.bubbleBitmapWidth = bubble.getBitmapWidth();
             mParams.bubbleBitmapHeight = bubble.getBitmapHeight();
 
-            for (int i=0; i<count; i++) {
-                mIcons[i] = Allocation.createFromBitmapResource(
-                        mRS, mRes, R.raw.maps, ie8888, true);
-                mLabels[i] = makeTextBitmap(bubble, i%9==0 ? "Google Maps" : "Maps");
-            }
-
-            for (int i=0; i<count; i++) {
-                mIcons[i].uploadToTexture(0);
-                mLabels[i].uploadToTexture(0);
-                mAllocIconIDBuf[i] = mIcons[i].getID();
-                mAllocLabelIDBuf[i] = mLabels[i].getID();
-            }
-            mAllocIconID.data(mAllocIconIDBuf);
-            mAllocLabelID.data(mAllocLabelIDBuf);
-
-            mState.iconCount = count;
-
             mParams.save();
             mState.save();
+
+            setApps(null);
         }
 
         Allocation makeTextBitmap(Utilities.BubbleText bubble, String label) {
@@ -383,8 +367,51 @@
 
             mRS.contextBindRootScript(mScript);
         }
-    }
 
+        private void setApps(ArrayList<ApplicationInfo> list) {
+            final int count = list != null ? list.size() : 0;
+            mIcons = new Allocation[count];
+            mAllocIconIDBuf = new int[count];
+            mAllocIconID = Allocation.createSized(mRS, Element.USER_I32, count);
+
+            mLabels = new Allocation[count];
+            mAllocLabelIDBuf = new int[count];
+            mAllocLabelID = Allocation.createSized(mRS, Element.USER_I32, count);
+
+            Element ie8888 = Element.RGBA_8888;
+
+            Utilities.BubbleText bubble = new Utilities.BubbleText(getContext());
+
+            for (int i=0; i<count; i++) {
+                final ApplicationInfo item = list.get(i);
+
+                mIcons[i] = Allocation.createFromBitmap(mRS, item.iconBitmap,
+                        Element.RGBA_8888, true);
+                mLabels[i] = Allocation.createFromBitmap(mRS, item.titleBitmap,
+                        Element.RGBA_8888, true);
+
+                mIcons[i].uploadToTexture(0);
+                mLabels[i].uploadToTexture(0);
+
+                mAllocIconIDBuf[i] = mIcons[i].getID();
+                mAllocLabelIDBuf[i] = mLabels[i].getID();
+            }
+
+            mAllocIconID.data(mAllocIconIDBuf);
+            mAllocLabelID.data(mAllocLabelIDBuf);
+
+            mState.iconCount = count;
+
+            Log.d("AllAppsView", "mScript=" + mScript + " mAllocIconID=" + mAllocIconID);
+
+            if (mScript != null) { // wtf
+                mScript.bindAllocation(mAllocIconID, Defines.ALLOC_ICON_IDS);
+                mScript.bindAllocation(mAllocLabelID, Defines.ALLOC_LABEL_IDS);
+            }
+
+            mState.save();
+        }
+    }
 }
 
 
diff --git a/src/com/android/launcher2/AppInfoCache.java b/src/com/android/launcher2/AppInfoCache.java
new file mode 100644
index 0000000..34e843e
--- /dev/null
+++ b/src/com/android/launcher2/AppInfoCache.java
@@ -0,0 +1,174 @@
+/*
+ * 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.launcher2;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+import android.util.Log;
+import android.os.Process;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Cache of application icons.  Icons can be made from any thread.
+ */
+public class AppInfoCache {
+    private static final String TAG = "Launcher.AppInfoCache";
+
+    private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
+
+    private static final HashMap<ComponentName, ApplicationInfo> sCache =
+            new HashMap<ComponentName, ApplicationInfo>(INITIAL_ICON_CACHE_CAPACITY);
+
+    /**
+     * no public constructor.
+     */
+    private AppInfoCache() {
+    }
+
+    /**
+     * For the given ResolveInfo, return an ApplicationInfo and cache the result for later.
+     */
+    public static ApplicationInfo cache(ResolveInfo info, Context context,
+            Utilities.BubbleText bubble) {
+        synchronized (sCache) {
+            ComponentName componentName = new ComponentName(
+                    info.activityInfo.applicationInfo.packageName,
+                    info.activityInfo.name);
+            ApplicationInfo application = sCache.get(componentName);
+
+            if (application == null) {
+                application = new ApplicationInfo();
+                application.container = ItemInfo.NO_ID;
+
+                updateTitleAndIcon(info, application, context, bubble);
+
+                application.setActivity(componentName,
+                        Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+                sCache.put(componentName, application);
+            }
+
+            return application;
+        }
+    }
+
+    /**
+     * Update the entry in the in the cache with its new metadata.
+     */
+    public static void update(ResolveInfo info, ApplicationInfo applicationInfo, Context context) {
+        synchronized (sCache) {
+            updateTitleAndIcon(info, applicationInfo, context, new Utilities.BubbleText(context));
+
+            ComponentName componentName = new ComponentName(
+                    info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
+            sCache.put(componentName, applicationInfo);
+        }
+    }
+
+    /**
+     * Remove any records for the supplied ComponentName.
+     */
+    public static void remove(ComponentName componentName) {
+        synchronized (sCache) {
+            sCache.remove(componentName);
+        }
+    }
+
+    /**
+     * Empty out the cache.
+     */
+    public static void flush() {
+        synchronized (sCache) {
+            sCache.clear();
+        }
+    }
+
+    /**
+     * Get the icon for the supplied ApplicationInfo.  If that activity already
+     * exists in the cache, use that.
+     */
+    public static Drawable getIconDrawable(PackageManager packageManager, ApplicationInfo info) {
+        final ResolveInfo resolveInfo = packageManager.resolveActivity(info.intent, 0);
+        if (resolveInfo == null) {
+            return null;
+        }
+
+        ComponentName componentName = new ComponentName(
+                resolveInfo.activityInfo.applicationInfo.packageName,
+                resolveInfo.activityInfo.name);
+        ApplicationInfo cached;
+        synchronized (sCache) {
+            cached = sCache.get(componentName);
+        }
+
+        if (cached != null) {
+            return cached.icon;
+        } else {
+            return resolveInfo.activityInfo.loadIcon(packageManager);
+        }
+    }
+
+    /**
+     * Go through the cache and disconnect any of the callbacks in the drawables or we
+     * leak the previous Home screen on orientation change.
+     */
+    public static void unbindDrawables() {
+        synchronized (sCache) {
+            for (ApplicationInfo appInfo: sCache.values()) {
+                appInfo.icon.setCallback(null);
+            }
+        }
+    }
+
+    /**
+     * Update the title and icon.  Don't keep a reference to the context!
+     */
+    private static void updateTitleAndIcon(ResolveInfo info, ApplicationInfo application,
+            Context context, Utilities.BubbleText bubble) {
+        final PackageManager packageManager = context.getPackageManager();
+
+        application.title = info.loadLabel(packageManager);
+        if (application.title == null) {
+            application.title = info.activityInfo.name;
+        }
+
+        Drawable icon = Utilities.createIconThumbnail(info.activityInfo.loadIcon(packageManager),
+                context, true);
+        application.iconBitmap = ((FastBitmapDrawable)icon).getBitmap();
+        application.filtered = true;
+
+        application.titleBitmap = bubble.createTextBitmap(application.title.toString());
+    }
+}
+
diff --git a/src/com/android/launcher2/ApplicationInfo.java b/src/com/android/launcher2/ApplicationInfo.java
index 912f04e..db802c7 100644
--- a/src/com/android/launcher2/ApplicationInfo.java
+++ b/src/com/android/launcher2/ApplicationInfo.java
@@ -34,6 +34,11 @@
     CharSequence title;
 
     /**
+     * A bitmap of the application's text in the bubble.
+     */
+    Bitmap titleBitmap;
+
+    /**
      * The intent used to start the application.
      */
     Intent intent;
@@ -44,6 +49,11 @@
     Drawable icon;
 
     /**
+     * A bitmap version of the application icon.
+     */
+    Bitmap iconBitmap;
+
+    /**
      * When set to true, indicates that the icon has been resized.
      */
     boolean filtered;
@@ -124,4 +134,10 @@
     public String toString() {
         return title.toString();
     }
+
+    @Override
+    void unbind() {
+        super.unbind();
+        icon.setCallback(null);
+    }
 }
diff --git a/src/com/android/launcher2/ApplicationsAdapter.java b/src/com/android/launcher2/ApplicationsAdapter.java
index 98f7c63..fc69703 100644
--- a/src/com/android/launcher2/ApplicationsAdapter.java
+++ b/src/com/android/launcher2/ApplicationsAdapter.java
@@ -28,7 +28,7 @@
 /**
  * GridView adapter to show the list of applications and shortcuts
  */
-public class ApplicationsAdapter extends ArrayAdapter<ApplicationInfo> {
+public class ApplicationsAdapter  extends ArrayAdapter<ApplicationInfo> {
     private final LayoutInflater mInflater;
 
     public ApplicationsAdapter(Context context, ArrayList<ApplicationInfo> apps) {
@@ -45,7 +45,7 @@
         }
 
         if (!info.filtered) {
-            info.icon = Utilities.createIconThumbnail(info.icon, getContext());
+            info.icon = Utilities.createIconThumbnail(info.icon, getContext(), false);
             info.filtered = true;
         }
 
diff --git a/src/com/android/launcher2/BubbleTextView.java b/src/com/android/launcher2/BubbleTextView.java
index de375b6..eaaff1c 100644
--- a/src/com/android/launcher2/BubbleTextView.java
+++ b/src/com/android/launcher2/BubbleTextView.java
@@ -63,7 +63,6 @@
         setFocusable(true);
         mBackground = getBackground();
         setBackgroundDrawable(null);
-        mBackground.setCallback(this);
 
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mPaint.setColor(getContext().getResources().getColor(R.color.bubble_dark_background));
@@ -131,4 +130,16 @@
 
         super.draw(canvas);
     }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mBackground.setCallback(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mBackground.setCallback(null);
+    }
 }
diff --git a/src/com/android/launcher2/DeferredHandler.java b/src/com/android/launcher2/DeferredHandler.java
new file mode 100644
index 0000000..433bf55
--- /dev/null
+++ b/src/com/android/launcher2/DeferredHandler.java
@@ -0,0 +1,104 @@
+/*
+ * 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.launcher2;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.util.Log;
+
+import java.util.LinkedList;
+
+/**
+ * Queue of things to run on a looper thread.  Items posted with {@link #post} will not
+ * be actually enqued on the handler until after the last one has run, to keep from
+ * starving the thread.
+ *
+ * This class is fifo.
+ */
+public class DeferredHandler {
+    private LinkedList<Runnable> mQueue = new LinkedList();
+    private MessageQueue mMessageQueue = Looper.myQueue();
+    private Impl mHandler = new Impl();
+
+    private class Impl extends Handler implements MessageQueue.IdleHandler {
+        public void handleMessage(Message msg) {
+            Runnable r;
+            synchronized (mQueue) {
+                r = mQueue.removeFirst();
+            }
+            r.run();
+            synchronized (mQueue) {
+                scheduleNextLocked();
+            }
+        }
+
+        public boolean queueIdle() {
+            handleMessage(null);
+            return false;
+        }
+    }
+
+    private class IdleRunnable implements Runnable {
+        Runnable mRunnable;
+
+        IdleRunnable(Runnable r) {
+            mRunnable = r;
+        }
+
+        public void run() {
+            mRunnable.run();
+        }
+    }
+
+    public DeferredHandler() {
+    }
+
+    /** Schedule runnable to run after everything that's on the queue right now. */
+    public void post(Runnable runnable) {
+        synchronized (mQueue) {
+            mQueue.add(runnable);
+            if (mQueue.size() == 1) {
+                scheduleNextLocked();
+            }
+        }
+    }
+
+    /** Schedule runnable to run when the queue goes idle. */
+    public void postIdle(final Runnable runnable) {
+        post(new IdleRunnable(runnable));
+    }
+
+    public void cancel() {
+        synchronized (mQueue) {
+            mQueue.clear();
+        }
+    }
+
+    void scheduleNextLocked() {
+        if (mQueue.size() > 0) {
+            Runnable peek = mQueue.getFirst();
+            if (peek instanceof IdleRunnable) {
+                mMessageQueue.addIdleHandler(mHandler);
+            } else {
+                mHandler.sendEmptyMessage(1);
+            }
+        }
+    }
+}
+
diff --git a/src/com/android/launcher2/DeleteZone.java b/src/com/android/launcher2/DeleteZone.java
index 8a2545f..43fb1a6 100644
--- a/src/com/android/launcher2/DeleteZone.java
+++ b/src/com/android/launcher2/DeleteZone.java
@@ -93,24 +93,23 @@
 
         if (item.container == -1) return;
 
-        final LauncherModel model = Launcher.getModel();
         if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
             if (item instanceof LauncherAppWidgetInfo) {
-                model.removeDesktopAppWidget((LauncherAppWidgetInfo) item);
-            } else {
-                model.removeDesktopItem(item);
+                mLauncher.removeAppWidget((LauncherAppWidgetInfo) item);
             }
         } else {
             if (source instanceof UserFolder) {
                 final UserFolder userFolder = (UserFolder) source;
                 final UserFolderInfo userFolderInfo = (UserFolderInfo) userFolder.getInfo();
-                model.removeUserFolderItem(userFolderInfo, item);
+                // item must be an ApplicationInfo otherwise it couldn't have been in the folder
+                // in the first place.
+                userFolderInfo.remove((ApplicationInfo)item);
             }
         }
         if (item instanceof UserFolderInfo) {
             final UserFolderInfo userFolderInfo = (UserFolderInfo)item;
             LauncherModel.deleteUserFolderContentsFromDatabase(mLauncher, userFolderInfo);
-            model.removeUserFolder(userFolderInfo);
+            mLauncher.removeFolder(userFolderInfo);
         } else if (item instanceof LauncherAppWidgetInfo) {
             final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
             final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index 51cf5e1..38bc468 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -273,8 +273,10 @@
      * Call this from a drag source view.
      */
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        Log.d(Launcher.LOG_TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging="
-                + mDragging);
+        if (false) {
+            Log.d(Launcher.LOG_TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging="
+                    + mDragging);
+        }
         final int action = ev.getAction();
 
         final float screenX = ev.getRawX();
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index 85fc3a7..c135fa3 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -48,7 +48,7 @@
 
         final Resources resources = launcher.getResources();
         Drawable d = resources.getDrawable(R.drawable.ic_launcher_folder);
-        d = Utilities.createIconThumbnail(d, launcher);
+        d = Utilities.createIconThumbnail(d, launcher, false);
         icon.mCloseIcon = d;
         icon.mOpenIcon = resources.getDrawable(R.drawable.ic_launcher_folder_open);
         icon.setCompoundDrawablesWithIntrinsicBounds(null, d, null, null);
diff --git a/src/com/android/launcher2/ItemInfo.java b/src/com/android/launcher2/ItemInfo.java
index b371163..f04880d 100644
--- a/src/com/android/launcher2/ItemInfo.java
+++ b/src/com/android/launcher2/ItemInfo.java
@@ -130,4 +130,6 @@
         }
     }
     
+    void unbind() {
+    }
 }
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index e388ac3..40f4b39 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -24,25 +24,20 @@
 import android.app.StatusBarManager;
 import android.app.WallpaperManager;
 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.Intent.ShortcutIconResource;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.TransitionDrawable;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
@@ -74,6 +69,7 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.io.DataOutputStream;
 import java.io.FileNotFoundException;
@@ -83,13 +79,15 @@
 /**
  * Default launcher application.
  */
-public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener {
+public final class Launcher extends Activity
+        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks {
     static final String LOG_TAG = "Launcher";
+    static final String TAG = LOG_TAG;
     static final boolean LOGD = false;
 
-    private static final boolean PROFILE_STARTUP = false;
-    private static final boolean PROFILE_ROTATE = false;
-    private static final boolean DEBUG_USER_INTERFACE = false;
+    static final boolean PROFILE_STARTUP = false;
+    static final boolean PROFILE_ROTATE = false;
+    static final boolean DEBUG_USER_INTERFACE = false;
 
     private static final int WALLPAPER_SCREENS_SPAN = 2;
 
@@ -150,14 +148,11 @@
     // Type: long
     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
 
-    private static final LauncherModel sModel = new LauncherModel();
+    static final int APPWIDGET_HOST_ID = 1024;
 
     private static final Object sLock = new Object();
     private static int sScreen = DEFAULT_SCREN;
 
-    private final BroadcastReceiver mApplicationsReceiver = new ApplicationsIntentReceiver();
-    private final ContentObserver mObserver = new FavoritesChangeObserver();
-
     private LayoutInflater mInflater;
 
     private DragController mDragController;
@@ -166,8 +161,6 @@
     private AppWidgetManager mAppWidgetManager;
     private LauncherAppWidgetHost mAppWidgetHost;
 
-    static final int APPWIDGET_HOST_ID = 1024;
-
     private CellLayout.CellInfo mAddItemCellInfo;
     private CellLayout.CellInfo mMenuAddInfo;
     private final int[] mCellCoordinates = new int[2];
@@ -178,15 +171,14 @@
     private AllAppsView mAllAppsGrid;
     private boolean mAllAppsVisible;
 
-    private boolean mDesktopLocked = true;
     private Bundle mSavedState;
 
     private SpannableStringBuilder mDefaultKeySsb = null;
 
-    private boolean mDestroyed;
-    
     private boolean mIsNewIntent;
 
+    private boolean mWorkspaceLoading = true;
+
     private boolean mRestoring;
     private boolean mWaitingForResult;
     private boolean mLocaleChanged;
@@ -196,12 +188,17 @@
     
     private Bundle mSavedInstanceState;
 
-    private DesktopBinder mBinder;
+    private LauncherModel mModel;
+
+    private ArrayList<ItemInfo> mDesktopItems = new ArrayList();
+    private static HashMap<Long, FolderInfo> mFolders = new HashMap();
+    private ArrayList<ApplicationInfo> mAllAppsList = new ArrayList();
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        mModel = ((LauncherApplication)getApplication()).setLauncher(this);
         mInflater = getLayoutInflater();
 
         mAppWidgetManager = AppWidgetManager.getInstance(this);
@@ -221,9 +218,6 @@
 
         lockAllApps();
 
-        registerIntentReceivers();
-        registerContentObservers();
-
         mSavedState = savedInstanceState;
         restoreState(mSavedState);
 
@@ -232,7 +226,7 @@
         }
 
         if (!mRestoring) {
-            startLoaders();
+            mModel.startLoader(this, true);
         }
 
         // For handling default keys
@@ -263,6 +257,7 @@
             localeConfiguration.mnc = mnc;
 
             writeConfiguration(this, localeConfiguration);
+            AppInfoCache.flush();
         }
     }
 
@@ -330,13 +325,6 @@
         }
     }
 
-    private void startLoaders() {
-        boolean loadApplications = sModel.loadApplications(true, this, mLocaleChanged);
-        sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, loadApplications);
-
-        mRestoring = false;
-    }
-
     private void setWallpaperDimension() {
         WallpaperManager wpm = (WallpaperManager)getSystemService(WALLPAPER_SERVICE);
 
@@ -361,25 +349,25 @@
         if (resultCode == RESULT_OK && mAddItemCellInfo != null) {
             switch (requestCode) {
                 case REQUEST_PICK_APPLICATION:
-                    completeAddApplication(this, data, mAddItemCellInfo, !mDesktopLocked);
+                    completeAddApplication(this, data, mAddItemCellInfo);
                     break;
                 case REQUEST_PICK_SHORTCUT:
                     processShortcut(data, REQUEST_PICK_APPLICATION, REQUEST_CREATE_SHORTCUT);
                     break;
                 case REQUEST_CREATE_SHORTCUT:
-                    completeAddShortcut(data, mAddItemCellInfo, !mDesktopLocked);
+                    completeAddShortcut(data, mAddItemCellInfo);
                     break;
                 case REQUEST_PICK_LIVE_FOLDER:
                     addLiveFolder(data);
                     break;
                 case REQUEST_CREATE_LIVE_FOLDER:
-                    completeAddLiveFolder(data, mAddItemCellInfo, !mDesktopLocked);
+                    completeAddLiveFolder(data, mAddItemCellInfo);
                     break;
                 case REQUEST_PICK_APPWIDGET:
                     addAppWidget(data);
                     break;
                 case REQUEST_CREATE_APPWIDGET:
-                    completeAddAppWidget(data, mAddItemCellInfo, !mDesktopLocked);
+                    completeAddAppWidget(data, mAddItemCellInfo);
                     break;
             }
         } else if (requestCode == REQUEST_PICK_APPWIDGET &&
@@ -396,8 +384,12 @@
     protected void onResume() {
         super.onResume();
 
+        Log.d(TAG, "onResume mRestoring=" + mRestoring);
+
         if (mRestoring) {
-            startLoaders();
+            mWorkspaceLoading = true;
+            mModel.startLoader(this, true);
+            mRestoring = false;
         }
         
         // If this was a new intent (i.e., the mIsNewIntent flag got set to true by
@@ -430,10 +422,8 @@
 
     @Override
     public Object onRetainNonConfigurationInstance() {
-        // Flag any binder to stop early before switching
-        if (mBinder != null) {
-            mBinder.mTerminate = true;
-        }
+        // Flag the loader to stop early before switching
+        mModel.stopLoader();
 
         if (PROFILE_ROTATE) {
             android.os.Debug.startMethodTracing("/sdcard/launcher-rotate");
@@ -512,7 +502,7 @@
         boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
         if (renameFolder) {
             long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
-            mFolderInfo = sModel.getFolderById(this, id);
+            mFolderInfo = mModel.getFolderById(this, mFolders, id);
             mRestoring = true;
         }
     }
@@ -583,7 +573,7 @@
         TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false);
 
         if (!info.filtered) {
-            info.icon = Utilities.createIconThumbnail(info.icon, this);
+            info.icon = Utilities.createIconThumbnail(info.icon, this, false);
             info.filtered = true;
         }
 
@@ -601,14 +591,13 @@
      * @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,
-            boolean insertAtFirst) {
+    void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo) {
         cellInfo.screen = mWorkspace.getCurrentScreen();
         if (!findSingleSlot(cellInfo)) return;
 
         final ApplicationInfo info = infoFromApplicationIntent(context, data);
         if (info != null) {
-            mWorkspace.addApplicationShortcut(info, cellInfo, insertAtFirst);
+            mWorkspace.addApplicationShortcut(info, cellInfo, isWorkspaceLocked());
         }
     }
 
@@ -646,22 +635,17 @@
      *
      * @param data The intent describing the shortcut.
      * @param cellInfo The position on screen where to create the shortcut.
-     * @param insertAtFirst
      */
-    private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo,
-            boolean insertAtFirst) {
+    private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo) {
         cellInfo.screen = mWorkspace.getCurrentScreen();
         if (!findSingleSlot(cellInfo)) return;
 
         final ApplicationInfo info = addShortcut(this, data, cellInfo, false);
 
         if (!mRestoring) {
-            sModel.addDesktopItem(info);
-
             final View view = createShortcut(info);
-            mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
-        } else if (sModel.isDesktopLoaded()) {
-            sModel.addDesktopItem(info);
+            mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
+                    isWorkspaceLocked());
         }
     }
 
@@ -672,9 +656,7 @@
      * @param data The intent describing the appWidgetId.
      * @param cellInfo The position on screen where to create the widget.
      */
-    private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo,
-            boolean insertAtFirst) {
-
+    private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo) {
         Bundle extras = data.getExtras();
         int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
 
@@ -700,7 +682,7 @@
                 mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
 
         if (!mRestoring) {
-            sModel.addDesktopAppWidget(launcherInfo);
+            mDesktopItems.add(launcherInfo);
 
             // Perform actual inflation because we're live
             launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
@@ -709,12 +691,15 @@
             launcherInfo.hostView.setTag(launcherInfo);
 
             mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
-                    launcherInfo.spanX, launcherInfo.spanY, insertAtFirst);
-        } else if (sModel.isDesktopLoaded()) {
-            sModel.addDesktopAppWidget(launcherInfo);
+                    launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
         }
     }
 
+    public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
+        mDesktopItems.remove(launcherInfo);
+        launcherInfo.hostView = null;
+    }
+
     public LauncherAppWidgetHost getAppWidgetHost() {
         return mAppWidgetHost;
     }
@@ -790,7 +775,6 @@
             try {
                 dismissDialog(DIALOG_CREATE_SHORTCUT);
                 // Unlock the workspace if the dialog was showing
-                mWorkspace.unlock();
             } catch (Exception e) {
                 // An exception is thrown if the dialog is not visible, which is fine
             }
@@ -798,7 +782,6 @@
             try {
                 dismissDialog(DIALOG_RENAME_FOLDER);
                 // Unlock the workspace if the dialog was showing
-                mWorkspace.unlock();
             } catch (Exception e) {
                 // An exception is thrown if the dialog is not visible, which is fine
             }
@@ -878,8 +861,6 @@
 
     @Override
     public void onDestroy() {
-        mDestroyed = true;
-
         super.onDestroy();
 
         try {
@@ -891,11 +872,10 @@
         TextKeyListener.getInstance().release();
 
         mAllAppsGrid.setAdapter(null);
-        sModel.unbind();
-        sModel.abortLoaders();
+        mModel.stopLoader();
 
-        getContentResolver().unregisterContentObserver(mObserver);
-        unregisterReceiver(mApplicationsReceiver);
+        unbindDesktopItems();
+        AppInfoCache.unbindDrawables();
     }
 
     @Override
@@ -975,7 +955,9 @@
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
-        if (mDesktopLocked) return false;
+        if (isWorkspaceLocked()) {
+            return false;
+        }
 
         super.onCreateOptionsMenu(menu);
         menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add)
@@ -1043,22 +1025,14 @@
         return true;
     }
 
+    public boolean isWorkspaceLocked() {
+        return mWorkspaceLoading || mWaitingForResult;
+    }
+
     private void addItems() {
         showAddDialog(mMenuAddInfo);
     }
 
-    private void removeShortcutsForPackage(String packageName) {
-        if (packageName != null && packageName.length() > 0) {
-            mWorkspace.removeShortcutsForPackage(packageName);
-        }
-    }
-
-    private void updateShortcutsForPackage(String packageName) {
-        if (packageName != null && packageName.length() > 0) {
-            mWorkspace.updateShortcutsForPackage(packageName);
-        }
-    }
-
     void addAppWidget(Intent data) {
         // TODO: catch bad widget exception when sent
         int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
@@ -1096,7 +1070,6 @@
 
         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);
 
@@ -1131,13 +1104,13 @@
         String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
 
         if (folderName != null && folderName.equals(shortcutName)) {
-            addFolder(!mDesktopLocked);
+            addFolder();
         } else {
             startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER);
         }
     }
 
-    void addFolder(boolean insertAtFirst) {
+    void addFolder() {
         UserFolderInfo folderInfo = new UserFolderInfo();
         folderInfo.title = getText(R.string.folder_name);
 
@@ -1146,33 +1119,33 @@
         if (!findSingleSlot(cellInfo)) return;
 
         // Update the model
-        LauncherModel.addItemToDatabase(this, folderInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP,
+        LauncherModel.addItemToDatabase(this, folderInfo,
+                LauncherSettings.Favorites.CONTAINER_DESKTOP,
                 mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false);
-        sModel.addDesktopItem(folderInfo);
-        sModel.addFolder(folderInfo);
+        mFolders.put(folderInfo.id, folderInfo);
 
         // Create the view
         FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo);
         mWorkspace.addInCurrentScreen(newFolder,
-                cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
+                cellInfo.cellX, cellInfo.cellY, 1, 1, isWorkspaceLocked());
     }
 
-    private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo,
-            boolean insertAtFirst) {
+    void removeFolder(FolderInfo folder) {
+        mFolders.remove(folder.id);
+    }
+
+    private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo) {
         cellInfo.screen = mWorkspace.getCurrentScreen();
         if (!findSingleSlot(cellInfo)) return;
 
         final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
 
         if (!mRestoring) {
-            sModel.addDesktopItem(info);
-
             final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,
                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
-            mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
-        } else if (sModel.isDesktopLoaded()) {
-            sModel.addDesktopItem(info);
+            mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
+                    isWorkspaceLocked());
         }
     }
 
@@ -1216,7 +1189,7 @@
 
         LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
                 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
-        sModel.addFolder(info);
+        mFolders.put(info.id, info);
 
         return info;
     }
@@ -1256,28 +1229,6 @@
         startActivity(Intent.createChooser(pickWallpaper, getString(R.string.chooser_wallpaper)));
     }
 
-    /**
-     * Registers various intent receivers. The current implementation registers
-     * only a wallpaper intent receiver to let other applications change the
-     * wallpaper.
-     */
-    private void registerIntentReceivers() {
-        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addDataScheme("package");
-        registerReceiver(mApplicationsReceiver, filter);
-    }
-
-    /**
-     * Registers various content observers. The current implementation registers
-     * only a favorites observer to keep track of the favorites applications.
-     */
-    private void registerContentObservers() {
-        ContentResolver resolver = getContentResolver();
-        resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true, mObserver);
-    }
-
     @Override
     public void onWindowFocusChanged(boolean hasFocus) {
         super.onWindowFocusChanged(hasFocus);
@@ -1331,202 +1282,19 @@
         ViewGroup parent = (ViewGroup) folder.getParent();
         if (parent != null) {
             parent.removeView(folder);
-            mDragController.removeDropTarget((DropTarget)folder);
+            // TODO: this line crashes.
+            //mDragController.removeDropTarget((DropTarget)folder);
         }
         folder.onClose();
     }
 
     /**
-     * When the notification that favorites have changed is received, requests
-     * a favorites list refresh.
+     * Go through the and disconnect any of the callbacks in the drawables and the views or we
+     * leak the previous Home screen on orientation change.
      */
-    private void onFavoritesChanged() {
-        mDesktopLocked = true;
-        lockAllApps();
-        sModel.loadUserItems(false, this, false, false);
-    }
-
-    void onDesktopItemsLoaded() {
-        if (mDestroyed) return;
-        bindDesktopItems();
-    }
-
-    /**
-     * Refreshes the shortcuts shown on the workspace.
-     */
-    private void bindDesktopItems() {
-        final ArrayList<ItemInfo> shortcuts = sModel.getDesktopItems();
-        final ArrayList<LauncherAppWidgetInfo> appWidgets = sModel.getDesktopAppWidgets();
-        final ApplicationsAdapter drawerAdapter = sModel.getApplicationsAdapter();
-        if (shortcuts == null || appWidgets == null || drawerAdapter == null) {
-            return;
-        }
-
-        final Workspace workspace = mWorkspace;
-        int count = workspace.getChildCount();
-        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();
-                }
-            });
-        }
-
-        // Flag any old binder to terminate early
-        if (mBinder != null) {
-            mBinder.mTerminate = true;
-        }
-
-        mBinder = new DesktopBinder(this, shortcuts, appWidgets, drawerAdapter);
-        mBinder.startBindingItems();
-    }
-
-    private void bindItems(Launcher.DesktopBinder binder,
-            ArrayList<ItemInfo> shortcuts, int start, int count) {
-
-        final Workspace workspace = mWorkspace;
-        final boolean desktopLocked = mDesktopLocked;
-
-        final int end = Math.min(start + DesktopBinder.ITEMS_COUNT, count);
-        int i = start;
-
-        for ( ; i < end; i++) {
-            final ItemInfo item = shortcuts.get(i);
-            switch (item.itemType) {
-                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                    final View shortcut = createShortcut((ApplicationInfo) item);
-                    workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1,
-                            !desktopLocked);
-                    break;
-                case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
-                    final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
-                            (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
-                            (UserFolderInfo) item);
-                    workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,
-                            !desktopLocked);
-                    break;
-                case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
-                    final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(
-                            R.layout.live_folder_icon, this,
-                            (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
-                            (LiveFolderInfo) item);
-                    workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
-                            !desktopLocked);
-                    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);
-
-                    Search search = (Search) view.findViewById(R.id.widget_search);
-                    search.setLauncher(this);
-
-                    final Widget widget = (Widget) item;
-                    view.setTag(widget);
-
-                    workspace.addWidget(view, widget, !desktopLocked);
-                    break;
-            }
-        }
-
-        workspace.requestLayout();
-
-        if (end >= count) {
-            finishBindDesktopItems();
-            binder.startBindingDrawer();
-        } else {
-            binder.obtainMessage(DesktopBinder.MESSAGE_BIND_ITEMS, i, count).sendToTarget();
-        }
-    }
-
-    private void finishBindDesktopItems() {
-        if (mSavedState != null) {
-            if (!mWorkspace.hasFocus()) {
-                mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
-            }
-
-            final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
-            if (userFolders != null) {
-                for (long folderId : userFolders) {
-                    final FolderInfo info = sModel.findFolderById(folderId);
-                    if (info != null) {
-                        openFolder(info);
-                    }
-                }
-                final Folder openFolder = mWorkspace.getOpenFolder();
-                if (openFolder != null) {
-                    openFolder.requestFocus();
-                }
-            }
-
-            final boolean allApps = mSavedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false);
-            if (allApps) {
-                showAllAppsDialog();
-            }
-
-            mSavedState = null;
-        }
-
-        if (mSavedInstanceState != null) {
-            super.onRestoreInstanceState(mSavedInstanceState);
-            mSavedInstanceState = null;
-        }
-
-        /* TODO
-        if (mAllAppsVisible && !mDrawer.hasFocus()) {
-            mDrawer.requestFocus();
-        }
-        */
-
-        mDesktopLocked = false;
-        unlockAllApps();
-    }
-
-    private void bindDrawer(Launcher.DesktopBinder binder,
-            ApplicationsAdapter drawerAdapter) {
-        mAllAppsGrid.setAdapter(drawerAdapter);
-        binder.startBindingAppWidgetsWhenIdle();
-    }
-
-    private void bindAppWidgets(Launcher.DesktopBinder binder,
-            LinkedList<LauncherAppWidgetInfo> appWidgets) {
-
-        final Workspace workspace = mWorkspace;
-        final boolean desktopLocked = mDesktopLocked;
-
-        if (!appWidgets.isEmpty()) {
-            final LauncherAppWidgetInfo item = appWidgets.removeFirst();
-
-            final int appWidgetId = item.appWidgetId;
-            final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
-            item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
-
-            if (LOGD) d(LOG_TAG, String.format("about to setAppWidget for id=%d, info=%s", appWidgetId, appWidgetInfo));
-
-            item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
-            item.hostView.setTag(item);
-
-            workspace.addInScreen(item.hostView, item.screen, item.cellX,
-                    item.cellY, item.spanX, item.spanY, !desktopLocked);
-
-            workspace.requestLayout();
-        }
-
-        if (appWidgets.isEmpty()) {
-            if (PROFILE_ROTATE) {
-                android.os.Debug.stopMethodTracing();
-            }
-        } else {
-            binder.obtainMessage(DesktopBinder.MESSAGE_BIND_APPWIDGETS).sendToTarget();
+    private void unbindDesktopItems() {
+        for (ItemInfo item: mDesktopItems) {
+            item.unbind();
         }
     }
 
@@ -1618,18 +1386,8 @@
         openFolder.onOpen();
     }
 
-    /**
-     * Returns true if the workspace is being loaded. When the workspace is loading,
-     * no user interaction should be allowed to avoid any conflict.
-     *
-     * @return True if the workspace is locked, false otherwise.
-     */
-    boolean isWorkspaceLocked() {
-        return mDesktopLocked;
-    }
-
     public boolean onLongClick(View v) {
-        if (mDesktopLocked) {
+        if (isWorkspaceLocked()) {
             return false;
         }
 
@@ -1661,10 +1419,6 @@
         return true;
     }
 
-    static LauncherModel getModel() {
-        return sModel;
-    }
-
     View getDrawerHandle() {
         return mHandleView;
     }
@@ -1779,7 +1533,6 @@
             final AlertDialog dialog = builder.create();
             dialog.setOnShowListener(new DialogInterface.OnShowListener() {
                 public void onShow(DialogInterface dialog) {
-                    mWorkspace.lock();
                 }
             });
 
@@ -1790,13 +1543,14 @@
             final String name = mInput.getText().toString();
             if (!TextUtils.isEmpty(name)) {
                 // Make sure we have the right folder info
-                mFolderInfo = sModel.findFolderById(mFolderInfo.id);
+                mFolderInfo = mFolders.get(mFolderInfo.id);
                 mFolderInfo.title = name;
                 LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo);
 
-                if (mDesktopLocked) {
+                if (mWorkspaceLoading) {
                     lockAllApps();
-                    sModel.loadUserItems(false, Launcher.this, false, false);
+                    mModel.setWorkspaceDirty();
+                    mModel.startLoader(Launcher.this, false);
                 } else {
                     final FolderIcon folderIcon = (FolderIcon)
                             mWorkspace.getViewForTag(mFolderInfo);
@@ -1804,9 +1558,10 @@
                         folderIcon.setText(name);
                         getWorkspace().requestLayout();
                     } else {
-                        mDesktopLocked = true;
                         lockAllApps();
-                        sModel.loadUserItems(false, Launcher.this, false, false);
+                        mModel.setWorkspaceDirty();
+                        mWorkspaceLoading = true;
+                        mModel.startLoader(Launcher.this, false);
                     }
                 }
             }
@@ -1814,7 +1569,6 @@
         }
 
         private void cleanup() {
-            mWorkspace.unlock();
             dismissDialog(DIALOG_RENAME_FOLDER);
             mWaitingForResult = false;
             mFolderInfo = null;
@@ -1893,11 +1647,9 @@
         }
 
         public void onDismiss(DialogInterface dialog) {
-            mWorkspace.unlock();
         }
 
         private void cleanup() {
-            mWorkspace.unlock();
             dismissDialog(DIALOG_CREATE_SHORTCUT);
         }
 
@@ -1974,158 +1726,215 @@
         }
 
         public void onShow(DialogInterface dialog) {
-            mWorkspace.lock();
         }
     }
 
     /**
-     * Receives notifications when applications are added/removed.
+     * Implementation of the method from LauncherModel.Callbacks.
      */
-    private class ApplicationsIntentReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            final String packageName = intent.getData().getSchemeSpecificPart();
-            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+    public int getCurrentWorkspaceScreen() {
+        return mWorkspace.getCurrentScreen();
+    }
 
-            if (LauncherModel.DEBUG_LOADERS) {
-                d(LauncherModel.LOG_TAG, "application intent received: " + action +
-                        ", replacing=" + replacing);
-                d(LauncherModel.LOG_TAG, "  --> " + intent.getData());
-            }
+    /**
+     * Refreshes the shortcuts shown on the workspace.
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
+     */
+    public void startBinding() {
+        final Workspace workspace = mWorkspace;
+        int count = workspace.getChildCount();
+        for (int i = 0; i < count; i++) {
+            ((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout();
+        }
 
-            if (!Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
-                if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                    if (!replacing) {
-                        removeShortcutsForPackage(packageName);
-                        if (LauncherModel.DEBUG_LOADERS) {
-                            d(LauncherModel.LOG_TAG, "  --> remove package");
-                        }
-                        sModel.removePackage(Launcher.this, packageName);
-                    }
-                    // else, we are replacing the package, so a PACKAGE_ADDED will be sent
-                    // later, we will update the package at this time
-                } else {
-                    if (!replacing) {
-                        if (LauncherModel.DEBUG_LOADERS) {
-                            d(LauncherModel.LOG_TAG, "  --> add package");
-                        }
-                        sModel.addPackage(Launcher.this, packageName);
-                    } else {
-                        if (LauncherModel.DEBUG_LOADERS) {
-                            d(LauncherModel.LOG_TAG, "  --> update package " + packageName);
-                        }
-                        sModel.updatePackage(Launcher.this, packageName);
-                        updateShortcutsForPackage(packageName);
-                    }
+        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();
                 }
-                removeDialog(DIALOG_CREATE_SHORTCUT);
-            } else {
-                if (LauncherModel.DEBUG_LOADERS) {
-                    d(LauncherModel.LOG_TAG, "  --> sync package " + packageName);
-                }
-                sModel.syncPackage(Launcher.this, packageName);
-            }
+            });
         }
     }
 
     /**
-     * Receives notifications whenever the user favorites have changed.
+     * Bind the items start-end from the list.
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
      */
-    private class FavoritesChangeObserver extends ContentObserver {
-        public FavoritesChangeObserver() {
-            super(new Handler());
+    public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) {
+
+        final Workspace workspace = mWorkspace;
+
+        for (int i=start; i<end; i++) {
+            final ItemInfo item = shortcuts.get(i);
+            mDesktopItems.add(item);
+            switch (item.itemType) {
+                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                    final View shortcut = createShortcut((ApplicationInfo) item);
+                    workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1,
+                            false);
+                    break;
+                case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
+                    final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
+                            (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
+                            (UserFolderInfo) item);
+                    workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,
+                            false);
+                    break;
+                case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
+                    final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(
+                            R.layout.live_folder_icon, this,
+                            (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
+                            (LiveFolderInfo) item);
+                    workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
+                            false);
+                    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);
+
+                    Search search = (Search) view.findViewById(R.id.widget_search);
+                    search.setLauncher(this);
+
+                    final Widget widget = (Widget) item;
+                    view.setTag(widget);
+
+                    workspace.addWidget(view, widget, false);
+                    break;
+            }
         }
 
-        @Override
-        public void onChange(boolean selfChange) {
-            onFavoritesChanged();
-        }
+        workspace.requestLayout();
     }
 
-    private static class DesktopBinder extends Handler implements MessageQueue.IdleHandler {
-        static final int MESSAGE_BIND_ITEMS = 0x1;
-        static final int MESSAGE_BIND_APPWIDGETS = 0x2;
-        static final int MESSAGE_BIND_DRAWER = 0x3;
-
-        // Number of items to bind in every pass
-        static final int ITEMS_COUNT = 6;
-
-        private final ArrayList<ItemInfo> mShortcuts;
-        private final LinkedList<LauncherAppWidgetInfo> mAppWidgets;
-        private final ApplicationsAdapter mDrawerAdapter;
-        private final WeakReference<Launcher> mLauncher;
-
-        public boolean mTerminate = false;
-
-        DesktopBinder(Launcher launcher, ArrayList<ItemInfo> shortcuts,
-                ArrayList<LauncherAppWidgetInfo> appWidgets,
-                ApplicationsAdapter drawerAdapter) {
-
-            mLauncher = new WeakReference<Launcher>(launcher);
-            mShortcuts = shortcuts;
-            mDrawerAdapter = drawerAdapter;
-
-            // Sort widgets so active workspace is bound first
-            final int currentScreen = launcher.mWorkspace.getCurrentScreen();
-            final int size = appWidgets.size();
-            mAppWidgets = new LinkedList<LauncherAppWidgetInfo>();
-
-            for (int i = 0; i < size; i++) {
-                LauncherAppWidgetInfo appWidgetInfo = appWidgets.get(i);
-                if (appWidgetInfo.screen == currentScreen) {
-                    mAppWidgets.addFirst(appWidgetInfo);
-                } else {
-                    mAppWidgets.addLast(appWidgetInfo);
-                }
-            }
-        }
-
-        public void startBindingItems() {
-            obtainMessage(MESSAGE_BIND_ITEMS, 0, mShortcuts.size()).sendToTarget();
-        }
-
-        public void startBindingDrawer() {
-            obtainMessage(MESSAGE_BIND_DRAWER).sendToTarget();
-        }
-
-        public void startBindingAppWidgetsWhenIdle() {
-            // Ask for notification when message queue becomes idle
-            final MessageQueue messageQueue = Looper.myQueue();
-            messageQueue.addIdleHandler(this);
-        }
-
-        public boolean queueIdle() {
-            // Queue is idle, so start binding items
-            startBindingAppWidgets();
-            return false;
-        }
-
-        public void startBindingAppWidgets() {
-            obtainMessage(MESSAGE_BIND_APPWIDGETS).sendToTarget();
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            Launcher launcher = mLauncher.get();
-            if (launcher == null || mTerminate) {
-                return;
-            }
-
-            switch (msg.what) {
-                case MESSAGE_BIND_ITEMS: {
-                    launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2);
-                    break;
-                }
-                case MESSAGE_BIND_DRAWER: {
-                    launcher.bindDrawer(this, mDrawerAdapter);
-                    break;
-                }
-                case MESSAGE_BIND_APPWIDGETS: {
-                    launcher.bindAppWidgets(this, mAppWidgets);
-                    break;
-                }
-            }
-        }
+    /**
+     * Implementation of the method from LauncherModel.Callbacks.
+     */
+    void bindFolders(HashMap<Long, FolderInfo> folders) {
+        mFolders.putAll(folders);
     }
+
+    /**
+     * Add the views for a widget to the workspace.
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
+     */
+    public void bindAppWidget(LauncherAppWidgetInfo item) {
+        final Workspace workspace = mWorkspace;
+
+        final int appWidgetId = item.appWidgetId;
+        final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+        item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+
+        if (true) {
+            Log.d(LOG_TAG, String.format("about to setAppWidget for id=%d, info=%s",
+                    appWidgetId, appWidgetInfo));
+        }
+
+        item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
+        item.hostView.setTag(item);
+
+        workspace.addInScreen(item.hostView, item.screen, item.cellX,
+                item.cellY, item.spanX, item.spanY, false);
+
+        workspace.requestLayout();
+
+        mDesktopItems.add(item);
+    }
+
+    /**
+     * Callback saying that there aren't any more items to bind.
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
+     */
+    public void finishBindingItems() {
+        if (mSavedState != null) {
+            if (!mWorkspace.hasFocus()) {
+                mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
+            }
+
+            final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
+            if (userFolders != null) {
+                for (long folderId : userFolders) {
+                    final FolderInfo info = mFolders.get(folderId);
+                    if (info != null) {
+                        openFolder(info);
+                    }
+                }
+                final Folder openFolder = mWorkspace.getOpenFolder();
+                if (openFolder != null) {
+                    openFolder.requestFocus();
+                }
+            }
+
+            final boolean allApps = mSavedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false);
+            if (allApps) {
+                showAllAppsDialog();
+            }
+
+            mSavedState = null;
+        }
+
+        if (mSavedInstanceState != null) {
+            super.onRestoreInstanceState(mSavedInstanceState);
+            mSavedInstanceState = null;
+        }
+
+        /* TODO
+        if (mAllAppsVisible && !mDrawer.hasFocus()) {
+            mDrawer.requestFocus();
+        }
+        */
+
+        Log.d(TAG, "finishBindingItems done");
+        mWorkspaceLoading = false;
+    }
+
+    /**
+     * Add the icons for all apps.
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
+     */
+    public void bindAllApplications(ArrayList<ApplicationInfo> apps) {
+        Log.d(LOG_TAG, "got info for " + apps.size() + " apps");
+        mAllAppsList = apps;
+        mAllAppsGrid.setApps(mAllAppsList);
+    }
+
+    /**
+     * A package was installed.
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
+     */
+    public void bindPackageAdded(ArrayList<ApplicationInfo> apps) {
+        removeDialog(DIALOG_CREATE_SHORTCUT);
+    }
+
+    /**
+     * A package was updated.
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
+     */
+    public void bindPackageUpdated(String packageName, ArrayList<ApplicationInfo> apps) {
+        removeDialog(DIALOG_CREATE_SHORTCUT);
+        mWorkspace.updateShortcutsForPackage(packageName);
+    }
+
+    /**
+     * A package was uninstalled.
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
+     */
+    public void bindPackageRemoved(String packageName, ArrayList<ApplicationInfo> apps) {
+        removeDialog(DIALOG_CREATE_SHORTCUT);
+        mWorkspace.removeShortcutsForPackage(packageName);
+    }
+
 }
diff --git a/src/com/android/launcher2/LauncherAppWidgetInfo.java b/src/com/android/launcher2/LauncherAppWidgetInfo.java
index cd46434..25db72b 100644
--- a/src/com/android/launcher2/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher2/LauncherAppWidgetInfo.java
@@ -50,4 +50,11 @@
     public String toString() {
         return Integer.toString(appWidgetId);
     }
+
+
+    @Override
+    void unbind() {
+        super.unbind();
+        hostView = null;
+    }
 }
diff --git a/src/com/android/launcher2/LauncherApplication.java b/src/com/android/launcher2/LauncherApplication.java
index 03fe562..ee6006c 100644
--- a/src/com/android/launcher2/LauncherApplication.java
+++ b/src/com/android/launcher2/LauncherApplication.java
@@ -17,13 +17,74 @@
 package com.android.launcher2;
 
 import android.app.Application;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.os.Handler;
 import dalvik.system.VMRuntime;
 
 public class LauncherApplication extends Application {
+    public static final LauncherModel sModel = new LauncherModel();
+
     @Override
     public void onCreate() {
         VMRuntime.getRuntime().setMinimumHeapSize(4 * 1024 * 1024);
 
         super.onCreate();
+
+        // Register intent receivers
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addDataScheme("package");
+        registerReceiver(mApplicationsReceiver, filter);
+
+        // Register for changes to the favorites
+        ContentResolver resolver = getContentResolver();
+        resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
+                mFavoritesObserver);
+    }
+
+    /**
+     * There's no guarantee that this function is ever called.
+     */
+    @Override
+    public void onTerminate() {
+        super.onTerminate();
+
+        unregisterReceiver(mApplicationsReceiver);
+
+        ContentResolver resolver = getContentResolver();
+        resolver.unregisterContentObserver(mFavoritesObserver);
+    }
+
+    /**
+     * Receives notifications when applications are added/removed.
+     */
+    private final BroadcastReceiver mApplicationsReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            sModel.onReceiveIntent(LauncherApplication.this, intent);
+        }
+    };
+
+    /**
+     * Receives notifications whenever the user favorites have changed.
+     */
+    private final ContentObserver mFavoritesObserver = new ContentObserver(new Handler()) {
+        @Override
+        public void onChange(boolean selfChange) {
+            // TODO: lockAllApps();
+            sModel.setWorkspaceDirty();
+            sModel.startLoader(LauncherApplication.this, false);
+        }
+    };
+
+    LauncherModel setLauncher(Launcher launcher) {
+        sModel.initialize(launcher);
+        return sModel;
     }
 }
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 7676b84..36b2090 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -31,15 +31,18 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import static android.util.Log.*;
+import android.util.Log;
 import android.os.Process;
+import android.os.SystemClock;
 
+import java.lang.ref.WeakReference;
+import java.net.URISyntaxException;
+import java.text.Collator;
 import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Comparator;
-import java.lang.ref.WeakReference;
-import java.text.Collator;
-import java.net.URISyntaxException;
 
 /**
  * Maintains in-memory state of the Launcher. It is expected that there should be only one
@@ -48,583 +51,1013 @@
  */
 public class LauncherModel {
     static final boolean DEBUG_LOADERS = true;
-    static final String LOG_TAG = "HomeLoaders";
+    static final String TAG = "Launcher.Model";
 
-    private static final int UI_NOTIFICATION_RATE = 4;
-    private static final int DEFAULT_APPLICATIONS_NUMBER = 42;
-    private static final long APPLICATION_NOT_RESPONDING_TIMEOUT = 5000;
-    private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
+    private final Object mLock = new Object();
+    private DeferredHandler mHandler = new DeferredHandler();
+    private Loader mLoader = new Loader();
 
-    private static final Collator sCollator = Collator.getInstance();
+    private WeakReference<Callbacks> mCallbacks;
 
-    private boolean mApplicationsLoaded;
-    private boolean mDesktopItemsLoaded;
+    private AllAppsList mAllAppsList = new AllAppsList();
 
-    private ArrayList<ItemInfo> mDesktopItems;
-    private ArrayList<LauncherAppWidgetInfo> mDesktopAppWidgets;
-    private HashMap<Long, FolderInfo> mFolders;
+    public interface Callbacks {
+        public int getCurrentWorkspaceScreen();
+        public void startBinding();
+        public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
+        public void finishBindingItems();
+        public void bindAppWidget(LauncherAppWidgetInfo info);
+        public void bindAllApplications(ArrayList<ApplicationInfo> apps);
+        public void bindPackageAdded(ArrayList<ApplicationInfo> apps);
+        public void bindPackageUpdated(String packageName, ArrayList<ApplicationInfo> apps);
+        public void bindPackageRemoved(String packageName, ArrayList<ApplicationInfo> apps);
+    }
 
-    private ArrayList<ApplicationInfo> mApplications;
-    private ApplicationsAdapter mApplicationsAdapter;
-    private ApplicationsLoader mApplicationsLoader;
-    private DesktopItemsLoader mDesktopItemsLoader;
-    private Thread mApplicationsLoaderThread;
-    private Thread mDesktopLoaderThread;
 
-    private final HashMap<ComponentName, ApplicationInfo> mAppInfoCache =
-            new HashMap<ComponentName, ApplicationInfo>(INITIAL_ICON_CACHE_CAPACITY);
-
-    synchronized void abortLoaders() {
-        if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
-            mApplicationsLoader.stop();
-            mApplicationsLoaded = false;
-        }
-
-        if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
-            mDesktopItemsLoader.stop();
-            mDesktopItemsLoaded = false;
+    /**
+     * Adds an item to the DB if it was not created previously, or move it to a new
+     * <container, screen, cellX, cellY>
+     */
+    static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
+            int screen, int cellX, int cellY) {
+        if (item.container == ItemInfo.NO_ID) {
+            // From all apps
+            addItemToDatabase(context, item, container, screen, cellX, cellY, false);
+        } else {
+            // From somewhere else
+            moveItemInDatabase(context, item, container, screen, cellX, cellY);
         }
     }
 
     /**
-     * Drop our cache of components to their lables & icons.  We do
-     * this from Launcher when applications are added/removed.  It's a
-     * bit overkill, but it's a rare operation anyway.
+     * Move an item in the DB to a new <container, screen, cellX, cellY>
      */
-    synchronized void dropApplicationCache() {
-        mAppInfoCache.clear();
+    static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
+            int cellX, int cellY) {
+        item.container = container;
+        item.screen = screen;
+        item.cellX = cellX;
+        item.cellY = cellY;
+
+        final ContentValues values = new ContentValues();
+        final ContentResolver cr = context.getContentResolver();
+
+        values.put(LauncherSettings.Favorites.CONTAINER, item.container);
+        values.put(LauncherSettings.Favorites.CELLX, item.cellX);
+        values.put(LauncherSettings.Favorites.CELLY, item.cellY);
+        values.put(LauncherSettings.Favorites.SCREEN, item.screen);
+
+        cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
     }
 
     /**
-     * Loads the list of installed applications in mApplications.
-     *
-     * @return true if the applications loader must be started
-     *         (see startApplicationsLoader()), false otherwise.
+     * Returns true if the shortcuts already exists in the database.
+     * we identify a shortcut by its title and intent.
      */
-    synchronized boolean loadApplications(boolean isLaunching, Launcher launcher,
-            boolean localeChanged) {
-
-        if (DEBUG_LOADERS) d(LOG_TAG, "load applications");
-
-        if (isLaunching && mApplicationsLoaded && !localeChanged) {
-            mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
-            if (DEBUG_LOADERS) d(LOG_TAG, "  --> applications loaded, return");
-            return false;
+    static boolean shortcutExists(Context context, String title, Intent intent) {
+        final ContentResolver cr = context.getContentResolver();
+        Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
+            new String[] { "title", "intent" }, "title=? and intent=?",
+            new String[] { title, intent.toUri(0) }, null);
+        boolean result = false;
+        try {
+            result = c.moveToFirst();
+        } finally {
+            c.close();
         }
-
-        stopAndWaitForApplicationsLoader();
-
-        if (localeChanged) {
-            dropApplicationCache();
-        }
-
-        if (mApplicationsAdapter == null || isLaunching || localeChanged) {
-            mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);
-            mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
-        }
-
-        mApplicationsLoaded = false;
-
-        if (!isLaunching) {
-            startApplicationsLoader(launcher, false);
-            return false;
-        }
-
-        return true;
+        return result;
     }
 
-    private synchronized void stopAndWaitForApplicationsLoader() {
-        if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
-            if (DEBUG_LOADERS) d(LOG_TAG, "  --> wait for applications loader");
+    /**
+     * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
+     */
+    FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
+        final ContentResolver cr = context.getContentResolver();
+        Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
+                "_id=? and (itemType=? or itemType=?)",
+                new String[] { String.valueOf(id),
+                        String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
+                        String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
 
-            mApplicationsLoader.stop();
-            // Wait for the currently running thread to finish, this can take a little
-            // time but it should be well below the timeout limit
-            try {
-                mApplicationsLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
-            } catch (InterruptedException e) {
-                // EMpty
-            }
-        }
-    }
+        try {
+            if (c.moveToFirst()) {
+                final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
+                final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
+                final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
+                final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
+                final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
+                final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
 
-    private synchronized void startApplicationsLoader(Launcher launcher, boolean isLaunching) {
-        if (DEBUG_LOADERS) d(LOG_TAG, "  --> starting applications loader");
-
-        stopAndWaitForApplicationsLoader();
-
-        mApplicationsLoader = new ApplicationsLoader(launcher, isLaunching);
-        mApplicationsLoaderThread = new Thread(mApplicationsLoader, "Applications Loader");
-        mApplicationsLoaderThread.start();
-    }
-
-    synchronized void addPackage(Launcher launcher, String packageName) {
-        if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
-            startApplicationsLoader(launcher, false);
-            return;
-        }
-
-        if (packageName != null && packageName.length() > 0) {
-            final PackageManager packageManager = launcher.getPackageManager();
-            final List<ResolveInfo> matches = findActivitiesForPackage(packageManager, packageName);
-
-            if (matches.size() > 0) {
-                final ApplicationsAdapter adapter = mApplicationsAdapter;
-                final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache;
-
-                for (ResolveInfo info : matches) {
-                    adapter.setNotifyOnChange(false);
-                    adapter.add(makeAndCacheApplicationInfo(packageManager, cache, info, launcher));
+                FolderInfo folderInfo = null;
+                switch (c.getInt(itemTypeIndex)) {
+                    case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
+                        folderInfo = findOrMakeUserFolder(folderList, id);
+                        break;
+                    case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
+                        folderInfo = findOrMakeLiveFolder(folderList, id);
+                        break;
                 }
 
-                adapter.sort(new ApplicationInfoComparator());
-                adapter.notifyDataSetChanged();
+                folderInfo.title = c.getString(titleIndex);
+                folderInfo.id = id;
+                folderInfo.container = c.getInt(containerIndex);
+                folderInfo.screen = c.getInt(screenIndex);
+                folderInfo.cellX = c.getInt(cellXIndex);
+                folderInfo.cellY = c.getInt(cellYIndex);
+
+                return folderInfo;
             }
-        }
-    }
-
-    synchronized void removePackage(Launcher launcher, String packageName) {
-        if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
-            dropApplicationCache(); // TODO: this could be optimized
-            startApplicationsLoader(launcher, false);
-            return;
-        }
-
-        if (packageName != null && packageName.length() > 0) {
-            final ApplicationsAdapter adapter = mApplicationsAdapter;
-
-            final List<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>();
-            final int count = adapter.getCount();
-
-            for (int i = 0; i < count; i++) {
-                final ApplicationInfo applicationInfo = adapter.getItem(i);
-                final Intent intent = applicationInfo.intent;
-                final ComponentName component = intent.getComponent();
-                if (packageName.equals(component.getPackageName())) {
-                    toRemove.add(applicationInfo);
-                }
-            }
-
-            final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache;
-            for (ApplicationInfo info : toRemove) {
-                adapter.setNotifyOnChange(false);
-                adapter.remove(info);
-                cache.remove(info.intent.getComponent());
-            }
-
-            if (toRemove.size() > 0) {
-                adapter.sort(new ApplicationInfoComparator());
-                adapter.notifyDataSetChanged();
-            }
-        }
-    }
-
-    synchronized void updatePackage(Launcher launcher, String packageName) {
-        if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
-            startApplicationsLoader(launcher, false);
-            return;
-        }
-
-        if (packageName != null && packageName.length() > 0) {
-            final PackageManager packageManager = launcher.getPackageManager();
-            final ApplicationsAdapter adapter = mApplicationsAdapter;
-
-            final List<ResolveInfo> matches = findActivitiesForPackage(packageManager, packageName);
-            final int count = matches.size();
-
-            boolean changed = false;
-
-            for (int i = 0; i < count; i++) {
-                final ResolveInfo info = matches.get(i);
-                final ApplicationInfo applicationInfo = findIntent(adapter,
-                        info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
-                if (applicationInfo != null) {
-                    updateAndCacheApplicationInfo(packageManager, info, applicationInfo, launcher);
-                    changed = true;
-                }
-            }
-
-            if (syncLocked(launcher, packageName)) changed = true;
-
-            if (changed) {
-                adapter.sort(new ApplicationInfoComparator());
-                adapter.notifyDataSetChanged();
-            }
-        }
-    }
-
-    private void updateAndCacheApplicationInfo(PackageManager packageManager, ResolveInfo info,
-            ApplicationInfo applicationInfo, Context context) {
-
-        updateApplicationInfoTitleAndIcon(packageManager, info, applicationInfo, context);
-
-        ComponentName componentName = new ComponentName(
-                info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
-        mAppInfoCache.put(componentName, applicationInfo);
-    }
-
-    synchronized void syncPackage(Launcher launcher, String packageName) {
-        if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
-            startApplicationsLoader(launcher, false);
-            return;
-        }
-
-        if (packageName != null && packageName.length() > 0) {
-            if (syncLocked(launcher, packageName)) {
-                final ApplicationsAdapter adapter = mApplicationsAdapter;
-                adapter.sort(new ApplicationInfoComparator());
-                adapter.notifyDataSetChanged();                
-            }
-        }
-    }
-
-    private boolean syncLocked(Launcher launcher, String packageName) {
-        final PackageManager packageManager = launcher.getPackageManager();
-        final List<ResolveInfo> matches = findActivitiesForPackage(packageManager, packageName);
-
-        if (matches.size() > 0) {
-            final ApplicationsAdapter adapter = mApplicationsAdapter;
-
-            // Find disabled activities and remove them from the adapter
-            boolean removed = removeDisabledActivities(packageName, matches, adapter);
-            // Find enable activities and add them to the adapter
-            // Also updates existing activities with new labels/icons
-            boolean added = addEnabledAndUpdateActivities(matches, adapter, launcher);
-
-            return added || removed;
-        }
-
-        return false;
-    }
-
-    private static List<ResolveInfo> findActivitiesForPackage(PackageManager packageManager,
-            String packageName) {
-
-        final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
-        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
-        final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
-        final List<ResolveInfo> matches = new ArrayList<ResolveInfo>();
-
-        if (apps != null) {
-            // Find all activities that match the packageName
-            int count = apps.size();
-            for (int i = 0; i < count; i++) {
-                final ResolveInfo info = apps.get(i);
-                final ActivityInfo activityInfo = info.activityInfo;
-                if (packageName.equals(activityInfo.packageName)) {
-                    matches.add(info);
-                }
-            }
-        }
-
-        return matches;
-    }
-
-    private boolean addEnabledAndUpdateActivities(List<ResolveInfo> matches,
-            ApplicationsAdapter adapter, Launcher launcher) {
-
-        final List<ApplicationInfo> toAdd = new ArrayList<ApplicationInfo>();
-        final int count = matches.size();
-
-        boolean changed = false;
-
-        for (int i = 0; i < count; i++) {
-            final ResolveInfo info = matches.get(i);
-            final ApplicationInfo applicationInfo = findIntent(adapter,
-                    info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
-            if (applicationInfo == null) {
-                toAdd.add(makeAndCacheApplicationInfo(launcher.getPackageManager(),
-                        mAppInfoCache, info, launcher));
-                changed = true;
-            } else {
-                updateAndCacheApplicationInfo(
-                        launcher.getPackageManager(), info, applicationInfo, launcher);
-                changed = true;
-            }
-        }
-
-        for (ApplicationInfo info : toAdd) {
-            adapter.setNotifyOnChange(false);
-            adapter.add(info);
-        }
-
-        return changed;
-    }
-
-    private boolean removeDisabledActivities(String packageName, List<ResolveInfo> matches,
-            ApplicationsAdapter adapter) {
-
-        final List<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>();
-        final int count = adapter.getCount();
-
-        boolean changed = false;
-
-        for (int i = 0; i < count; i++) {
-            final ApplicationInfo applicationInfo = adapter.getItem(i);
-            final Intent intent = applicationInfo.intent;
-            final ComponentName component = intent.getComponent();
-            if (packageName.equals(component.getPackageName())) {
-                if (!findIntent(matches, component)) {
-                    toRemove.add(applicationInfo);
-                    changed = true;
-                }
-            }
-        }
-
-        final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache;
-        for (ApplicationInfo info : toRemove) {
-            adapter.setNotifyOnChange(false);
-            adapter.remove(info);
-            cache.remove(info.intent.getComponent());
-        }
-
-        return changed;
-    }
-
-    private static ApplicationInfo findIntent(ApplicationsAdapter adapter, String packageName,
-            String name) {
-
-        final int count = adapter.getCount();
-        for (int i = 0; i < count; i++) {
-            final ApplicationInfo applicationInfo = adapter.getItem(i);
-            final Intent intent = applicationInfo.intent;
-            final ComponentName component = intent.getComponent();
-            if (packageName.equals(component.getPackageName()) &&
-                    name.equals(component.getClassName())) {
-                return applicationInfo;
-            }
+        } finally {
+            c.close();
         }
 
         return null;
     }
 
-    private static boolean findIntent(List<ResolveInfo> apps, ComponentName component) {
-        final String className = component.getClassName();
-        for (ResolveInfo info : apps) {
-            final ActivityInfo activityInfo = info.activityInfo;
-            if (activityInfo.name.equals(className)) {
-                return true;
-            }
+    /**
+     * Add an item to the database in a specified container. Sets the container, screen, cellX and
+     * cellY fields of the item. Also assigns an ID to the item.
+     */
+    static void addItemToDatabase(Context context, ItemInfo item, long container,
+            int screen, int cellX, int cellY, boolean notify) {
+        item.container = container;
+        item.screen = screen;
+        item.cellX = cellX;
+        item.cellY = cellY;
+
+        final ContentValues values = new ContentValues();
+        final ContentResolver cr = context.getContentResolver();
+
+        item.onAddToDatabase(values);
+
+        Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
+                LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
+
+        if (result != null) {
+            item.id = Integer.parseInt(result.getPathSegments().get(1));
         }
-        return false;
     }
 
-    Drawable getApplicationInfoIcon(PackageManager manager, ApplicationInfo info) {
-        final ResolveInfo resolveInfo = manager.resolveActivity(info.intent, 0);
+    /**
+     * Update an item to the database in a specified container.
+     */
+    static void updateItemInDatabase(Context context, ItemInfo item) {
+        final ContentValues values = new ContentValues();
+        final ContentResolver cr = context.getContentResolver();
+
+        item.onAddToDatabase(values);
+
+        cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
+    }
+
+    /**
+     * Removes the specified item from the database
+     * @param context
+     * @param item
+     */
+    static void deleteItemFromDatabase(Context context, ItemInfo item) {
+        final ContentResolver cr = context.getContentResolver();
+
+        cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
+    }
+
+    /**
+     * Remove the contents of the specified folder from the database
+     */
+    static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
+        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);
+    }
+
+    /**
+     * Set this as the current Launcher activity object for the loader.
+     */
+    public void initialize(Callbacks callbacks) {
+        synchronized (mLock) {
+            mCallbacks = new WeakReference<Callbacks>(callbacks);
+        }
+    }
+
+    public void startLoader(Context context, boolean isLaunching) {
+        mLoader.startLoader(context, isLaunching);
+    }
+
+    public void stopLoader() {
+        mLoader.stopLoader();
+    }
+
+    public void setWorkspaceDirty() {
+        mLoader.setWorkspaceDirty();
+    }
+
+    /**
+     * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
+     * ACTION_PACKAGE_CHANGED.
+     */
+    public void onReceiveIntent(Context context, Intent intent) {
+        final String packageName = intent.getData().getSchemeSpecificPart();
+
+        ArrayList<ApplicationInfo> added = null;
+        ArrayList<ApplicationInfo> removed = null;
+        ArrayList<ApplicationInfo> modified = null;
+        boolean update = false;
+        boolean remove = false;
+
+        synchronized (mLock) {
+            final String action = intent.getAction();
+            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+
+            if (packageName == null || packageName.length() == 0) {
+                // they sent us a bad intent
+                return;
+            }
+
+            if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+                mAllAppsList.updatePackage(context, packageName);
+                update = true;
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                if (!replacing) {
+                    mAllAppsList.removePackage(packageName);
+                    remove = true;
+                }
+                // else, we are replacing the package, so a PACKAGE_ADDED will be sent
+                // later, we will update the package at this time
+            } else {
+                if (!replacing) {
+                    mAllAppsList.addPackage(context, packageName);
+                } else {
+                    mAllAppsList.updatePackage(context, packageName);
+                    update = true;
+                }
+            }
+
+            if (mAllAppsList.added.size() > 0) {
+                added = mAllAppsList.added;
+                added = new ArrayList();
+            }
+            if (mAllAppsList.removed.size() > 0) {
+                removed = mAllAppsList.removed;
+                removed = new ArrayList();
+                for (ApplicationInfo info: removed) {
+                    AppInfoCache.remove(info.intent.getComponent());
+                }
+            }
+            if (mAllAppsList.modified.size() > 0) {
+                modified = mAllAppsList.modified;
+                modified = new ArrayList();
+            }
+
+            final Callbacks callbacks = mCallbacks.get();
+            if (callbacks == null) {
+                return;
+            }
+
+            if (added != null) {
+                final ArrayList<ApplicationInfo> addedFinal = added;
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        callbacks.bindPackageAdded(addedFinal);
+                    }
+                });
+            }
+            if (update || modified != null) {
+                final ArrayList<ApplicationInfo> modifiedFinal = modified;
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        callbacks.bindPackageUpdated(packageName, modifiedFinal);
+                    }
+                });
+            }
+            if (remove || removed != null) {
+                final ArrayList<ApplicationInfo> removedFinal = removed;
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        callbacks.bindPackageRemoved(packageName, removedFinal);
+                    }
+                });
+            }
+        }
+    }
+
+    public class Loader {
+        private static final int ITEMS_CHUNK = 6;
+
+        private LoaderThread mLoaderThread;
+
+        private int mLastWorkspaceSeq = 0;
+        private int mWorkspaceSeq = 1;
+
+        private int mLastAllAppsSeq = 0;
+        private int mAllAppsSeq = 1;
+
+        final ArrayList<ItemInfo> mItems = new ArrayList();
+        final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList();
+        final HashMap<Long, FolderInfo> folders = new HashMap();
+                
+        /**
+         * Call this from the ui thread so the handler is initialized on the correct thread.
+         */
+        public Loader() {
+        }
+
+        public void startLoader(Context context, boolean isLaunching) {
+            synchronized (mLock) {
+                Log.d(TAG, "startLoader isLaunching=" + isLaunching);
+                // Don't bother to start the thread if we know it's not going to do anything
+                if (mCallbacks.get() != null) {
+                    LoaderThread oldThread = mLoaderThread;
+                    if (oldThread != null) {
+                        if (oldThread.isLaunching()) {
+                            // don't downgrade isLaunching if we're already running
+                            isLaunching = true;
+                        }
+                        oldThread.stopLocked();
+                    }
+                    mLoaderThread = new LoaderThread(context, oldThread, isLaunching);
+                    mLoaderThread.start();
+                }
+            }
+        }
+
+        public void stopLoader() {
+            synchronized (mLock) {
+                if (mLoaderThread != null) {
+                    mLoaderThread.stopLocked();
+                }
+            }
+        }
+
+        public void setWorkspaceDirty() {
+            synchronized (mLock) {
+                mWorkspaceSeq++;
+            }
+        }
+
+        public void setAllAppsDirty() {
+            synchronized (mLock) {
+                mAllAppsSeq++;
+            }
+        }
+
+        /**
+         * Runnable for the thread that loads the contents of the launcher:
+         *   - workspace icons
+         *   - widgets
+         *   - all apps icons
+         */
+        private class LoaderThread extends Thread {
+            private Context mContext;
+            private Thread mWaitThread;
+            private boolean mIsLaunching;
+            private boolean mStopped;
+            private boolean mWorkspaceDoneBinding;
+
+            LoaderThread(Context context, Thread waitThread, boolean isLaunching) {
+                mContext = context;
+                mWaitThread = waitThread;
+                mIsLaunching = isLaunching;
+            }
+
+            boolean isLaunching() {
+                return mIsLaunching;
+            }
+
+            /**
+             * If another LoaderThread was supplied, we need to wait for that to finish before
+             * we start our processing.  This keeps the ordering of the setting and clearing
+             * of the dirty flags correct by making sure we don't start processing stuff until
+             * they've had a chance to re-set them.  We do this waiting the worker thread, not
+             * the ui thread to avoid ANRs.
+             */
+            private void waitForOtherThread() {
+                if (mWaitThread != null) {
+                    boolean done = false;
+                    while (!done) {
+                        try {
+                            mWaitThread.join();
+                        } catch (InterruptedException ex) {
+                        }
+                    }
+                    mWaitThread = null;
+                }
+            }
+
+            public void run() {
+                waitForOtherThread();
+
+                // Elevate priority when Home launches for the first time to avoid
+                // starving at boot time. Staring at a blank home is not cool.
+                synchronized (mLock) {
+                    android.os.Process.setThreadPriority(mIsLaunching
+                            ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
+                }
+
+                // Load the workspace only if it's dirty.
+                int workspaceSeq;
+                boolean workspaceDirty;
+                synchronized (mLock) {
+                    workspaceSeq = mWorkspaceSeq;
+                    workspaceDirty = mWorkspaceSeq != mLastWorkspaceSeq;
+                }
+                if (workspaceDirty) {
+                    loadWorkspace();
+                }
+                synchronized (mLock) {
+                    // If we're not stopped, and nobody has incremented mWorkspaceSeq.
+                    if (mStopped) {
+                        return;
+                    }
+                    if (workspaceSeq == mWorkspaceSeq) {
+                        mLastWorkspaceSeq = mWorkspaceSeq;
+                    }
+                }
+
+                // Bind the workspace
+                bindWorkspace();
+                
+                // Wait until the either we're stopped or the other threads are done.
+                // This way we don't start loading all apps until the workspace has settled
+                // down.
+                synchronized (LoaderThread.this) {
+                    mHandler.post(new Runnable() {
+                            public void run() {
+                                synchronized (LoaderThread.this) {
+                                    mWorkspaceDoneBinding = true;
+                                    Log.d(TAG, "done with workspace");
+                                    LoaderThread.this.notify();
+                                }
+                            }
+                        });
+                    Log.d(TAG, "waiting to be done with workspace");
+                    while (!mStopped && !mWorkspaceDoneBinding) {
+                        try {
+                            this.wait();
+                        } catch (InterruptedException ex) {
+                        }
+                    }
+                    Log.d(TAG, "done waiting to be done with workspace");
+                }
+
+                // Load all apps if they're dirty
+                int allAppsSeq;
+                boolean allAppsDirty;
+                synchronized (mLock) {
+                    allAppsSeq = mAllAppsSeq;
+                    allAppsDirty = mAllAppsSeq != mLastAllAppsSeq;
+                }
+                if (allAppsDirty) {
+                    loadAllApps();
+                }
+                synchronized (mLock) {
+                    // If we're not stopped, and nobody has incremented mAllAppsSeq.
+                    if (mStopped) {
+                        return;
+                    }
+                    if (allAppsSeq == mAllAppsSeq) {
+                        mLastAllAppsSeq = mAllAppsSeq;
+                    }
+                }
+
+                // Bind all apps
+                bindAllApps();
+
+                // Clear out this reference, otherwise we end up holding it until all of the
+                // callback runnables are done.
+                mContext = null;
+
+                synchronized (mLock) {
+                    // Setting the reference is atomic, but we can't do it inside the other critical
+                    // sections.
+                    mLoaderThread = null;
+                    return;
+                }
+            }
+
+            public void stopLocked() {
+                synchronized (LoaderThread.this) {
+                    mStopped = true;
+                    this.notify();
+                }
+            }
+
+            /**
+             * Gets the callbacks object.  If we've been stopped, or if the launcher object
+             * has somehow been garbage collected, return null instead.
+             */
+            Callbacks tryGetCallbacks() {
+                synchronized (mLock) {
+                    if (mStopped) {
+                        return null;
+                    }
+
+                    final Callbacks callbacks = mCallbacks.get();
+                    if (callbacks == null) {
+                        Log.w(TAG, "no mCallbacks");
+                        return null;
+                    }
+
+                    return callbacks;
+                }
+            }
+
+            private void loadWorkspace() {
+                long t = SystemClock.uptimeMillis();
+
+                final Context context = mContext;
+                final ContentResolver contentResolver = context.getContentResolver();
+                final PackageManager manager = context.getPackageManager();
+
+                /* TODO
+                if (mLocaleChanged) {
+                    updateShortcutLabels(contentResolver, manager);
+                }
+                */
+
+                final Cursor c = contentResolver.query(
+                        LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
+
+                try {
+                    final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
+                    final int intentIndex = c.getColumnIndexOrThrow
+                            (LauncherSettings.Favorites.INTENT);
+                    final int titleIndex = c.getColumnIndexOrThrow
+                            (LauncherSettings.Favorites.TITLE);
+                    final int iconTypeIndex = c.getColumnIndexOrThrow(
+                            LauncherSettings.Favorites.ICON_TYPE);
+                    final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
+                    final int iconPackageIndex = c.getColumnIndexOrThrow(
+                            LauncherSettings.Favorites.ICON_PACKAGE);
+                    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 appWidgetIdIndex = c.getColumnIndexOrThrow(
+                            LauncherSettings.Favorites.APPWIDGET_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;
+                    LauncherAppWidgetInfo appWidgetInfo;
+                    int container;
+                    long id;
+                    Intent intent;
+
+                    while (!mStopped && c.moveToNext()) {
+                        try {
+                            int itemType = c.getInt(itemTypeIndex);
+
+                            switch (itemType) {
+                            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+                            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                                intentDescription = c.getString(intentIndex);
+                                try {
+                                    intent = Intent.parseUri(intentDescription, 0);
+                                } catch (URISyntaxException e) {
+                                    continue;
+                                }
+
+                                if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                                    info = getApplicationInfo(manager, intent, context);
+                                } else {
+                                    info = getApplicationInfoShortcut(c, context, iconTypeIndex,
+                                            iconPackageIndex, iconResourceIndex, iconIndex);
+                                }
+
+                                if (info == null) {
+                                    info = new ApplicationInfo();
+                                    info.icon = manager.getDefaultActivityIcon();
+                                }
+
+                                if (info != null) {
+                                    info.title = c.getString(titleIndex);
+                                    info.intent = intent;
+
+                                    info.id = c.getLong(idIndex);
+                                    container = c.getInt(containerIndex);
+                                    info.container = container;
+                                    info.screen = c.getInt(screenIndex);
+                                    info.cellX = c.getInt(cellXIndex);
+                                    info.cellY = c.getInt(cellYIndex);
+
+                                    switch (container) {
+                                    case LauncherSettings.Favorites.CONTAINER_DESKTOP:
+                                        mItems.add(info);
+                                        break;
+                                    default:
+                                        // Item is in a user folder
+                                        UserFolderInfo folderInfo =
+                                                findOrMakeUserFolder(folders, container);
+                                        folderInfo.add(info);
+                                        break;
+                                    }
+                                }
+                                break;
+                            case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
+
+                                id = c.getLong(idIndex);
+                                UserFolderInfo folderInfo = findOrMakeUserFolder(folders, id);
+
+                                folderInfo.title = c.getString(titleIndex);
+
+                                folderInfo.id = id;
+                                container = c.getInt(containerIndex);
+                                folderInfo.container = container;
+                                folderInfo.screen = c.getInt(screenIndex);
+                                folderInfo.cellX = c.getInt(cellXIndex);
+                                folderInfo.cellY = c.getInt(cellYIndex);
+
+                                switch (container) {
+                                    case LauncherSettings.Favorites.CONTAINER_DESKTOP:
+                                        mItems.add(folderInfo);
+                                        break;
+                                }
+                                break;
+                            case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
+
+                                id = c.getLong(idIndex);
+                                LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(folders, id);
+
+                                intentDescription = c.getString(intentIndex);
+                                intent = null;
+                                if (intentDescription != null) {
+                                    try {
+                                        intent = Intent.parseUri(intentDescription, 0);
+                                    } catch (URISyntaxException e) {
+                                        // Ignore, a live folder might not have a base intent
+                                    }
+                                }
+
+                                liveFolderInfo.title = c.getString(titleIndex);
+                                liveFolderInfo.id = id;
+                                container = c.getInt(containerIndex);
+                                liveFolderInfo.container = container;
+                                liveFolderInfo.screen = c.getInt(screenIndex);
+                                liveFolderInfo.cellX = c.getInt(cellXIndex);
+                                liveFolderInfo.cellY = c.getInt(cellYIndex);
+                                liveFolderInfo.uri = Uri.parse(c.getString(uriIndex));
+                                liveFolderInfo.baseIntent = intent;
+                                liveFolderInfo.displayMode = c.getInt(displayModeIndex);
+
+                                loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
+                                        iconResourceIndex, liveFolderInfo);
+
+                                switch (container) {
+                                    case LauncherSettings.Favorites.CONTAINER_DESKTOP:
+                                        mItems.add(liveFolderInfo);
+                                        break;
+                                }
+                                break;
+                            case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
+                                widgetInfo = Widget.makeSearch();
+
+                                container = c.getInt(containerIndex);
+                                if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                                    Log.e(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);
+
+                                mItems.add(widgetInfo);
+                                break;
+                            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                                // Read all Launcher-specific widget details
+                                int appWidgetId = c.getInt(appWidgetIdIndex);
+                                appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
+                                appWidgetInfo.id = c.getLong(idIndex);
+                                appWidgetInfo.screen = c.getInt(screenIndex);
+                                appWidgetInfo.cellX = c.getInt(cellXIndex);
+                                appWidgetInfo.cellY = c.getInt(cellYIndex);
+                                appWidgetInfo.spanX = c.getInt(spanXIndex);
+                                appWidgetInfo.spanY = c.getInt(spanYIndex);
+
+                                container = c.getInt(containerIndex);
+                                if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                                    Log.e(TAG, "Widget found where container "
+                                            + "!= CONTAINER_DESKTOP -- ignoring!");
+                                    continue;
+                                }
+                                appWidgetInfo.container = c.getInt(containerIndex);
+
+                                mAppWidgets.add(appWidgetInfo);
+                                break;
+                            }
+                        } catch (Exception e) {
+                            Log.w(TAG, "Desktop items loading interrupted:", e);
+                        }
+                    }
+                } finally {
+                    c.close();
+                }
+                Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
+            }
+
+            /**
+             * Read everything out of our database.
+             */
+            private void bindWorkspace() {
+                final long t = SystemClock.uptimeMillis();
+
+                // Don't use these two variables in any of the callback runnables.
+                // Otherwise we hold a reference to them.
+                Callbacks callbacks = mCallbacks.get();
+                if (callbacks == null) {
+                    // This launcher has exited and nobody bothered to tell us.  Just bail.
+                    Log.w(TAG, "LoaderThread running with no launcher");
+                    return;
+                }
+
+                int N;
+                // Tell the workspace that we're about to start firing items at it
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        Callbacks callbacks = tryGetCallbacks();
+                        if (callbacks != null) {
+                            callbacks.startBinding();
+                        }
+                    }
+                });
+                // Add the items to the workspace.
+                N = mItems.size();
+                for (int i=0; i<N; i+=ITEMS_CHUNK) {
+                    final int start = i;
+                    final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
+                    mHandler.post(new Runnable() {
+                        public void run() {
+                            Callbacks callbacks = tryGetCallbacks();
+                            if (callbacks != null) {
+                                callbacks.bindItems(mItems, start, start+chunkSize);
+                            }
+                        }
+                    });
+                }
+                // Wait until the queue goes empty.
+                mHandler.postIdle(new Runnable() {
+                    public void run() {
+                        Log.d(TAG, "Going to start binding widgets soon.");
+                    }
+                });
+                // Bind the widgets, one at a time.
+                // WARNING: this is calling into the workspace from the background thread,
+                // but since getCurrentScreen() just returns the int, we should be okay.  This
+                // is just a hint for the order, and if it's wrong, we'll be okay.
+                // TODO: instead, we should have that push the current screen into here.
+                final int currentScreen = callbacks.getCurrentWorkspaceScreen();
+                N = mAppWidgets.size();
+                // once for the current screen
+                for (int i=0; i<N; i++) {
+                    final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
+                    if (widget.screen == currentScreen) {
+                        mHandler.post(new Runnable() {
+                            public void run() {
+                                Callbacks callbacks = tryGetCallbacks();
+                                if (callbacks != null) {
+                                    callbacks.bindAppWidget(widget);
+                                }
+                            }
+                        });
+                    }
+                }
+                // once for the other screens
+                for (int i=0; i<N; i++) {
+                    final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
+                    if (widget.screen != currentScreen) {
+                        mHandler.post(new Runnable() {
+                            public void run() {
+                                Callbacks callbacks = tryGetCallbacks();
+                                if (callbacks != null) {
+                                    callbacks.bindAppWidget(widget);
+                                }
+                            }
+                        });
+                    }
+                }
+                // TODO: Bind the folders
+                // Tell the workspace that we're done.
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        Callbacks callbacks = tryGetCallbacks();
+                        if (callbacks != null) {
+                            callbacks.finishBindingItems();
+                        }
+                    }
+                });
+                // If we're profiling, this is the last thing in the queue.
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        Log.d(TAG, "bound workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
+                        if (Launcher.PROFILE_ROTATE) {
+                            android.os.Debug.stopMethodTracing();
+                        }
+                    }
+                });
+            }
+
+            private void loadAllApps() {
+                final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+                mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+                final Callbacks callbacks = tryGetCallbacks();
+                if (callbacks == null) {
+                    return;
+                }
+
+                final Context context = mContext;
+                final PackageManager packageManager = context.getPackageManager();
+
+                final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
+
+                synchronized (mLock) {
+                    mAllAppsList.clear();
+                    if (apps != null) {
+                        long t = SystemClock.uptimeMillis();
+
+                        int N = apps.size();
+                        Utilities.BubbleText bubble = new Utilities.BubbleText(context);
+                        for (int i=0; i<N && !mStopped; i++) {
+                            // This builds the icon bitmaps.
+                            mAllAppsList.add(AppInfoCache.cache(apps.get(i), context, bubble));
+                        }
+                        Collections.sort(mAllAppsList.data, sComparator);
+                        Collections.sort(mAllAppsList.added, sComparator);
+                        Log.d(TAG, "cached app icons in " + (SystemClock.uptimeMillis()-t) + "ms");
+                    }
+                }
+            }
+
+            private void bindAllApps() {
+                synchronized (mLock) {
+                    final ArrayList<ApplicationInfo> results = mAllAppsList.added;
+                    mAllAppsList.added = new ArrayList();
+                    mHandler.post(new Runnable() {
+                        public void run() {
+                            long t = SystemClock.uptimeMillis();
+
+                            Callbacks callbacks = tryGetCallbacks();
+                            if (callbacks != null) {
+                                callbacks.bindAllApplications(results);
+                            }
+
+                            Log.d(TAG, "bound app icons in "
+                                + (SystemClock.uptimeMillis()-t) + "ms");
+                        }
+                    });
+                }
+            }
+        }
+    }
+
+    /**
+     * Make an ApplicationInfo object for an application.
+     */
+    private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent,
+                                                      Context context) {
+        final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
+
         if (resolveInfo == null) {
             return null;
         }
 
-        ComponentName componentName = new ComponentName(
-                resolveInfo.activityInfo.applicationInfo.packageName,
-                resolveInfo.activityInfo.name);
-        ApplicationInfo application = mAppInfoCache.get(componentName);
-
-        if (application == null) {
-            return resolveInfo.activityInfo.loadIcon(manager);
+        final ApplicationInfo info = new ApplicationInfo();
+        final ActivityInfo activityInfo = resolveInfo.activityInfo;
+        info.icon = Utilities.createIconThumbnail(activityInfo.loadIcon(manager), context, false);
+        if (info.title == null || info.title.length() == 0) {
+            info.title = activityInfo.loadLabel(manager);
         }
-
-        return application.icon;
-    }
-
-    private static ApplicationInfo makeAndCacheApplicationInfo(PackageManager manager,
-            HashMap<ComponentName, ApplicationInfo> appInfoCache, ResolveInfo info,
-            Context context) {
-
-        ComponentName componentName = new ComponentName(
-                info.activityInfo.applicationInfo.packageName,
-                info.activityInfo.name);
-        ApplicationInfo application = appInfoCache.get(componentName);
-
-        if (application == null) {
-            application = new ApplicationInfo();
-            application.container = ItemInfo.NO_ID;
-
-            updateApplicationInfoTitleAndIcon(manager, info, application, context);
-
-            application.setActivity(componentName,
-                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-
-            appInfoCache.put(componentName, application);
+        if (info.title == null) {
+            info.title = "";
         }
-
-        return application;
-    }
-
-    private static void updateApplicationInfoTitleAndIcon(PackageManager manager, ResolveInfo info,
-            ApplicationInfo application, Context context) {
-
-        application.title = info.loadLabel(manager);
-        if (application.title == null) {
-            application.title = info.activityInfo.name;
-        }
-
-        application.icon =
-                Utilities.createIconThumbnail(info.activityInfo.loadIcon(manager), context);
-        application.filtered = false;
-    }
-
-    private class ApplicationsLoader implements Runnable {
-        private final WeakReference<Launcher> mLauncher;
-
-        private volatile boolean mStopped;
-        private volatile boolean mRunning;
-        private final boolean mIsLaunching;
-
-        ApplicationsLoader(Launcher launcher, boolean isLaunching) {
-            mIsLaunching = isLaunching;
-            mLauncher = new WeakReference<Launcher>(launcher);
-        }
-
-        void stop() {
-            mStopped = true;
-        }
-
-        boolean isRunning() {
-            return mRunning;
-        }
-
-        public void run() {
-            mRunning = true;
-
-            // Elevate priority when Home launches for the first time to avoid
-            // starving at boot time. Staring at a blank home is not cool.
-            android.os.Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT :
-                    Process.THREAD_PRIORITY_BACKGROUND);
-
-            final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
-            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
-            final Launcher launcher = mLauncher.get();
-            final PackageManager manager = launcher.getPackageManager();
-            final List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0);
-
-            if (apps != null && !mStopped) {
-                final int count = apps.size();
-                // Can be set to null on the UI thread by the unbind() method
-                // Do not access without checking for null first
-                final ApplicationsAdapter applicationList = mApplicationsAdapter;
-
-                ChangeNotifier action = new ChangeNotifier(applicationList, true);
-                final HashMap<ComponentName, ApplicationInfo> appInfoCache = mAppInfoCache;
-
-                for (int i = 0; i < count && !mStopped; i++) {
-                    ResolveInfo info = apps.get(i);
-                    ApplicationInfo application =
-                        makeAndCacheApplicationInfo(manager, appInfoCache, info, launcher);
-
-                    if (action.add(application) && !mStopped) {
-                        launcher.runOnUiThread(action);
-                        action = new ChangeNotifier(applicationList, false);
-                    }
-                }
-
-                launcher.runOnUiThread(action);
-            }
-
-            if (!mStopped) {
-                mApplicationsLoaded = true;
-            }
-            mRunning = false;
-        }
-    }
-
-    private static class ChangeNotifier implements Runnable {
-        private final ApplicationsAdapter mApplicationList;
-        private final ArrayList<ApplicationInfo> mBuffer;
-
-        private boolean mFirst = true;
-
-        ChangeNotifier(ApplicationsAdapter applicationList, boolean first) {
-            mApplicationList = applicationList;
-            mFirst = first;
-            mBuffer = new ArrayList<ApplicationInfo>(UI_NOTIFICATION_RATE);
-        }
-
-        public void run() {
-            final ApplicationsAdapter applicationList = mApplicationList;
-            // Can be set to null on the UI thread by the unbind() method
-            if (applicationList == null) return;
-
-            if (mFirst) {
-                applicationList.setNotifyOnChange(false);
-                applicationList.clear();
-                mFirst = false;
-            }
-
-            final ArrayList<ApplicationInfo> buffer = mBuffer;
-            final int count = buffer.size();
-
-            for (int i = 0; i < count; i++) {
-                applicationList.setNotifyOnChange(false);
-                applicationList.add(buffer.get(i));
-            }
-
-            buffer.clear();
-
-            applicationList.sort(new ApplicationInfoComparator());
-            applicationList.notifyDataSetChanged();
-        }
-
-        boolean add(ApplicationInfo application) {
-            final ArrayList<ApplicationInfo> buffer = mBuffer;
-            buffer.add(application);
-            return buffer.size() >= UI_NOTIFICATION_RATE;
-        }
-    }
-
-    static class ApplicationInfoComparator implements Comparator<ApplicationInfo> {
-        public final int compare(ApplicationInfo a, ApplicationInfo b) {
-            return sCollator.compare(a.title.toString(), b.title.toString());
-        }
-    }
-
-    boolean isDesktopLoaded() {
-        return mDesktopItems != null && mDesktopAppWidgets != null && mDesktopItemsLoaded;
+        info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+        return info;
     }
 
     /**
-     * Loads all of the items on the desktop, in folders, or in the dock.
-     * These can be apps, shortcuts or widgets
+     * Make an ApplicationInfo object for a sortcut
      */
-    void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged,
-            boolean loadApplications) {
-        if (DEBUG_LOADERS) d(LOG_TAG, "loading user items");
+    private static ApplicationInfo getApplicationInfoShortcut(Cursor c, Context context,
+            int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
 
-        if (isLaunching && isDesktopLoaded()) {
-            if (DEBUG_LOADERS) d(LOG_TAG, "  --> items loaded, return");
-            if (loadApplications) startApplicationsLoader(launcher, true);
-            // We have already loaded our data from the DB
-            launcher.onDesktopItemsLoaded();
-            return;
-        }
+        final ApplicationInfo info = new ApplicationInfo();
+        info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
 
-        if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
-            if (DEBUG_LOADERS) d(LOG_TAG, "  --> stopping workspace loader");
-            mDesktopItemsLoader.stop();
-            // Wait for the currently running thread to finish, this can take a little
-            // time but it should be well below the timeout limit
+        int iconType = c.getInt(iconTypeIndex);
+        switch (iconType) {
+        case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
+            String packageName = c.getString(iconPackageIndex);
+            String resourceName = c.getString(iconResourceIndex);
+            PackageManager packageManager = context.getPackageManager();
             try {
-                mDesktopLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
-            } catch (InterruptedException e) {
-                // Empty
+                Resources resources = packageManager.getResourcesForApplication(packageName);
+                final int id = resources.getIdentifier(resourceName, null, null);
+                info.icon = Utilities.createIconThumbnail(resources.getDrawable(id), context,
+                        false);
+            } catch (Exception e) {
+                info.icon = packageManager.getDefaultActivityIcon();
             }
-
-            // If the thread we are interrupting was tasked to load the list of
-            // applications make sure we keep that information in the new loader
-            // spawned below
-            // note: we don't apply this to localeChanged because the thread can
-            // only be stopped *after* the localeChanged handling has occured
-            loadApplications = mDesktopItemsLoader.mLoadApplications;
+            info.iconResource = new Intent.ShortcutIconResource();
+            info.iconResource.packageName = packageName;
+            info.iconResource.resourceName = resourceName;
+            info.customIcon = false;
+            break;
+        case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
+            byte[] data = c.getBlob(iconIndex);
+            try {
+                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
+                info.icon = new FastBitmapDrawable(
+                        Utilities.createBitmapThumbnail(bitmap, context));
+            } catch (Exception e) {
+                packageManager = context.getPackageManager();
+                info.icon = packageManager.getDefaultActivityIcon();
+            }
+            info.filtered = true;
+            info.customIcon = true;
+            break;
+        default:
+            info.icon = context.getPackageManager().getDefaultActivityIcon();
+            info.customIcon = false;
+            break;
         }
+        return info;
+    }
 
-        if (DEBUG_LOADERS) d(LOG_TAG, "  --> starting workspace loader");
-        mDesktopItemsLoaded = false;
-        mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications,
-                isLaunching);
-        mDesktopLoaderThread = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
-        mDesktopLoaderThread.start();
+    private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
+            int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
+
+        int iconType = c.getInt(iconTypeIndex);
+        switch (iconType) {
+        case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
+            String packageName = c.getString(iconPackageIndex);
+            String resourceName = c.getString(iconResourceIndex);
+            PackageManager packageManager = context.getPackageManager();
+            try {
+                Resources resources = packageManager.getResourcesForApplication(packageName);
+                final int id = resources.getIdentifier(resourceName, null, null);
+                liveFolderInfo.icon = resources.getDrawable(id);
+            } catch (Exception e) {
+                liveFolderInfo.icon =
+                        context.getResources().getDrawable(R.drawable.ic_launcher_folder);
+            }
+            liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
+            liveFolderInfo.iconResource.packageName = packageName;
+            liveFolderInfo.iconResource.resourceName = resourceName;
+            break;
+        default:
+            liveFolderInfo.icon =
+                    context.getResources().getDrawable(R.drawable.ic_launcher_folder);
+        }
+    }
+
+    /**
+     * Return an existing UserFolderInfo object if we have encountered this ID previously,
+     * or make a new one.
+     */
+    private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
+        // See if a placeholder was created for us already
+        FolderInfo folderInfo = folders.get(id);
+        if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
+            // No placeholder -- create a new instance
+            folderInfo = new UserFolderInfo();
+            folders.put(id, folderInfo);
+        }
+        return (UserFolderInfo) folderInfo;
+    }
+
+    /**
+     * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
+     * new one.
+     */
+    private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
+        // See if a placeholder was created for us already
+        FolderInfo folderInfo = folders.get(id);
+        if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
+            // No placeholder -- create a new instance
+            folderInfo = new LiveFolderInfo();
+            folders.put(id, folderInfo);
+        }
+        return (LiveFolderInfo) folderInfo;
     }
 
     private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
@@ -696,787 +1129,11 @@
         return label;
     }
 
-    private class DesktopItemsLoader implements Runnable {
-        private volatile boolean mStopped;
-        private volatile boolean mRunning;
-
-        private final WeakReference<Launcher> mLauncher;
-        private final boolean mLocaleChanged;
-        private final boolean mLoadApplications;
-        private final boolean mIsLaunching;
-
-        DesktopItemsLoader(Launcher launcher, boolean localeChanged, boolean loadApplications,
-                boolean isLaunching) {
-            mLoadApplications = loadApplications;
-            mIsLaunching = isLaunching;
-            mLauncher = new WeakReference<Launcher>(launcher);
-            mLocaleChanged = localeChanged;
+    private static final Collator sCollator = Collator.getInstance();
+    private static final Comparator<ApplicationInfo> sComparator
+            = new Comparator<ApplicationInfo>() {
+        public final int compare(ApplicationInfo a, ApplicationInfo b) {
+            return sCollator.compare(a.title.toString(), b.title.toString());
         }
-
-        void stop() {
-            mStopped = true;
-        }
-
-        boolean isRunning() {
-            return mRunning;
-        }
-
-        public void run() {
-            mRunning = true;
-
-            android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
-
-            final Launcher launcher = mLauncher.get();
-            final ContentResolver contentResolver = launcher.getContentResolver();
-            final PackageManager manager = launcher.getPackageManager();
-
-            if (mLocaleChanged) {
-                updateShortcutLabels(contentResolver, manager);
-            }
-
-            mDesktopItems = new ArrayList<ItemInfo>();
-            mDesktopAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
-            mFolders = new HashMap<Long, FolderInfo>();
-
-            final ArrayList<ItemInfo> desktopItems = mDesktopItems;
-            final ArrayList<LauncherAppWidgetInfo> desktopAppWidgets = mDesktopAppWidgets;
-
-            final Cursor c = contentResolver.query(
-                    LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
-
-            try {
-                final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
-                final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
-                final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
-                final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
-                final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
-                final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
-                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 appWidgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.APPWIDGET_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;
-                LauncherAppWidgetInfo appWidgetInfo;
-                int container;
-                long id;
-                Intent intent;
-
-                final HashMap<Long, FolderInfo> folders = mFolders;
-
-                while (!mStopped && c.moveToNext()) {
-                    try {
-                        int itemType = c.getInt(itemTypeIndex);
-
-                        switch (itemType) {
-                        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                            intentDescription = c.getString(intentIndex);
-                            try {
-                                intent = Intent.parseUri(intentDescription, 0);
-                            } catch (java.net.URISyntaxException e) {
-                                continue;
-                            }
-
-                            if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-                                info = getApplicationInfo(manager, intent, launcher);
-                            } else {
-                                info = getApplicationInfoShortcut(c, launcher, iconTypeIndex,
-                                        iconPackageIndex, iconResourceIndex, iconIndex);
-                            }
-
-                            if (info == null) {
-                                info = new ApplicationInfo();
-                                info.icon = manager.getDefaultActivityIcon();
-                            }
-
-                            if (info != null) {
-                                info.title = c.getString(titleIndex);
-                                info.intent = intent;
-
-                                info.id = c.getLong(idIndex);
-                                container = c.getInt(containerIndex);
-                                info.container = container;
-                                info.screen = c.getInt(screenIndex);
-                                info.cellX = c.getInt(cellXIndex);
-                                info.cellY = c.getInt(cellYIndex);
-
-                                switch (container) {
-                                case LauncherSettings.Favorites.CONTAINER_DESKTOP:
-                                    desktopItems.add(info);
-                                    break;
-                                default:
-                                    // Item is in a user folder
-                                    UserFolderInfo folderInfo =
-                                            findOrMakeUserFolder(folders, container);
-                                    folderInfo.add(info);
-                                    break;
-                                }
-                            }
-                            break;
-                        case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
-
-                            id = c.getLong(idIndex);
-                            UserFolderInfo folderInfo = findOrMakeUserFolder(folders, id);
-
-                            folderInfo.title = c.getString(titleIndex);
-
-                            folderInfo.id = id;
-                            container = c.getInt(containerIndex);
-                            folderInfo.container = container;
-                            folderInfo.screen = c.getInt(screenIndex);
-                            folderInfo.cellX = c.getInt(cellXIndex);
-                            folderInfo.cellY = c.getInt(cellYIndex);
-
-                            switch (container) {
-                                case LauncherSettings.Favorites.CONTAINER_DESKTOP:
-                                    desktopItems.add(folderInfo);
-                                    break;
-                            }
-                            break;
-                        case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
-
-                            id = c.getLong(idIndex);
-                            LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(folders, id);
-
-                            intentDescription = c.getString(intentIndex);
-                            intent = null;
-                            if (intentDescription != null) {
-                                try {
-                                    intent = Intent.parseUri(intentDescription, 0);
-                                } catch (java.net.URISyntaxException e) {
-                                    // Ignore, a live folder might not have a base intent
-                                }
-                            }
-
-                            liveFolderInfo.title = c.getString(titleIndex);
-                            liveFolderInfo.id = id;
-                            container = c.getInt(containerIndex);
-                            liveFolderInfo.container = container;
-                            liveFolderInfo.screen = c.getInt(screenIndex);
-                            liveFolderInfo.cellX = c.getInt(cellXIndex);
-                            liveFolderInfo.cellY = c.getInt(cellYIndex);
-                            liveFolderInfo.uri = Uri.parse(c.getString(uriIndex));
-                            liveFolderInfo.baseIntent = intent;
-                            liveFolderInfo.displayMode = c.getInt(displayModeIndex);
-
-                            loadLiveFolderIcon(launcher, c, iconTypeIndex, iconPackageIndex,
-                                    iconResourceIndex, liveFolderInfo);
-
-                            switch (container) {
-                                case LauncherSettings.Favorites.CONTAINER_DESKTOP:
-                                    desktopItems.add(liveFolderInfo);
-                                    break;
-                            }
-                            break;
-                        case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
-                            widgetInfo = Widget.makeSearch();
-
-                            container = c.getInt(containerIndex);
-                            if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                                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_APPWIDGET:
-                            // Read all Launcher-specific widget details
-                            int appWidgetId = c.getInt(appWidgetIdIndex);
-                            appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
-                            appWidgetInfo.id = c.getLong(idIndex);
-                            appWidgetInfo.screen = c.getInt(screenIndex);
-                            appWidgetInfo.cellX = c.getInt(cellXIndex);
-                            appWidgetInfo.cellY = c.getInt(cellYIndex);
-                            appWidgetInfo.spanX = c.getInt(spanXIndex);
-                            appWidgetInfo.spanY = c.getInt(spanYIndex);
-
-                            container = c.getInt(containerIndex);
-                            if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                                e(Launcher.LOG_TAG, "Widget found where container "
-                                        + "!= CONTAINER_DESKTOP -- ignoring!");
-                                continue;
-                            }
-                            appWidgetInfo.container = c.getInt(containerIndex);
-
-                            desktopAppWidgets.add(appWidgetInfo);
-                            break;
-                        }
-                    } catch (Exception e) {
-                        w(Launcher.LOG_TAG, "Desktop items loading interrupted:", e);
-                    }
-                }
-            } finally {
-                c.close();
-            }
-
-            if (!mStopped) {
-                launcher.runOnUiThread(new Runnable() {
-                    public void run() {
-                        launcher.onDesktopItemsLoaded();
-                    }
-                });
-                if (mLoadApplications) startApplicationsLoader(launcher, mIsLaunching);
-            }
-
-            if (!mStopped) {
-                mDesktopItemsLoaded = true;
-            }
-            mRunning = false;
-        }
-    }
-
-    private static void loadLiveFolderIcon(Launcher launcher, Cursor c, int iconTypeIndex,
-            int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
-
-        int iconType = c.getInt(iconTypeIndex);
-        switch (iconType) {
-            case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
-                String packageName = c.getString(iconPackageIndex);
-                String resourceName = c.getString(iconResourceIndex);
-                PackageManager packageManager = launcher.getPackageManager();
-                try {
-                    Resources resources = packageManager.getResourcesForApplication(packageName);
-                    final int id = resources.getIdentifier(resourceName, null, null);
-                    liveFolderInfo.icon = resources.getDrawable(id);
-                } catch (Exception e) {
-                    liveFolderInfo.icon =
-                            launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
-                }
-                liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
-                liveFolderInfo.iconResource.packageName = packageName;
-                liveFolderInfo.iconResource.resourceName = resourceName;
-                break;
-            default:
-                liveFolderInfo.icon =
-                        launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
-        }
-    }
-
-    /**
-     * Finds the user folder defined by the specified id.
-     *
-     * @param id The id of the folder to look for.
-     *
-     * @return A UserFolderInfo if the folder exists or null otherwise.
-     */
-    FolderInfo findFolderById(long id) {
-        return mFolders.get(id);
-    }
-
-    void addFolder(FolderInfo info) {
-        mFolders.put(info.id, info);
-    }
-
-    /**
-     * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
-     * new one.
-     */
-    private UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
-        // See if a placeholder was created for us already
-        FolderInfo folderInfo = folders.get(id);
-        if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
-            // No placeholder -- create a new instance
-            folderInfo = new UserFolderInfo();
-            folders.put(id, folderInfo);
-        }
-        return (UserFolderInfo) folderInfo;
-    }
-
-    /**
-     * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
-     * new one.
-     */
-    private LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
-        // See if a placeholder was created for us already
-        FolderInfo folderInfo = folders.get(id);
-        if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
-            // No placeholder -- create a new instance
-            folderInfo = new LiveFolderInfo();
-            folders.put(id, folderInfo);
-        }
-        return (LiveFolderInfo) folderInfo;
-    }
-
-    /**
-     * Remove the callback for the cached drawables or we leak the previous
-     * Home screen on orientation change.
-     */
-    void unbind() {
-        // Interrupt the applications loader before setting the adapter to null
-        stopAndWaitForApplicationsLoader();
-        mApplicationsAdapter = null;
-        unbindAppDrawables(mApplications);
-        unbindDrawables(mDesktopItems);
-        unbindAppWidgetHostViews(mDesktopAppWidgets);
-        unbindCachedIconDrawables();
-    }
-
-    /**
-     * Remove the callback for the cached drawables or we leak the previous
-     * Home screen on orientation change.
-     */
-    private void unbindDrawables(ArrayList<ItemInfo> desktopItems) {
-        if (desktopItems != null) {
-            final int count = desktopItems.size();
-            for (int i = 0; i < count; i++) {
-                ItemInfo item = desktopItems.get(i);
-                switch (item.itemType) {
-                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                    ((ApplicationInfo)item).icon.setCallback(null);
-                    break;
-                }
-            }
-        }
-    }
-
-    /**
-     * Remove the callback for the cached drawables or we leak the previous
-     * Home screen on orientation change.
-     */
-    private void unbindAppDrawables(ArrayList<ApplicationInfo> applications) {
-        if (applications != null) {
-            final int count = applications.size();
-            for (int i = 0; i < count; i++) {
-                applications.get(i).icon.setCallback(null);
-            }
-        }
-    }
-
-    /**
-     * Remove any {@link LauncherAppWidgetHostView} references in our widgets.
-     */
-    private void unbindAppWidgetHostViews(ArrayList<LauncherAppWidgetInfo> appWidgets) {
-        if (appWidgets != null) {
-            final int count = appWidgets.size();
-            for (int i = 0; i < count; i++) {
-                LauncherAppWidgetInfo launcherInfo = appWidgets.get(i);
-                launcherInfo.hostView = null;
-            }
-        }
-    }
-
-    /**
-     * Remove the callback for the cached drawables or we leak the previous
-     * Home screen on orientation change.
-     */
-    private void unbindCachedIconDrawables() {
-        for (ApplicationInfo appInfo : mAppInfoCache.values()) {
-            appInfo.icon.setCallback(null);
-        }
-    }
-
-    /**
-     * @return The current list of applications
-     */
-    ApplicationsAdapter getApplicationsAdapter() {
-        return mApplicationsAdapter;
-    }
-
-    /**
-     * @return The current list of desktop items
-     */
-    ArrayList<ItemInfo> getDesktopItems() {
-        return mDesktopItems;
-    }
-
-    /**
-     * @return The current list of desktop items
-     */
-    ArrayList<LauncherAppWidgetInfo> getDesktopAppWidgets() {
-        return mDesktopAppWidgets;
-    }
-
-    /**
-     * Add an item to the desktop
-     * @param info
-     */
-    void addDesktopItem(ItemInfo info) {
-        // TODO: write to DB; also check that folder has been added to folders list
-        mDesktopItems.add(info);
-    }
-
-    /**
-     * Remove an item from the desktop
-     * @param info
-     */
-    void removeDesktopItem(ItemInfo info) {
-        // TODO: write to DB; figure out if we should remove folder from folders list
-        mDesktopItems.remove(info);
-    }
-
-    /**
-     * Add a widget to the desktop
-     */
-    void addDesktopAppWidget(LauncherAppWidgetInfo info) {
-        mDesktopAppWidgets.add(info);
-    }
-
-    /**
-     * Remove a widget from the desktop
-     */
-    void removeDesktopAppWidget(LauncherAppWidgetInfo info) {
-        mDesktopAppWidgets.remove(info);
-    }
-
-    /**
-     * Make an ApplicationInfo object for an application
-     */
-    private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent,
-                                                      Context context) {
-        final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
-
-        if (resolveInfo == null) {
-            return null;
-        }
-
-        final ApplicationInfo info = new ApplicationInfo();
-        final ActivityInfo activityInfo = resolveInfo.activityInfo;
-        info.icon = Utilities.createIconThumbnail(activityInfo.loadIcon(manager), context);
-        if (info.title == null || info.title.length() == 0) {
-            info.title = activityInfo.loadLabel(manager);
-        }
-        if (info.title == null) {
-            info.title = "";
-        }
-        info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-        return info;
-    }
-
-    /**
-     * Make an ApplicationInfo object for a sortcut
-     */
-    private ApplicationInfo getApplicationInfoShortcut(Cursor c, Context context,
-            int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
-
-        final ApplicationInfo info = new ApplicationInfo();
-        info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
-
-        int iconType = c.getInt(iconTypeIndex);
-        switch (iconType) {
-            case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
-                String packageName = c.getString(iconPackageIndex);
-                String resourceName = c.getString(iconResourceIndex);
-                PackageManager packageManager = context.getPackageManager();
-                try {
-                    Resources resources = packageManager.getResourcesForApplication(packageName);
-                    final int id = resources.getIdentifier(resourceName, null, null);
-                    info.icon = Utilities.createIconThumbnail(resources.getDrawable(id), context);
-                } catch (Exception e) {
-                    info.icon = packageManager.getDefaultActivityIcon();
-                }
-                info.iconResource = new Intent.ShortcutIconResource();
-                info.iconResource.packageName = packageName;
-                info.iconResource.resourceName = resourceName;
-                info.customIcon = false;
-                break;
-            case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
-                byte[] data = c.getBlob(iconIndex);
-                try {
-                    Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
-                    info.icon = new FastBitmapDrawable(
-                            Utilities.createBitmapThumbnail(bitmap, context));
-                } catch (Exception e) {
-                    packageManager = context.getPackageManager();
-                    info.icon = packageManager.getDefaultActivityIcon();
-                }
-                info.filtered = true;
-                info.customIcon = true;
-                break;
-            default:
-                info.icon = context.getPackageManager().getDefaultActivityIcon();
-                info.customIcon = false;
-                break;
-        }
-        return info;
-    }
-
-    /**
-     * Remove an item from the in-memory represention of a user folder. Does not change the DB.
-     */
-    void removeUserFolderItem(UserFolderInfo folder, ItemInfo info) {
-        //noinspection SuspiciousMethodCalls
-        folder.contents.remove(info);
-    }
-
-    /**
-     * Removes a UserFolder from the in-memory list of folders. Does not change the DB.
-     * @param userFolderInfo
-     */
-    void removeUserFolder(UserFolderInfo userFolderInfo) {
-        mFolders.remove(userFolderInfo.id);
-    }
-
-    /**
-     * Adds an item to the DB if it was not created previously, or move it to a new
-     * <container, screen, cellX, cellY>
-     */
-    static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
-            int screen, int cellX, int cellY) {
-        if (item.container == ItemInfo.NO_ID) {
-            // From all apps
-            addItemToDatabase(context, item, container, screen, cellX, cellY, false);
-        } else {
-            // From somewhere else
-            moveItemInDatabase(context, item, container, screen, cellX, cellY);
-        }
-    }
-
-    /**
-     * Move an item in the DB to a new <container, screen, cellX, cellY>
-     */
-    static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
-            int cellX, int cellY) {
-        item.container = container;
-        item.screen = screen;
-        item.cellX = cellX;
-        item.cellY = cellY;
-
-        final ContentValues values = new ContentValues();
-        final ContentResolver cr = context.getContentResolver();
-
-        values.put(LauncherSettings.Favorites.CONTAINER, item.container);
-        values.put(LauncherSettings.Favorites.CELLX, item.cellX);
-        values.put(LauncherSettings.Favorites.CELLY, item.cellY);
-        values.put(LauncherSettings.Favorites.SCREEN, item.screen);
-
-        cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
-    }
-
-    /**
-     * Returns true if the shortcuts already exists in the database.
-     * we identify a shortcut by its title and intent.
-     */
-    static boolean shortcutExists(Context context, String title, Intent intent) {
-        final ContentResolver cr = context.getContentResolver();
-        Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
-            new String[] { "title", "intent" }, "title=? and intent=?",
-            new String[] { title, intent.toUri(0) }, null);
-        boolean result = false;
-        try {
-            result = c.moveToFirst();
-        } finally {
-            c.close();
-        }
-        return result;
-    }
-
-    FolderInfo getFolderById(Context context, long id) {
-        final ContentResolver cr = context.getContentResolver();
-        Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
-                "_id=? and (itemType=? or itemType=?)",
-                new String[] { String.valueOf(id),
-                        String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
-                        String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
-
-        try {
-            if (c.moveToFirst()) {
-                final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
-                final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
-                final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
-                final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
-                final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
-                final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
-
-                FolderInfo folderInfo = null;
-                switch (c.getInt(itemTypeIndex)) {
-                    case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
-                        folderInfo = findOrMakeUserFolder(mFolders, id);
-                        break;
-                    case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
-                        folderInfo = findOrMakeLiveFolder(mFolders, id);
-                        break;
-                }
-
-                folderInfo.title = c.getString(titleIndex);
-                folderInfo.id = id;
-                folderInfo.container = c.getInt(containerIndex);
-                folderInfo.screen = c.getInt(screenIndex);
-                folderInfo.cellX = c.getInt(cellXIndex);
-                folderInfo.cellY = c.getInt(cellYIndex);
-
-                return folderInfo;
-            }
-        } finally {
-            c.close();
-        }
-
-        return null;
-    }
-
-    /**
-     * Add an item to the database in a specified container. Sets the container, screen, cellX and
-     * cellY fields of the item. Also assigns an ID to the item.
-     */
-    static void addItemToDatabase(Context context, ItemInfo item, long container,
-            int screen, int cellX, int cellY, boolean notify) {
-        item.container = container;
-        item.screen = screen;
-        item.cellX = cellX;
-        item.cellY = cellY;
-
-        final ContentValues values = new ContentValues();
-        final ContentResolver cr = context.getContentResolver();
-
-        item.onAddToDatabase(values);
-
-        Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
-                LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
-
-        if (result != null) {
-            item.id = Integer.parseInt(result.getPathSegments().get(1));
-        }
-    }
-
-    /**
-     * Add an item to the database in a specified container. Sets the container, screen, cellX and
-     * cellY fields of the item. Also assigns an ID to the item.
-     */
-    static boolean addGestureToDatabase(Context context, ItemInfo item, boolean notify) {
-        final ContentValues values = new ContentValues();
-        final ContentResolver cr = context.getContentResolver();
-
-        item.onAddToDatabase(values);
-
-        Uri result = cr.insert(notify ? LauncherSettings.Gestures.CONTENT_URI :
-                LauncherSettings.Gestures.CONTENT_URI_NO_NOTIFICATION, values);
-
-        if (result != null) {
-            item.id = Integer.parseInt(result.getPathSegments().get(1));
-        }
-
-        return result != null;
-    }
-
-    /**
-     * Update an item to the database in a specified container.
-     */
-    static void updateItemInDatabase(Context context, ItemInfo item) {
-        final ContentValues values = new ContentValues();
-        final ContentResolver cr = context.getContentResolver();
-
-        item.onAddToDatabase(values);
-
-        cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
-    }
-
-    /**
-     * Removes the specified item from the database
-     * @param context
-     * @param item
-     */
-    static void deleteItemFromDatabase(Context context, ItemInfo item) {
-        final ContentResolver cr = context.getContentResolver();
-
-        cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
-    }
-
-
-    /**
-     * Remove the contents of the specified folder from the database
-     */
-    static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
-        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);
-    }
-
-    static void deleteGestureFromDatabase(Context context, ItemInfo item) {
-        final ContentResolver cr = context.getContentResolver();
-
-        cr.delete(LauncherSettings.Gestures.getContentUri(item.id, false), null, null);
-    }
-
-    static void updateGestureInDatabase(Context context, ItemInfo item) {
-        final ContentValues values = new ContentValues();
-        final ContentResolver cr = context.getContentResolver();
-
-        item.onAddToDatabase(values);
-
-        cr.update(LauncherSettings.Gestures.getContentUri(item.id, false), values, null, null);
-    }
-
-
-    ApplicationInfo queryGesture(Context context, String id) {
-        final ContentResolver contentResolver = context.getContentResolver();
-        final PackageManager manager = context.getPackageManager();
-        final Cursor c = contentResolver.query(
-                LauncherSettings.Gestures.CONTENT_URI, null, LauncherSettings.Gestures._ID + "=?",
-                new String[] { id }, null);
-
-        ApplicationInfo info = null;
-
-        try {
-            final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures._ID);
-            final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.INTENT);
-            final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.TITLE);
-            final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ICON_TYPE);
-            final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ICON);
-            final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ICON_PACKAGE);
-            final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ICON_RESOURCE);
-            final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ITEM_TYPE);
-
-            String intentDescription;
-            Intent intent;
-
-            if (c.moveToNext()) {
-                int itemType = c.getInt(itemTypeIndex);
-
-                switch (itemType) {
-                    case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                    case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                        intentDescription = c.getString(intentIndex);
-                        try {
-                            intent = Intent.parseUri(intentDescription, 0);
-                        } catch (java.net.URISyntaxException e) {
-                            return null;
-                        }
-
-                        if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-                            info = getApplicationInfo(manager, intent, context);
-                        } else {
-                            info = getApplicationInfoShortcut(c, context, iconTypeIndex,
-                                    iconPackageIndex, iconResourceIndex, iconIndex);
-                        }
-
-                        if (info == null) {
-                            info = new ApplicationInfo();
-                            info.icon = manager.getDefaultActivityIcon();
-                        }
-
-                        info.isGesture = true;
-                        info.title = c.getString(titleIndex);
-                        info.intent = intent;
-                        info.id = c.getLong(idIndex);
-
-                        break;
-                }
-            }
-        } catch (Exception e) {
-            w(LOG_TAG, "Could not load gesture with name " + id);
-        } finally {
-            c.close();
-        }
-
-        return info;
-    }
+    };
 }
diff --git a/src/com/android/launcher2/LiveFolderAdapter.java b/src/com/android/launcher2/LiveFolderAdapter.java
index b0e9eff..05e7a6a 100644
--- a/src/com/android/launcher2/LiveFolderAdapter.java
+++ b/src/com/android/launcher2/LiveFolderAdapter.java
@@ -154,7 +154,8 @@
                             cursor.getString(holder.iconPackageIndex));
                     final int id = resources.getIdentifier(resource,
                             null, null);
-                    icon = Utilities.createIconThumbnail(resources.getDrawable(id), mContext);
+                    icon = Utilities.createIconThumbnail(resources.getDrawable(id), mContext,
+                            false);
                     mIcons.put(resource, icon);
                 } catch (Exception e) {
                     // Ignore
diff --git a/src/com/android/launcher2/LiveFolderIcon.java b/src/com/android/launcher2/LiveFolderIcon.java
index 1876f2e..d281a64 100644
--- a/src/com/android/launcher2/LiveFolderIcon.java
+++ b/src/com/android/launcher2/LiveFolderIcon.java
@@ -41,8 +41,8 @@
         final Resources resources = launcher.getResources();
         Drawable d = folderInfo.icon;
         if (d == null) {
-            d = Utilities.createIconThumbnail(
-                    resources.getDrawable(R.drawable.ic_launcher_folder), launcher);
+            d = Utilities.createIconThumbnail(resources.getDrawable(R.drawable.ic_launcher_folder),
+                    launcher, false);
             folderInfo.filtered = true;
         }
         icon.setCompoundDrawablesWithIntrinsicBounds(null, d, null, null);
diff --git a/src/com/android/launcher2/UserFolderInfo.java b/src/com/android/launcher2/UserFolderInfo.java
index 40ec9e1..75b19ee 100644
--- a/src/com/android/launcher2/UserFolderInfo.java
+++ b/src/com/android/launcher2/UserFolderInfo.java
@@ -43,7 +43,7 @@
     }
     
     /**
-     * Remove an app or shortcut
+     * Remove an app or shortcut. Does not change the DB.
      * 
      * @param item
      */
diff --git a/src/com/android/launcher2/Utilities.java b/src/com/android/launcher2/Utilities.java
index 9e715d1..53e42e3 100644
--- a/src/com/android/launcher2/Utilities.java
+++ b/src/com/android/launcher2/Utilities.java
@@ -78,82 +78,101 @@
      * The size of the thumbnail is defined by the dimension
      * android.R.dimen.launcher_application_icon_size.
      *
-     * This method is not thread-safe and should be invoked on the UI thread only.
-     *
      * @param icon The icon to get a thumbnail of.
      * @param context The application's context.
+     * @param forceBitmap If this is true, the drawable will always be redrawn
+     *          into a new bitmap if it isn't already a BitmapDrawable or a
+     *          FastBitmapDrawable.
      *
      * @return A thumbnail for the specified icon or the icon itself if the
      *         thumbnail could not be created. 
      */
-    static Drawable createIconThumbnail(Drawable icon, Context context) {
-        if (sIconWidth == -1) {
-            final Resources resources = context.getResources();
-            sIconWidth = sIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
-        }
-
-        int width = sIconWidth;
-        int height = sIconHeight;
-
-        float scale = 1.0f;
-        if (icon instanceof PaintDrawable) {
-            PaintDrawable painter = (PaintDrawable) icon;
-            painter.setIntrinsicWidth(width);
-            painter.setIntrinsicHeight(height);
-        } else if (icon instanceof BitmapDrawable) {
-            // Ensure the bitmap has a density.
-            BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
-            Bitmap bitmap = bitmapDrawable.getBitmap();
-            if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
-                bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
+    static Drawable createIconThumbnail(Drawable icon, Context context, boolean forceBitmap) {
+        synchronized (sCanvas) { // we share the statics :-(
+            if (sIconWidth == -1) {
+                final Resources resources = context.getResources();
+                sIconWidth = (int)resources.getDimension(android.R.dimen.app_icon_size);
+                sIconHeight = sIconWidth;
             }
-        }
-        int iconWidth = icon.getIntrinsicWidth();
-        int iconHeight = icon.getIntrinsicHeight();
 
-        if (width > 0 && height > 0) {
-            if (width < iconWidth || height < iconHeight || scale != 1.0f) {
-                final float ratio = (float) iconWidth / iconHeight;
+            int width = sIconWidth;
+            int height = sIconHeight;
 
-                if (iconWidth > iconHeight) {
-                    height = (int) (width / ratio);
-                } else if (iconHeight > iconWidth) {
-                    width = (int) (height * ratio);
+            float scale = 1.0f;
+            if (icon instanceof PaintDrawable) {
+                PaintDrawable painter = (PaintDrawable) icon;
+                painter.setIntrinsicWidth(width);
+                painter.setIntrinsicHeight(height);
+            } else if (icon instanceof BitmapDrawable) {
+                // Ensure the bitmap has a density.
+                BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
+                Bitmap bitmap = bitmapDrawable.getBitmap();
+                if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
+                    bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
                 }
+            }
+            int iconWidth = icon.getIntrinsicWidth();
+            int iconHeight = icon.getIntrinsicHeight();
 
-                final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ?
-                            Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
-                final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
-                final Canvas canvas = sCanvas;
-                canvas.setBitmap(thumb);
-                // Copy the old bounds to restore them later
-                // If we were to do oldBounds = icon.getBounds(),
-                // the call to setBounds() that follows would
-                // change the same instance and we would lose the
-                // old bounds
-                sOldBounds.set(icon.getBounds());
-                final int x = (sIconWidth - width) / 2;
-                final int y = (sIconHeight - height) / 2;
-                icon.setBounds(x, y, x + width, y + height);
-                icon.draw(canvas);
-                icon.setBounds(sOldBounds);
-                icon = new FastBitmapDrawable(thumb);
-            } else if (iconWidth < width && iconHeight < height) {
+            if (iconWidth > 0 && iconWidth > 0) {
+                if (width < iconWidth || height < iconHeight || scale != 1.0f) {
+                    final float ratio = (float) iconWidth / iconHeight;
+
+                    if (iconWidth > iconHeight) {
+                        height = (int) (width / ratio);
+                    } else if (iconHeight > iconWidth) {
+                        width = (int) (height * ratio);
+                    }
+
+                    final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ?
+                                Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
+                    final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
+                    final Canvas canvas = sCanvas;
+                    canvas.setBitmap(thumb);
+                    // Copy the old bounds to restore them later
+                    // If we were to do oldBounds = icon.getBounds(),
+                    // the call to setBounds() that follows would
+                    // change the same instance and we would lose the
+                    // old bounds
+                    sOldBounds.set(icon.getBounds());
+                    final int x = (sIconWidth - width) / 2;
+                    final int y = (sIconHeight - height) / 2;
+                    icon.setBounds(x, y, x + width, y + height);
+                    icon.draw(canvas);
+                    icon.setBounds(sOldBounds);
+                    icon = new FastBitmapDrawable(thumb);
+                } else if (iconWidth < width && iconHeight < height) {
+                    final Bitmap.Config c = Bitmap.Config.ARGB_8888;
+                    final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
+                    final Canvas canvas = sCanvas;
+                    canvas.setBitmap(thumb);
+                    sOldBounds.set(icon.getBounds());
+                    final int x = (width - iconWidth) / 2;
+                    final int y = (height - iconHeight) / 2;
+                    icon.setBounds(x, y, x + iconWidth, y + iconHeight);
+                    icon.draw(canvas);
+                    icon.setBounds(sOldBounds);
+                    icon = new FastBitmapDrawable(thumb);
+                }
+            }
+            
+            if (forceBitmap) {
+                // no intrinsic size --> use default size
+                int w = sIconWidth;
+                int h = sIconHeight;
                 final Bitmap.Config c = Bitmap.Config.ARGB_8888;
-                final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
+                final Bitmap thumb = Bitmap.createBitmap(roundToPow2(w), roundToPow2(h), c);
                 final Canvas canvas = sCanvas;
                 canvas.setBitmap(thumb);
                 sOldBounds.set(icon.getBounds());
-                final int x = (width - iconWidth) / 2;
-                final int y = (height - iconHeight) / 2;
-                icon.setBounds(x, y, x + iconWidth, y + iconHeight);
+                icon.setBounds(0, 0, w, h);
                 icon.draw(canvas);
                 icon.setBounds(sOldBounds);
                 icon = new FastBitmapDrawable(thumb);
             }
-        }
 
-        return icon;
+            return icon;
+        }
     }
 
     /**
@@ -161,8 +180,6 @@
      * The size of the thumbnail is defined by the dimension
      * android.R.dimen.launcher_application_icon_size.
      *
-     * This method is not thread-safe and should be invoked on the UI thread only.
-     *
      * @param bitmap The bitmap to get a thumbnail of.
      * @param context The application's context.
      *
@@ -170,42 +187,44 @@
      *         thumbnail could not be created.
      */
     static Bitmap createBitmapThumbnail(Bitmap bitmap, Context context) {
-        if (sIconWidth == -1) {
-            final Resources resources = context.getResources();
-            sIconWidth = sIconHeight = (int) resources.getDimension(
-                    android.R.dimen.app_icon_size);
-        }
-
-        int width = sIconWidth;
-        int height = sIconHeight;
-
-        final int bitmapWidth = bitmap.getWidth();
-        final int bitmapHeight = bitmap.getHeight();
-
-        if (width > 0 && height > 0 && (width < bitmapWidth || height < bitmapHeight)) {
-            final float ratio = (float) bitmapWidth / bitmapHeight;
-
-            if (bitmapWidth > bitmapHeight) {
-                height = (int) (width / ratio);
-            } else if (bitmapHeight > bitmapWidth) {
-                width = (int) (height * ratio);
+        synchronized (sCanvas) { // we share the statics :-(
+            if (sIconWidth == -1) {
+                final Resources resources = context.getResources();
+                sIconWidth = sIconHeight = (int) resources.getDimension(
+                        android.R.dimen.app_icon_size);
             }
 
-            final Bitmap.Config c = (width == sIconWidth && height == sIconHeight) ?
-                    bitmap.getConfig() : Bitmap.Config.ARGB_8888;
-            final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
-            final Canvas canvas = sCanvas;
-            final Paint paint = sPaint;
-            canvas.setBitmap(thumb);
-            paint.setDither(false);
-            paint.setFilterBitmap(true);
-            sBounds.set((sIconWidth - width) / 2, (sIconHeight - height) / 2, width, height);
-            sOldBounds.set(0, 0, bitmapWidth, bitmapHeight);
-            canvas.drawBitmap(bitmap, sOldBounds, sBounds, paint);
-            return thumb;
-        }
+            int width = sIconWidth;
+            int height = sIconHeight;
 
-        return bitmap;
+            final int bitmapWidth = bitmap.getWidth();
+            final int bitmapHeight = bitmap.getHeight();
+
+            if (width > 0 && height > 0 && (width < bitmapWidth || height < bitmapHeight)) {
+                final float ratio = (float) bitmapWidth / bitmapHeight;
+
+                if (bitmapWidth > bitmapHeight) {
+                    height = (int) (width / ratio);
+                } else if (bitmapHeight > bitmapWidth) {
+                    width = (int) (height * ratio);
+                }
+
+                final Bitmap.Config c = (width == sIconWidth && height == sIconHeight) ?
+                        bitmap.getConfig() : Bitmap.Config.ARGB_8888;
+                final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
+                final Canvas canvas = sCanvas;
+                final Paint paint = sPaint;
+                canvas.setBitmap(thumb);
+                paint.setDither(false);
+                paint.setFilterBitmap(true);
+                sBounds.set((sIconWidth - width) / 2, (sIconHeight - height) / 2, width, height);
+                sOldBounds.set(0, 0, bitmapWidth, bitmapHeight);
+                canvas.drawBitmap(bitmap, sOldBounds, sBounds, paint);
+                return thumb;
+            }
+
+            return bitmap;
+        }
     }
 
     static class BubbleText {
@@ -261,7 +280,6 @@
             mFirstLineY = (int)(leading + ascent + 0.5f);
             mLineHeight = (int)(leading + ascent + descent + 0.5f);
 
-            roundToPow2(64);
             mBitmapWidth = roundToPow2((int)(mBubbleRect.width() + 0.5f));
             mBitmapHeight = roundToPow2((int)((MAX_LINES * mLineHeight) + leading + 0.5f));
 
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 759deb6..8b00483 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.ComponentName;
 import android.content.res.TypedArray;
+import android.content.pm.PackageManager;
 import android.graphics.Canvas;
 import android.graphics.RectF;
 import android.graphics.Rect;
@@ -100,7 +101,6 @@
     private int[] mTempEstimate = new int[2];
 
     private boolean mAllowLongPress;
-    private boolean mLocked;
 
     private int mTouchSlop;
     private int mMaximumVelocity;
@@ -642,10 +642,7 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        Log.d(Launcher.LOG_TAG, "Workspace onIntercept " + ev + " mLocked=" + mLocked
-                + " mLauncher.isDrawerDown()=" + mLauncher.isDrawerDown());
-        if (mLocked || !mLauncher.isDrawerDown()) {
-            Log.d(Launcher.LOG_TAG, "returning false");
+        if (mLauncher.isWorkspaceLocked() || !mLauncher.isDrawerDown()) {
             return false; // We don't want the events.  Let them fall through to the all apps view.
         }
 
@@ -755,9 +752,7 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
 
-        Log.d(Launcher.LOG_TAG, "Workspace onTouchEvent " + ev);
-
-        if (mLocked || !mLauncher.isDrawerDown()) {
+        if (mLauncher.isWorkspaceLocked() || !mLauncher.isDrawerDown()) {
             return false; // We don't want the events.  Let them fall through to the all apps view.
         }
 
@@ -990,8 +985,6 @@
         cellLayout.onDropChild(view, mTargetCell);
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
 
-        final LauncherModel model = Launcher.getModel();
-        model.addDesktopItem(info);
         LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
                 LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, lp.cellX, lp.cellY);
     }
@@ -1088,7 +1081,6 @@
                     mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
                 }
                 final Object tag = mDragInfo.cell.getTag();
-                Launcher.getModel().removeDesktopItem((ItemInfo) tag);
             }
         } else {
             if (mDragInfo != null) {
@@ -1187,24 +1179,6 @@
     }
 
     /**
-     * Unlocks the SlidingDrawer so that touch events are processed.
-     *
-     * @see #lock()
-     */
-    public void unlock() {
-        mLocked = false;
-    }
-
-    /**
-     * Locks the SlidingDrawer so that touch events are ignores.
-     *
-     * @see #unlock()
-     */
-    public void lock() {
-        mLocked = true;
-    }
-    
-    /**
      * @return True is long presses are still allowed for the current touch
      */
     public boolean allowLongPress() {
@@ -1221,7 +1195,6 @@
 
     void removeShortcutsForPackage(String packageName) {
         final ArrayList<View> childrenToRemove = new ArrayList<View>();
-        final LauncherModel model = Launcher.getModel();
         final int count = getChildCount();
 
         for (int i = 0; i < count; i++) {
@@ -1244,7 +1217,6 @@
 
                     if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
                             name != null && packageName.equals(name.getPackageName())) {
-                        model.removeDesktopItem(info);
                         LauncherModel.deleteItemFromDatabase(mLauncher, info);
                         childrenToRemove.add(view);
                     }
@@ -1293,6 +1265,8 @@
     }
 
     void updateShortcutsForPackage(String packageName) {
+        final PackageManager pm = mLauncher.getPackageManager();
+
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
             final CellLayout layout = (CellLayout) getChildAt(i);
@@ -1311,11 +1285,10 @@
                             Intent.ACTION_MAIN.equals(intent.getAction()) && name != null &&
                             packageName.equals(name.getPackageName())) {
 
-                        final Drawable icon = Launcher.getModel().getApplicationInfoIcon(
-                                mLauncher.getPackageManager(), info);
+                        final Drawable icon = AppInfoCache.getIconDrawable(pm, info);
                         if (icon != null && icon != info.icon) {
                             info.icon.setCallback(null);
-                            info.icon = Utilities.createIconThumbnail(icon, mContext);
+                            info.icon = Utilities.createIconThumbnail(icon, mContext, false);
                             info.filtered = true;
                             ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(null,
                                     info.icon, null, null);