auto import from //branches/cupcake_rel/...@140373
diff --git a/src/com/android/launcher/AddAdapter.java b/src/com/android/launcher/AddAdapter.java
index cbcb338..996c619 100644
--- a/src/com/android/launcher/AddAdapter.java
+++ b/src/com/android/launcher/AddAdapter.java
@@ -79,8 +79,10 @@
         mItems.add(new ListItem(res, R.string.group_shortcuts,
                 R.drawable.ic_launcher_empty, ITEM_SHORTCUT));
         
-        mItems.add(new ListItem(res, R.string.group_search,
-                R.drawable.ic_search_widget, ITEM_SEARCH));
+        if (false) {
+            mItems.add(new ListItem(res, R.string.group_search,
+                    R.drawable.ic_search_widget, ITEM_SEARCH));
+        }
         
         mItems.add(new ListItem(res, R.string.group_widgets,
                 R.drawable.ic_launcher_appwidget, ITEM_APPWIDGET));
diff --git a/src/com/android/launcher/Launcher.java b/src/com/android/launcher/Launcher.java
index 17f16a7..50c785d 100644
--- a/src/com/android/launcher/Launcher.java
+++ b/src/com/android/launcher/Launcher.java
@@ -57,6 +57,7 @@
 import android.text.TextUtils;
 import android.text.method.TextKeyListener;
 import android.util.Log;
+import static android.util.Log.*;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -112,6 +113,9 @@
 
     static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
 
+    static final String EXTRA_CUSTOM_WIDGET = "custom_widget";
+    static final String SEARCH_WIDGET = "search_widget";
+
     static final int SCREEN_COUNT = 3;
     static final int DEFAULT_SCREN = 1;
     static final int NUMBER_CELLS_X = 4;
@@ -603,7 +607,7 @@
         Bundle extras = data.getExtras();
         int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
         
-        Log.d(LOG_TAG, "dumping extras content="+extras.toString());
+        d(LOG_TAG, "dumping extras content="+extras.toString());
         
         AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
         
@@ -671,7 +675,7 @@
                     final int id = resources.getIdentifier(iconResource.resourceName, null, null);
                     icon = resources.getDrawable(id);
                 } catch (Exception e) {
-                    Log.w(LOG_TAG, "Could not load shortcut icon: " + extra);
+                    w(LOG_TAG, "Could not load shortcut icon: " + extra);
                 }
             }
         }
@@ -794,7 +798,7 @@
         try {
             mAppWidgetHost.stopListening();
         } catch (NullPointerException ex) {
-            Log.w(LOG_TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
+            w(LOG_TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
         }
 
         TextKeyListener.getInstance().release();
@@ -903,21 +907,36 @@
         }
     }
     
+    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);
-        AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
 
-        if (appWidget.configure != null) {
-            // Launch over to configure widget, if needed
-            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
-            intent.setComponent(appWidget.configure);
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
-
-            startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);
+        String customWidget = data.getStringExtra(EXTRA_CUSTOM_WIDGET);
+        if (SEARCH_WIDGET.equals(customWidget)) {
+            // We don't need this any more, since this isn't a real app widget.
+            mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+            // add the search widget
+            addSearch();
         } else {
-            // Otherwise just add it
-            onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
+            AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+
+            if (appWidget.configure != null) {
+                // Launch over to configure widget, if needed
+                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
+                intent.setComponent(appWidget.configure);
+                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+
+                startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);
+            } else {
+                // Otherwise just add it
+                onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
+            }
         }
     }
     
@@ -1008,7 +1027,7 @@
                 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
                 icon = resources.getDrawable(id);
             } catch (Exception e) {
-                Log.w(LOG_TAG, "Could not load live folder icon: " + extra);
+                w(LOG_TAG, "Could not load live folder icon: " + extra);
             }
         }
 
