auto import from //depot/cupcake/@135843
diff --git a/src/com/android/launcher/LauncherModel.java b/src/com/android/launcher/LauncherModel.java
new file mode 100644
index 0000000..40b5402
--- /dev/null
+++ b/src/com/android/launcher/LauncherModel.java
@@ -0,0 +1,995 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher;
+
+import android.content.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.net.Uri;
+import android.util.Log;
+import android.os.Process;
+
+import java.util.ArrayList;
+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
+ * LauncherModel object held in a static. Also provide APIs for updating the database state
+ * for the Launcher
+ */
+public class LauncherModel {
+    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 final Collator sCollator = Collator.getInstance();    
+
+    private boolean mApplicationsLoaded;
+    private boolean mDesktopItemsLoaded;
+
+    private ArrayList<ItemInfo> mDesktopItems;
+    private ArrayList<LauncherGadgetInfo> mDesktopGadgets;
+    private HashMap<Long, FolderInfo> mFolders;
+
+    private ArrayList<ApplicationInfo> mApplications;
+    private ApplicationsAdapter mApplicationsAdapter;
+    private ApplicationsLoader mApplicationsLoader;
+    private DesktopItemsLoader mDesktopItemsLoader;
+    private Thread mLoader;
+    private Thread mDesktopLoader;
+
+    void abortLoaders() {
+        if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
+            mApplicationsLoader.stop();
+            mApplicationsLoaded = false;
+        }
+        if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
+            mDesktopItemsLoader.stop();
+            mDesktopItemsLoaded = false;
+        }
+    }
+
+    /**
+     * Loads the list of installed applications in mApplications.
+     */
+    void loadApplications(boolean isLaunching, Launcher launcher, boolean localeChanged) {
+        if (isLaunching && mApplicationsLoaded && !localeChanged) {
+            mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
+            return;
+        }
+
+        if (mApplicationsAdapter == null || isLaunching || localeChanged) {
+            mApplicationsAdapter = new ApplicationsAdapter(launcher,
+                    mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER));
+        }
+
+        if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
+            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 {
+                mLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
+            } catch (InterruptedException e) {
+                // Empty
+            }
+        }
+
+        mApplicationsLoaded = false;
+
+        if (!isLaunching) {
+            startApplicationsLoader(launcher);
+        }
+    }
+
+    private void startApplicationsLoader(Launcher launcher) {
+        mApplicationsLoader = new ApplicationsLoader(launcher);
+        mLoader = new Thread(mApplicationsLoader, "Applications Loader");
+        mLoader.start();
+    }
+
+    private class ApplicationsLoader implements Runnable {
+        private final WeakReference<Launcher> mLauncher;
+
+        private volatile boolean mStopped;
+        private volatile boolean mRunning;
+
+        ApplicationsLoader(Launcher launcher) {
+            mLauncher = new WeakReference<Launcher>(launcher);
+        }
+
+        void stop() {
+            mStopped = true;
+        }
+
+        boolean isRunning() {
+            return mRunning;
+        }
+
+        public void run() {
+            mRunning = true;
+
+            android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
+            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();
+                final ApplicationsAdapter applicationList = mApplicationsAdapter;
+
+                ChangeNotifier action = new ChangeNotifier(applicationList);
+
+                for (int i = 0; i < count && !mStopped; i++) {
+                    ApplicationInfo application = new ApplicationInfo();
+                    ResolveInfo info = apps.get(i);
+
+                    application.title = info.loadLabel(manager);
+                    if (application.title == null) {
+                        application.title = info.activityInfo.name;
+                    }
+                    application.setActivity(new ComponentName(
+                            info.activityInfo.applicationInfo.packageName,
+                            info.activityInfo.name),
+                            Intent.FLAG_ACTIVITY_NEW_TASK |
+                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                    application.icon = info.activityInfo.loadIcon(manager);
+                    application.container = ItemInfo.NO_ID;
+
+                    action.add(application);
+                }
+
+                action.sort(new Comparator<ApplicationInfo>() {
+                    public final int compare(ApplicationInfo a, ApplicationInfo b) {
+                        return sCollator.compare(a.title.toString(), b.title.toString());
+                    }
+                });
+
+                if (!mStopped) {
+                    launcher.runOnUiThread(action);
+                }
+            }
+
+            if (!mStopped) {
+                mApplicationsLoaded = true;
+            }
+            mRunning = false;
+        }
+    }
+
+    private static class ChangeNotifier implements Runnable {
+        private final ApplicationsAdapter mApplicationList;
+        private ArrayList<ApplicationInfo> mBuffer;
+
+        ChangeNotifier(ApplicationsAdapter applicationList) {
+            mApplicationList = applicationList;
+            mBuffer = new ArrayList<ApplicationInfo>(UI_NOTIFICATION_RATE);
+        }
+
+        public void run() {
+            final ArrayList<ApplicationInfo> buffer = mBuffer;
+            final ApplicationsAdapter applicationList = mApplicationList;
+            final int count = buffer.size();
+
+            applicationList.setNotifyOnChange(false);
+            applicationList.clear();
+            for (int i = 0; i < count; i++) {
+                applicationList.setNotifyOnChange(false);
+                applicationList.add(buffer.get(i));
+            }
+
+            applicationList.notifyDataSetChanged();
+            buffer.clear();
+        }
+
+        void add(ApplicationInfo application) {
+            mBuffer.add(application);
+        }
+
+        void sort(Comparator<ApplicationInfo> comparator) {
+            Collections.sort(mBuffer, comparator);
+        }
+    }
+
+    boolean isDesktopLoaded() {
+        return mDesktopItems != null && mDesktopGadgets != null && mDesktopItemsLoaded;
+    }
+    
+    /**
+     * Loads all of the items on the desktop, in folders, or in the dock.
+     * These can be apps, shortcuts or widgets
+     */
+    void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged,
+            boolean loadApplications) {
+
+        if (isLaunching && isDesktopLoaded()) {
+            if (loadApplications) startApplicationsLoader(launcher);
+            // We have already loaded our data from the DB
+            launcher.onDesktopItemsLoaded();
+            return;
+        }
+
+        if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
+            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
+            try {
+                mDesktopLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
+            } catch (InterruptedException e) {
+                // Empty
+            }
+        }
+
+        mDesktopItemsLoaded = false;
+        mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications);
+        mDesktopLoader = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
+        mDesktopLoader.start();
+    }
+
+    private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
+        final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[] { LauncherSettings.Favorites.ID, LauncherSettings.Favorites.TITLE,
+                        LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE },
+                null, null, null);
+
+        final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
+        final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
+        final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
+        final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
+
+        // boolean changed = false;
+
+        try {
+            while (c.moveToNext()) {
+                try {
+                    if (c.getInt(itemTypeIndex) !=
+                            LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                        continue;
+                    }
+
+                    final String intentUri = c.getString(intentIndex);
+                    if (intentUri != null) {
+                        final Intent shortcut = Intent.getIntent(intentUri);
+                        if (Intent.ACTION_MAIN.equals(shortcut.getAction())) {
+                            final ComponentName name = shortcut.getComponent();
+                            if (name != null) {
+                                final ActivityInfo activityInfo = manager.getActivityInfo(name, 0);
+                                final String title = c.getString(titleIndex);
+                                String label = getLabel(manager, activityInfo);
+
+                                if (title == null || !title.equals(label)) {
+                                    final ContentValues values = new ContentValues();
+                                    values.put(LauncherSettings.Favorites.TITLE, label);
+
+                                    resolver.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
+                                            values, "_id=?",
+                                            new String[] { String.valueOf(c.getLong(idIndex)) });
+
+                                    // changed = true;
+                                }
+                            }
+                        }
+                    }
+                } catch (URISyntaxException e) {
+                    // Ignore
+                } catch (PackageManager.NameNotFoundException e) {
+                    // Ignore
+                }
+            }
+        } finally {
+            c.close();
+        }
+
+        // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null);
+    }
+
+    private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
+        String label = activityInfo.loadLabel(manager).toString();
+        if (label == null) {
+            label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
+            if (label == null) {
+                label = activityInfo.name;
+            }
+        }
+        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;
+
+        DesktopItemsLoader(Launcher launcher, boolean localeChanged, boolean loadApplications) {
+            mLoadApplications = loadApplications;
+            mLauncher = new WeakReference<Launcher>(launcher);
+            mLocaleChanged = localeChanged;
+        }
+
+        void stop() {
+            mStopped = true;
+        }
+
+        boolean isRunning() {
+            return mRunning;
+        }
+
+        public void run() {
+            mRunning = true;
+
+            final Launcher launcher = mLauncher.get();
+            final ContentResolver contentResolver = launcher.getContentResolver();
+            final PackageManager manager = launcher.getPackageManager();
+
+            if (mLocaleChanged) {
+                updateShortcutLabels(contentResolver, manager);
+            }
+
+            mDesktopItems = new ArrayList<ItemInfo>();
+            mDesktopGadgets = new ArrayList<LauncherGadgetInfo>();
+            mFolders = new HashMap<Long, FolderInfo>();
+
+            final ArrayList<ItemInfo> desktopItems = mDesktopItems;
+            final ArrayList<LauncherGadgetInfo> desktopGadgets = mDesktopGadgets;
+
+            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 gadgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.GADGET_ID);
+                final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
+                final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
+                final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
+                final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
+                final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
+                final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
+                final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
+
+                ApplicationInfo info;
+                String intentDescription;
+                Widget widgetInfo;
+                LauncherGadgetInfo gadgetInfo;
+                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.getIntent(intentDescription);
+                            } catch (java.net.URISyntaxException e) {
+                                continue;
+                            }
+
+                            if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                                info = getApplicationInfo(manager, intent);
+                            } 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.getIntent(intentDescription);
+                                } 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) {
+                                Log.e(Launcher.LOG_TAG, "Widget found where container "
+                                        + "!= CONTAINER_DESKTOP  ignoring!");
+                                continue;
+                            }
+                            
+                            widgetInfo.id = c.getLong(idIndex);
+                            widgetInfo.screen = c.getInt(screenIndex);
+                            widgetInfo.container = container;
+                            widgetInfo.cellX = c.getInt(cellXIndex);
+                            widgetInfo.cellY = c.getInt(cellYIndex);
+
+                            desktopItems.add(widgetInfo);
+                            break;
+                        case LauncherSettings.Favorites.ITEM_TYPE_GADGET:
+                            // Read all Launcher-specific gadget details
+                            int gadgetId = c.getInt(gadgetIdIndex);
+                            gadgetInfo = new LauncherGadgetInfo(gadgetId);
+                            gadgetInfo.id = c.getLong(idIndex);
+                            gadgetInfo.screen = c.getInt(screenIndex);
+                            gadgetInfo.cellX = c.getInt(cellXIndex);
+                            gadgetInfo.cellY = c.getInt(cellYIndex);
+                            gadgetInfo.spanX = c.getInt(spanXIndex);
+                            gadgetInfo.spanY = c.getInt(spanYIndex);
+
+                            container = c.getInt(containerIndex);
+                            if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                                Log.e(Launcher.LOG_TAG, "Gadget found where container "
+                                        + "!= CONTAINER_DESKTOP -- ignoring!");
+                                continue;
+                            }
+                            gadgetInfo.container = c.getInt(containerIndex);
+                            
+                            desktopGadgets.add(gadgetInfo);
+                            break;
+                        }
+                    } catch (Exception e) {
+                        Log.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);
+            }
+
+            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() {
+        mApplicationsAdapter = null;
+        unbindAppDrawables(mApplications);
+        unbindDrawables(mDesktopItems);
+        unbindGadgetHostViews(mDesktopGadgets);
+    }
+    
+    /**
+     * 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 LauncherGadgetHostView} references in our gadgets.
+     */
+    private void unbindGadgetHostViews(ArrayList<LauncherGadgetInfo> gadgets) {
+        if (gadgets != null) {
+            final int count = gadgets.size();
+            for (int i = 0; i < count; i++) {
+                LauncherGadgetInfo launcherInfo = gadgets.get(i);
+                launcherInfo.hostView = null;
+            }
+        }
+    }
+
+    /**
+     * @return The current list of applications
+     */
+    public ArrayList<ApplicationInfo> getApplications() {
+        return mApplications;
+    }
+
+    /**
+     * @return The current list of applications
+     */
+    public ApplicationsAdapter getApplicationsAdapter() {
+        return mApplicationsAdapter;
+    }
+
+    /**
+     * @return The current list of desktop items
+     */
+    public ArrayList<ItemInfo> getDesktopItems() {
+        return mDesktopItems;
+    }
+    
+    /**
+     * @return The current list of desktop items
+     */
+    public ArrayList<LauncherGadgetInfo> getDesktopGadgets() {
+        return mDesktopGadgets;
+    }
+
+    /**
+     * Add an item to the desktop
+     * @param info
+     */
+    public 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
+     */
+    public void removeDesktopItem(ItemInfo info) {
+        // TODO: write to DB; figure out if we should remove folder from folders list
+        mDesktopItems.remove(info);
+    }
+
+    /**
+     * Add a gadget to the desktop
+     */
+    public void addDesktopGadget(LauncherGadgetInfo info) {
+        mDesktopGadgets.add(info);
+    }
+    
+    /**
+     * Remove a gadget from the desktop
+     */
+    public void removeDesktopGadget(LauncherGadgetInfo info) {
+        mDesktopGadgets.remove(info);
+    }
+
+    /**
+     * Make an ApplicationInfo object for an application
+     */
+    private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent) {
+        final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
+
+        if (resolveInfo == null) {
+            return null;
+        }
+        
+        final ApplicationInfo info = new ApplicationInfo();
+        final ActivityInfo activityInfo = resolveInfo.activityInfo;
+        info.icon = activityInfo.loadIcon(manager);
+        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, Launcher launcher,
+            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 = launcher.getPackageManager();
+                try {
+                    Resources resources = packageManager.getResourcesForApplication(packageName);
+                    final int id = resources.getIdentifier(resourceName, null, null);
+                    info.icon = resources.getDrawable(id);
+                } 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);
+                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
+                info.icon = new FastBitmapDrawable(
+                        Utilities.createBitmapThumbnail(bitmap, launcher));
+                info.filtered = true;
+                info.customIcon = true;
+                break;
+            default:
+                info.icon = launcher.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() }, 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));
+        }
+    }
+
+    /**
+     * 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);
+    }
+}