Redo the launcher loading code and put the real app icons into rollo.
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);
+    }
+
 }