@@ -1107,8 +1126,11 @@
             switch (event.getKeyCode()) {
                 case KeyEvent.KEYCODE_BACK:
                     mWorkspace.dispatchKeyEvent(event); 
-                    closeFolder();
-                    closeDrawer();
+                    if (mDrawer.isOpened()) {
+                        closeDrawer();
+                    } else {
+                        closeFolder();                        
+                    }
                     return true;
                 case KeyEvent.KEYCODE_HOME:
                     return true;
@@ -1163,7 +1185,7 @@
 
     void onDesktopItemsLoaded() {
         if (mDestroyed) return;
-android.util.Log.d("Home", "setting grid adapter");
+d("Home", "setting grid adapter");
         mAllAppsGrid.setAdapter(sModel.getApplicationsAdapter());
         bindDesktopItems();
     }
@@ -1313,7 +1335,7 @@
             final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
             item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
             
-            if (LOGD) Log.d(LOG_TAG, String.format("about to setAppWidget for id=%d, info=%s", 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);
@@ -1704,6 +1726,23 @@
                         
                         Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
                         pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+                        // add the search widget
+                        ArrayList<AppWidgetProviderInfo> customInfo =
+                                new ArrayList<AppWidgetProviderInfo>();
+                        AppWidgetProviderInfo info = new AppWidgetProviderInfo();
+                        info.provider = new ComponentName(getPackageName(), "XXX.YYY");
+                        info.label = getString(R.string.group_search);
+                        info.icon = R.drawable.ic_search_widget;
+                        customInfo.add(info);
+                        pickIntent.putParcelableArrayListExtra(
+                                AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo);
+                        ArrayList<Bundle> customExtras = new ArrayList<Bundle>();
+                        Bundle b = new Bundle();
+                        b.putString(EXTRA_CUSTOM_WIDGET, SEARCH_WIDGET);
+                        customExtras.add(b);
+                        pickIntent.putParcelableArrayListExtra(
+                                AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras);
+                        // start the pick activity
                         startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
                         break;
                     }
@@ -1742,23 +1781,47 @@
     private class ApplicationsIntentReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            boolean reloadWorkspace = false;
-android.util.Log.d("Home", "application intent received: " + intent.getAction());
-android.util.Log.d("Home", "  --> " + intent.getData());
-            if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
-                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
-                    removeShortcutsForPackage(intent.getData().getSchemeSpecificPart());
-                } else {
-                    reloadWorkspace = true;
-                }
+            final String action = intent.getAction();
+            final String packageName = intent.getData().getSchemeSpecificPart();
+            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+
+            if (LauncherModel.DEBUG_LOADERS) {
+                d(LauncherModel.LOG_TAG, "application intent received: " + action +
+                        ", replacing=" + replacing);
+                d(LauncherModel.LOG_TAG, "  --> " + intent.getData());
             }
-            removeDialog(DIALOG_CREATE_SHORTCUT);
-            if (!reloadWorkspace) {
-android.util.Log.d("Home", "  --> loading apps");
-                sModel.loadApplications(false, Launcher.this, false);
+
+            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);
+                    }
+                }
+                removeDialog(DIALOG_CREATE_SHORTCUT);
             } else {
-android.util.Log.d("Home", "  --> loading workspace");
-                sModel.loadUserItems(false, Launcher.this, false, true);
+                if (LauncherModel.DEBUG_LOADERS) {
+                    d(LauncherModel.LOG_TAG, "  --> sync package " + packageName);
+                }
+                sModel.syncPackage(Launcher.this, packageName);
             }
         }
     }
diff --git a/src/com/android/launcher/LauncherModel.java b/src/com/android/launcher/LauncherModel.java
index 70b4c10..de84560 100644
--- a/src/com/android/launcher/LauncherModel.java
+++ b/src/com/android/launcher/LauncherModel.java
@@ -28,8 +28,9 @@
 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 static android.util.Log.*;
 import android.os.Process;
 
 import java.util.ArrayList;
@@ -46,13 +47,14 @@
  * for the Launcher.
  */
 public class LauncherModel {
+    static final boolean DEBUG_LOADERS = false;
+    static final String LOG_TAG = "HomeLoaders";
+
     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 static final boolean DEBUG = false;
-
     private static final Collator sCollator = Collator.getInstance();
 
     private boolean mApplicationsLoaded;
@@ -101,14 +103,16 @@
      */
     synchronized boolean loadApplications(boolean isLaunching, Launcher launcher,
             boolean localeChanged) {
-android.util.Log.d("Home", "load applications");
+
+        if (DEBUG_LOADERS) d(LOG_TAG, "load applications");
+
         if (isLaunching && mApplicationsLoaded && !localeChanged) {
             mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
-android.util.Log.d("Home", "  --> applications loaded, return");
+            if (DEBUG_LOADERS) d(LOG_TAG, "  --> applications loaded, return");
             return false;
         }
 
-        waitForApplicationsLoader();
+        stopAndWaitForApplicationsLoader();
 
         if (localeChanged) {
             dropApplicationCache();
@@ -129,9 +133,9 @@
         return true;
     }
 
-    private synchronized void waitForApplicationsLoader() {
+    private synchronized void stopAndWaitForApplicationsLoader() {
         if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
-            android.util.Log.d("Home", "  --> wait for applications loader");
+            if (DEBUG_LOADERS) d(LOG_TAG, "  --> wait for applications loader");
 
             mApplicationsLoader.stop();
             // Wait for the currently running thread to finish, this can take a little
@@ -145,14 +149,310 @@
     }
 
     private synchronized void startApplicationsLoader(Launcher launcher) {
-android.util.Log.d("Home", "  --> starting applications loader");
-        waitForApplicationsLoader();
+        if (DEBUG_LOADERS) d(LOG_TAG, "  --> starting applications loader");
+
+        stopAndWaitForApplicationsLoader();
 
         mApplicationsLoader = new ApplicationsLoader(launcher);
         mApplicationsLoaderThread = new Thread(mApplicationsLoader, "Applications Loader");
         mApplicationsLoaderThread.start();
     }
 
+    synchronized void addPackage(Launcher launcher, String packageName) {
+        if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
+            startApplicationsLoader(launcher);
+            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));
+                }
+
+                adapter.sort(new ApplicationInfoComparator());
+                adapter.notifyDataSetChanged();
+            }
+        }
+    }
+
+    synchronized void removePackage(Launcher launcher, String packageName) {
+        if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
+            dropApplicationCache(); // TODO: this could be optimized
+            startApplicationsLoader(launcher);
+            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);
+            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);
+                    changed = true;
+                }
+            }
+
+            if (changed) {
+                adapter.sort(new ApplicationInfoComparator());
+                adapter.notifyDataSetChanged();
+            }
+        }
+    }
+
+    private void updateAndCacheApplicationInfo(PackageManager packageManager, ResolveInfo info,
+            ApplicationInfo applicationInfo) {
+
+        updateApplicationInfoTitleAndIcon(packageManager, info, applicationInfo);
+
+        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);
+            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;
+
+                // 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);
+
+                if (added || removed) {
+                    adapter.sort(new ApplicationInfoComparator());
+                    adapter.notifyDataSetChanged();
+                }
+            }
+        }
+    }
+
+    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));
+                changed = true;
+            } else {
+                updateAndCacheApplicationInfo(launcher.getPackageManager(), info, applicationInfo);
+                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;
+            }
+        }
+
+        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;
+            }
+        }
+        return false;
+    }
+
+    Drawable getApplicationInfoIcon(PackageManager manager, ApplicationInfo info) {
+        final ResolveInfo resolveInfo = manager.resolveActivity(info.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);
+        }
+
+        return application.icon;
+    }
+
+    private static ApplicationInfo makeAndCacheApplicationInfo(PackageManager manager,
+            HashMap<ComponentName, ApplicationInfo> appInfoCache, ResolveInfo info) {
+
+        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);
+
+            application.setActivity(componentName,
+                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+            appInfoCache.put(componentName, application);
+        }
+
+        return application;
+    }
+
+    private static void updateApplicationInfoTitleAndIcon(PackageManager manager, ResolveInfo info,
+            ApplicationInfo application) {
+
+        application.title = info.loadLabel(manager);
+        if (application.title == null) {
+            application.title = info.activityInfo.name;
+        }
+
+        application.icon = info.activityInfo.loadIcon(manager);
+        application.filtered = false;
+    }
+
     private class ApplicationsLoader implements Runnable {
         private final WeakReference<Launcher> mLauncher;
 
@@ -176,7 +476,7 @@
 
             android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
 
-            Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+            final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
             mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
 
             final Launcher launcher = mLauncher.get();
@@ -185,6 +485,8 @@
 
             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);
@@ -192,28 +494,10 @@
 
                 for (int i = 0; i < count && !mStopped; i++) {
                     ResolveInfo info = apps.get(i);
-                    ComponentName componentName = new ComponentName(
-                            info.activityInfo.applicationInfo.packageName,
-                            info.activityInfo.name);
-                    ApplicationInfo application = appInfoCache.get(componentName);
-                    if (application == null) {
-                        application = new ApplicationInfo();
-                        application.title = info.loadLabel(manager);
-                        if (application.title == null) {
-                            application.title = info.activityInfo.name;
-                        }
-                        application.setActivity(componentName,
-                                Intent.FLAG_ACTIVITY_NEW_TASK |
-                                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                        application.container = ItemInfo.NO_ID;
-                        application.icon = info.activityInfo.loadIcon(manager);
-                        if (DEBUG) {
-                            Log.d(Launcher.LOG_TAG, "Loaded ApplicationInfo for " + componentName);
-                        }
-                        appInfoCache.put(componentName, application);
-                    }
+                    ApplicationInfo application =
+                            makeAndCacheApplicationInfo(manager, appInfoCache, info);
 
-                    if (action.add(application)) {
+                    if (action.add(application) && !mStopped) {
                         launcher.runOnUiThread(action);
                         action = new ChangeNotifier(applicationList, false);
                     }
@@ -229,7 +513,7 @@
         }
     }
 
-    private static class ChangeNotifier implements Runnable, Comparator<ApplicationInfo> {
+    private static class ChangeNotifier implements Runnable {
         private final ApplicationsAdapter mApplicationList;
         private final ArrayList<ApplicationInfo> mBuffer;
 
@@ -243,6 +527,8 @@
 
         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);
@@ -260,7 +546,7 @@
 
             buffer.clear();
 
-            applicationList.sort(this);
+            applicationList.sort(new ApplicationInfoComparator());
             applicationList.notifyDataSetChanged();
         }
 
@@ -269,7 +555,9 @@
             buffer.add(application);
             return buffer.size() >= UI_NOTIFICATION_RATE;
         }
+    }
 
+    private static class ApplicationInfoComparator implements Comparator<ApplicationInfo> {
         public final int compare(ApplicationInfo a, ApplicationInfo b) {
             return sCollator.compare(a.title.toString(), b.title.toString());
         }
@@ -285,10 +573,10 @@
      */
     void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged,
             boolean loadApplications) {
-android.util.Log.d("Home", "loading user items");
+        if (DEBUG_LOADERS) d(LOG_TAG, "loading user items");
 
         if (isLaunching && isDesktopLoaded()) {
-android.util.Log.d("Home", "  --> items loaded, return");
+            if (DEBUG_LOADERS) d(LOG_TAG, "  --> items loaded, return");
             if (loadApplications) startApplicationsLoader(launcher);
             // We have already loaded our data from the DB
             launcher.onDesktopItemsLoaded();
@@ -306,7 +594,7 @@
             }
         }
 
-android.util.Log.d("Home", "  --> starting workspace loader");
+        if (DEBUG_LOADERS) d(LOG_TAG, "  --> starting workspace loader");
         mDesktopItemsLoaded = false;
         mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications);
         mDesktopLoaderThread = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
@@ -563,7 +851,7 @@
 
                             container = c.getInt(containerIndex);
                             if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                                Log.e(Launcher.LOG_TAG, "Widget found where container "
+                                e(Launcher.LOG_TAG, "Widget found where container "
                                         + "!= CONTAINER_DESKTOP  ignoring!");
                                 continue;
                             }
@@ -589,7 +877,7 @@
 
                             container = c.getInt(containerIndex);
                             if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                                Log.e(Launcher.LOG_TAG, "Widget found where container "
+                                e(Launcher.LOG_TAG, "Widget found where container "
                                         + "!= CONTAINER_DESKTOP -- ignoring!");
                                 continue;
                             }
@@ -599,7 +887,7 @@
                             break;
                         }
                     } catch (Exception e) {
-                        Log.w(Launcher.LOG_TAG, "Desktop items loading interrupted:", e);
+                        w(Launcher.LOG_TAG, "Desktop items loading interrupted:", e);
                     }
                 }
             } finally {
@@ -699,6 +987,8 @@
      * Home screen on orientation change.
      */
     void unbind() {
+        // Interrupt the applications loader before setting the adapter to null
+        stopAndWaitForApplicationsLoader();
         mApplicationsAdapter = null;
         unbindAppDrawables(mApplications);
         unbindDrawables(mDesktopItems);
diff --git a/src/com/android/launcher/LauncherProvider.java b/src/com/android/launcher/LauncherProvider.java
index 5cd7a0f..21ef1e0 100644
--- a/src/com/android/launcher/LauncherProvider.java
+++ b/src/com/android/launcher/LauncherProvider.java
@@ -24,6 +24,8 @@
 import android.content.ComponentName;
 import android.content.ContentUris;
 import android.content.ContentResolver;
+import android.content.res.XmlResourceParser;
+import android.content.res.TypedArray;
 import android.content.pm.PackageManager;
 import android.content.pm.ActivityInfo;
 import android.database.sqlite.SQLiteOpenHelper;
@@ -33,19 +35,17 @@
 import android.database.SQLException;
 import android.util.Log;
 import android.util.Xml;
+import android.util.AttributeSet;
 import android.net.Uri;
 import android.text.TextUtils;
 import android.os.*;
 import android.provider.Settings;
 
-import java.io.FileReader;
-import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.ArrayList;
 
-import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParser;
 import com.android.internal.util.XmlUtils;
 import com.android.launcher.LauncherSettings.Favorites;
 
@@ -162,19 +162,10 @@
     }
 
     private static class DatabaseHelper extends SQLiteOpenHelper {
-        /**
-         * Path to file containing default favorite packages, relative to ANDROID_ROOT.
-         */
-        private static final String DEFAULT_FAVORITES_PATH = "etc/favorites.xml";
-
         private static final String TAG_FAVORITES = "favorites";
         private static final String TAG_FAVORITE = "favorite";
-        private static final String TAG_PACKAGE = "package";
-        private static final String TAG_CLASS = "class";
-
-        private static final String ATTRIBUTE_SCREEN = "screen";
-        private static final String ATTRIBUTE_X = "x";
-        private static final String ATTRIBUTE_Y = "y";
+        private static final String TAG_CLOCK = "clock";
+        private static final String TAG_SEARCH = "search";
 
         private final Context mContext;
         private final AppWidgetHost mAppWidgetHost;
@@ -217,7 +208,7 @@
             
             if (!convertDatabase(db)) {
                 // Populate favorites table with initial favorites
-                loadFavorites(db, DEFAULT_FAVORITES_PATH);
+                loadFavorites(db);
             }
         }
 
@@ -445,115 +436,121 @@
          * Loads the default set of favorite packages from an xml file.
          *
          * @param db The database to write the values into
-         * @param subPath The relative path from ANDROID_ROOT to the file to read
          */
-        private int loadFavorites(SQLiteDatabase db, String subPath) {
-            FileReader favReader;
-
-            // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
-            final File favFile = new File(Environment.getRootDirectory(), subPath);
-            try {
-                favReader = new FileReader(favFile);
-            } catch (FileNotFoundException e) {
-                Log.e(LOG_TAG, "Couldn't find or open favorites file " + favFile);
-                return 0;
-            }
-
+        private int loadFavorites(SQLiteDatabase db) {
             Intent intent = new Intent(Intent.ACTION_MAIN, null);
             intent.addCategory(Intent.CATEGORY_LAUNCHER);
             ContentValues values = new ContentValues();
 
             PackageManager packageManager = mContext.getPackageManager();
-            ActivityInfo info;
             int i = 0;
             try {
-                XmlPullParser parser = Xml.newPullParser();
-                parser.setInput(favReader);
-
+                XmlResourceParser parser = mContext.getResources().getXml(R.xml.default_workspace);
+                AttributeSet attrs = Xml.asAttributeSet(parser);
                 XmlUtils.beginDocument(parser, TAG_FAVORITES);
 
-                while (true) {
-                    XmlUtils.nextElement(parser);
+                final int depth = parser.getDepth();
 
-                    String name = parser.getName();
-                    if (!TAG_FAVORITE.equals(name)) {
-                        break;
+                int type;
+                while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                        parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+
+                    if (type != XmlPullParser.START_TAG) {
+                        continue;
                     }
 
-                    String pkg = parser.getAttributeValue(null, TAG_PACKAGE);
-                    String cls = parser.getAttributeValue(null, TAG_CLASS);
-                    try {
-                        ComponentName cn = new ComponentName(pkg, cls);
-                        info = packageManager.getActivityInfo(cn, 0);
-                        intent.setComponent(cn);
-                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                        values.put(LauncherSettings.Favorites.INTENT, intent.toURI());
-                        values.put(LauncherSettings.Favorites.TITLE,
-                                info.loadLabel(packageManager).toString());
-                        values.put(LauncherSettings.Favorites.CONTAINER,
-                                LauncherSettings.Favorites.CONTAINER_DESKTOP);
-                        values.put(LauncherSettings.Favorites.ITEM_TYPE,
-                                LauncherSettings.Favorites.ITEM_TYPE_APPLICATION);
-                        values.put(LauncherSettings.Favorites.SCREEN,
-                                parser.getAttributeValue(null, ATTRIBUTE_SCREEN));
-                        values.put(LauncherSettings.Favorites.CELLX,
-                                parser.getAttributeValue(null, ATTRIBUTE_X));
-                        values.put(LauncherSettings.Favorites.CELLY,
-                                parser.getAttributeValue(null, ATTRIBUTE_Y));
-                        values.put(LauncherSettings.Favorites.SPANX, 1);
-                        values.put(LauncherSettings.Favorites.SPANY, 1);
-                        db.insert(TABLE_FAVORITES, null, values);
-                        i++;
-                    } catch (PackageManager.NameNotFoundException e) {
-                        Log.w(LOG_TAG, "Unable to add favorite: " + pkg + "/" + cls, e);
+                    boolean added = false;
+                    final String name = parser.getName();
+
+                    TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
+
+                    values.clear();                    
+                    values.put(LauncherSettings.Favorites.CONTAINER,
+                            LauncherSettings.Favorites.CONTAINER_DESKTOP);
+                    values.put(LauncherSettings.Favorites.SCREEN,
+                            a.getString(R.styleable.Favorite_screen));
+                    values.put(LauncherSettings.Favorites.CELLX,
+                            a.getString(R.styleable.Favorite_x));
+                    values.put(LauncherSettings.Favorites.CELLY,
+                            a.getString(R.styleable.Favorite_y));
+
+                    if (TAG_FAVORITE.equals(name)) {
+                        added = addShortcut(db, values, a, packageManager, intent);
+                    } else if (TAG_SEARCH.equals(name)) {
+                        added = addSearchWidget(db, values);
+                    } else if (TAG_CLOCK.equals(name)) {
+                        added = addClockWidget(db, values);
                     }
+
+                    if (added) i++;
+
+                    a.recycle();
                 }
             } catch (XmlPullParserException e) {
                 Log.w(LOG_TAG, "Got exception parsing favorites.", e);
             } catch (IOException e) {
                 Log.w(LOG_TAG, "Got exception parsing favorites.", e);
             }
-            
+
+            return i;
+        }
+
+        private boolean addShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
+                PackageManager packageManager, Intent intent) {
+
+            ActivityInfo info;
+            String packageName = a.getString(R.styleable.Favorite_packageName);
+            String className = a.getString(R.styleable.Favorite_className);
+            try {
+                ComponentName cn = new ComponentName(packageName, className);
+                info = packageManager.getActivityInfo(cn, 0);
+                intent.setComponent(cn);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                values.put(Favorites.INTENT, intent.toURI());
+                values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
+                values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
+                values.put(Favorites.SPANX, 1);
+                values.put(Favorites.SPANY, 1);
+                db.insert(TABLE_FAVORITES, null, values);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.w(LOG_TAG, "Unable to add favorite: " + packageName +
+                        "/" + className, e);
+                return false;
+            }
+            return true;
+        }
+
+        private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
             // Add a search box
-            values.clear();
-            values.put(LauncherSettings.Favorites.CONTAINER,
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP);
-            values.put(LauncherSettings.Favorites.ITEM_TYPE,
-                    LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH);
-            values.put(LauncherSettings.Favorites.SCREEN, 2);
-            values.put(LauncherSettings.Favorites.CELLX, 0);
-            values.put(LauncherSettings.Favorites.CELLY, 0);
-            values.put(LauncherSettings.Favorites.SPANX, 4);
-            values.put(LauncherSettings.Favorites.SPANY, 1);
+            values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_WIDGET_SEARCH);
+            values.put(Favorites.SPANX, 4);
+            values.put(Favorites.SPANY, 1);
             db.insert(TABLE_FAVORITES, null, values);
-            
+
+            return true;
+        }
+
+        private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
             final int[] bindSources = new int[] {
                     Favorites.ITEM_TYPE_WIDGET_CLOCK,
             };
-            
+
             final ArrayList<ComponentName> bindTargets = new ArrayList<ComponentName>();
             bindTargets.add(new ComponentName("com.android.alarmclock",
                     "com.android.alarmclock.AnalogAppWidgetProvider"));
-            
+
             boolean allocatedAppWidgets = false;
-            
+
             // Try binding to an analog clock widget
             try {
                 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
-                
-                values.clear();
-                values.put(LauncherSettings.Favorites.CONTAINER,
-                        LauncherSettings.Favorites.CONTAINER_DESKTOP);
-                values.put(LauncherSettings.Favorites.ITEM_TYPE,
-                        LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK);
-                values.put(LauncherSettings.Favorites.SCREEN, 1);
-                values.put(LauncherSettings.Favorites.CELLX, 1);
-                values.put(LauncherSettings.Favorites.CELLY, 0);
-                values.put(LauncherSettings.Favorites.SPANX, 2);
-                values.put(LauncherSettings.Favorites.SPANY, 2);
-                values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
+
+                values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_WIDGET_CLOCK);
+                values.put(Favorites.SPANX, 2);
+                values.put(Favorites.SPANY, 2);
+                values.put(Favorites.APPWIDGET_ID, appWidgetId);
                 db.insert(TABLE_FAVORITES, null, values);
-                
+
                 allocatedAppWidgets = true;
             } catch (RuntimeException ex) {
                 Log.e(LOG_TAG, "Problem allocating appWidgetId", ex);
@@ -563,8 +560,8 @@
             if (allocatedAppWidgets) {
                 launchAppWidgetBinder(bindSources, bindTargets);
             }
-            
-            return i;
+
+            return allocatedAppWidgets;
         }
     }
 
diff --git a/src/com/android/launcher/Workspace.java b/src/com/android/launcher/Workspace.java
index bc5347e..359767a 100644
--- a/src/com/android/launcher/Workspace.java
+++ b/src/com/android/launcher/Workspace.java
@@ -26,6 +26,7 @@
 import android.graphics.RectF;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
@@ -34,6 +35,7 @@
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.widget.Scroller;
+import android.widget.TextView;
 import android.os.Parcelable;
 import android.os.Parcel;
 
@@ -1240,7 +1242,41 @@
             }
         }
     }
-    
+
+    void updateShortcutsForPackage(String packageName) {
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            final CellLayout layout = (CellLayout) getChildAt(i);
+            int childCount = layout.getChildCount();
+            for (int j = 0; j < childCount; j++) {
+                final View view = layout.getChildAt(j);
+                Object tag = view.getTag();
+                if (tag instanceof ApplicationInfo) {
+                    ApplicationInfo info = (ApplicationInfo) tag;
+                    // We need to check for ACTION_MAIN otherwise getComponent() might
+                    // return null for some shortcuts (for instance, for shortcuts to
+                    // web pages.)
+                    final Intent intent = info.intent;
+                    final ComponentName name = intent.getComponent();
+                    if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
+                            Intent.ACTION_MAIN.equals(intent.getAction()) && name != null &&
+                            packageName.equals(name.getPackageName())) {
+
+                        final Drawable icon = Launcher.getModel().getApplicationInfoIcon(
+                                mLauncher.getPackageManager(), info);
+                        if (icon != null && icon != info.icon) {
+                            info.icon.setCallback(null);
+                            info.icon = Utilities.createIconThumbnail(icon, mContext);
+                            info.filtered = true;
+                            ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(null,
+                                    info.icon, null, null);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     // TODO: remove widgets when appwidgetmanager tells us they're gone
 //    void removeAppWidgetsForProvider() {
 //    }