From 3611578579b4bfb25616085dafdb1a45207394f9 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Thu, 17 Jun 2010 13:28:48 -0400 Subject: [PATCH] Make the loader thread a Looper and move the package manager updates into that thread as well. This gets rid of the ANRs that happened because both threads were holding mAllAppsListLock. Now mAllAppsList should only be touched from within the worker thread. Change-Id: Ifc4ac27e0c0a927ac66c456d097fd3d4ef6c5e1e --- src/com/android/launcher2/Launcher.java | 6 +- src/com/android/launcher2/LauncherModel.java | 1671 +++++++++--------- 2 files changed, 832 insertions(+), 845 deletions(-) diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java index 6bd915aa0d..a5988bf6c0 100644 --- a/src/com/android/launcher2/Launcher.java +++ b/src/com/android/launcher2/Launcher.java @@ -2281,9 +2281,11 @@ public final class Launcher extends Activity * * Implementation of the method from LauncherModel.Callbacks. */ - public void bindAppsRemoved(ArrayList apps) { + public void bindAppsRemoved(ArrayList apps, boolean permanent) { removeDialog(DIALOG_CREATE_SHORTCUT); - mWorkspace.removeItems(apps); + if (permanent) { + mWorkspace.removeItems(apps); + } mAllAppsGrid.removeApps(apps); } diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java index 17f75732aa..eb341f68a1 100644 --- a/src/com/android/launcher2/LauncherModel.java +++ b/src/com/android/launcher2/LauncherModel.java @@ -35,6 +35,8 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; +import android.os.Handler; +import android.os.HandlerThread; import android.os.Parcelable; import android.os.RemoteException; import android.util.Log; @@ -60,16 +62,18 @@ import com.android.launcher.R; */ public class LauncherModel extends BroadcastReceiver { static final boolean DEBUG_LOADERS = false; - static final boolean PROFILE_LOADERS = false; static final String TAG = "Launcher.Model"; + private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons private int mBatchSize; // 0 is all apps at once private int mAllAppsLoadDelay; // milliseconds between batches private final LauncherApplication mApp; private final Object mLock = new Object(); private DeferredHandler mHandler = new DeferredHandler(); - private Loader mLoader = new Loader(); + private HandlerThread mWorkerThread; + private Handler mWorker; + private LoaderTask mLoaderTask; // We start off with everything not loaded. After that, we assume that // our monitoring of the package manager provides all updates and we never @@ -77,12 +81,13 @@ public class LauncherModel extends BroadcastReceiver { private boolean mWorkspaceLoaded; private boolean mAllAppsLoaded; - private boolean mBeforeFirstLoad = true; // only access this from main thread private WeakReference mCallbacks; - private final Object mAllAppsListLock = new Object(); - private AllAppsList mAllAppsList; + private AllAppsList mAllAppsList; // only access in worker thread private IconCache mIconCache; + final ArrayList mItems = new ArrayList(); + final ArrayList mAppWidgets = new ArrayList(); + final HashMap mFolders = new HashMap(); private Bitmap mDefaultIcon; @@ -96,7 +101,7 @@ public class LauncherModel extends BroadcastReceiver { public void bindAllApplications(ArrayList apps); public void bindAppsAdded(ArrayList apps); public void bindAppsUpdated(ArrayList apps); - public void bindAppsRemoved(ArrayList apps); + public void bindAppsRemoved(ArrayList apps, boolean permanent); public boolean isAllAppsVisible(); } @@ -111,6 +116,10 @@ public class LauncherModel extends BroadcastReceiver { mAllAppsLoadDelay = app.getResources().getInteger(R.integer.config_allAppsBatchLoadDelay); mBatchSize = app.getResources().getInteger(R.integer.config_allAppsBatchSize); + + mWorkerThread = new HandlerThread("launcher-loader"); + mWorkerThread.start(); + mWorker = new Handler(mWorkerThread.getLooper()); } public Bitmap getFallbackIcon() { @@ -284,295 +293,176 @@ public class LauncherModel extends BroadcastReceiver { } } - public void startLoader(Context context, boolean isLaunching) { - mLoader.startLoader(context, isLaunching); - } - - public void stopLoader() { - mLoader.stopLoader(); - } - /** * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and * ACTION_PACKAGE_CHANGED. */ public void onReceive(Context context, Intent intent) { - // Use the app as the context. - context = mApp; + if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent); + + final String action = intent.getAction(); - ArrayList added = null; - ArrayList removed = null; - ArrayList modified = null; + if (Intent.ACTION_PACKAGE_CHANGED.equals(action) + || Intent.ACTION_PACKAGE_REMOVED.equals(action) + || Intent.ACTION_PACKAGE_ADDED.equals(action)) { + final String packageName = intent.getData().getSchemeSpecificPart(); + final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + + int op = PackageUpdatedTask.OP_NONE; + + if (packageName == null || packageName.length() == 0) { + // they sent us a bad intent + return; + } + + if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { + op = PackageUpdatedTask.OP_UPDATE; + } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + if (!replacing) { + op = PackageUpdatedTask.OP_REMOVE; + } + // else, we are replacing the package, so a PACKAGE_ADDED will be sent + // later, we will update the package at this time + } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + if (!replacing) { + op = PackageUpdatedTask.OP_ADD; + } else { + op = PackageUpdatedTask.OP_UPDATE; + } + } + + if (op != PackageUpdatedTask.OP_NONE) { + enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName })); + } + + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { + String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages)); + + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { + String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + enqueuePackageUpdated(new PackageUpdatedTask( + PackageUpdatedTask.OP_UNAVAILABLE, packages)); - if (mBeforeFirstLoad) { - // If we haven't even loaded yet, don't bother, since we'll just pick - // up the changes. - return; } + } - synchronized (mAllAppsListLock) { - final String action = intent.getAction(); + public void startLoader(Context context, boolean isLaunching) { + synchronized (mLock) { + if (DEBUG_LOADERS) { + Log.d(TAG, "startLoader isLaunching=" + isLaunching); + } - if (Intent.ACTION_PACKAGE_CHANGED.equals(action) - || Intent.ACTION_PACKAGE_REMOVED.equals(action) - || Intent.ACTION_PACKAGE_ADDED.equals(action)) { - final String packageName = intent.getData().getSchemeSpecificPart(); - final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); - - if (packageName == null || packageName.length() == 0) { - // they sent us a bad intent - return; - } - - if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { - mAllAppsList.updatePackage(context, packageName); - } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { - if (!replacing) { - mAllAppsList.removePackage(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 (Intent.ACTION_PACKAGE_ADDED.equals(action)) { - if (!replacing) { - mAllAppsList.addPackage(context, packageName); - } else { - mAllAppsList.updatePackage(context, packageName); + // Don't bother to start the thread if we know it's not going to do anything + if (mCallbacks != null && mCallbacks.get() != null) { + // If there is already one running, tell it to stop. + LoaderTask oldTask = mLoaderTask; + if (oldTask != null) { + if (oldTask.isLaunching()) { + // don't downgrade isLaunching if we're already running + isLaunching = true; } + oldTask.stopLocked(); } - - if (mAllAppsList.added.size() > 0) { - added = mAllAppsList.added; - mAllAppsList.added = new ArrayList(); - } - if (mAllAppsList.removed.size() > 0) { - removed = mAllAppsList.removed; - mAllAppsList.removed = new ArrayList(); - for (ApplicationInfo info: removed) { - mIconCache.remove(info.intent.getComponent()); - } - } - if (mAllAppsList.modified.size() > 0) { - modified = mAllAppsList.modified; - mAllAppsList.modified = new ArrayList(); - } - - final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null; - if (callbacks == null) { - Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading."); - return; - } - - if (added != null) { - final ArrayList addedFinal = added; - mHandler.post(new Runnable() { - public void run() { - callbacks.bindAppsAdded(addedFinal); - } - }); - } - if (modified != null) { - final ArrayList modifiedFinal = modified; - mHandler.post(new Runnable() { - public void run() { - callbacks.bindAppsUpdated(modifiedFinal); - } - }); - } - if (removed != null) { - final ArrayList removedFinal = removed; - mHandler.post(new Runnable() { - public void run() { - callbacks.bindAppsRemoved(removedFinal); - } - }); - } - } else { - if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { - String packages[] = intent.getStringArrayExtra( - Intent.EXTRA_CHANGED_PACKAGE_LIST); - if (packages == null || packages.length == 0) { - return; - } - synchronized (this) { - mAllAppsLoaded = mWorkspaceLoaded = false; - } - startLoader(context, false); - } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { - String packages[] = intent.getStringArrayExtra( - Intent.EXTRA_CHANGED_PACKAGE_LIST); - if (packages == null || packages.length == 0) { - return; - } - synchronized (this) { - mAllAppsLoaded = mWorkspaceLoaded = false; - } - startLoader(context, false); - } + mLoaderTask = new LoaderTask(context, isLaunching); + mWorker.post(mLoaderTask); } } } - public class Loader { - private static final int ITEMS_CHUNK = 6; - - private LoaderThread mLoaderThread; - - final ArrayList mItems = new ArrayList(); - final ArrayList mAppWidgets = new ArrayList(); - final HashMap mFolders = new HashMap(); - - /** - * Call this from the ui thread so the handler is initialized on the correct thread. - */ - public Loader() { - } - - public void startLoader(Context context, boolean isLaunching) { - synchronized (mLock) { - if (DEBUG_LOADERS) { - Log.d(TAG, "startLoader isLaunching=" + isLaunching); - } - - // Don't bother to start the thread if we know it's not going to do anything - if (mCallbacks != null && mCallbacks.get() != null) { - LoaderThread oldThread = mLoaderThread; - if (oldThread != null) { - if (oldThread.isLaunching()) { - // don't downgrade isLaunching if we're already running - isLaunching = true; - } - oldThread.stopLocked(); - } - mLoaderThread = new LoaderThread(context, oldThread, isLaunching); - mLoaderThread.start(); - } + public void stopLoader() { + synchronized (mLock) { + if (mLoaderTask != null) { + mLoaderTask.stopLocked(); } } + } - public void stopLoader() { - synchronized (mLock) { - if (mLoaderThread != null) { - mLoaderThread.stopLocked(); - } - } + /** + * Runnable for the thread that loads the contents of the launcher: + * - workspace icons + * - widgets + * - all apps icons + */ + private class LoaderTask implements Runnable { + private Context mContext; + private Thread mWaitThread; + private boolean mIsLaunching; + private boolean mStopped; + private boolean mLoadAndBindStepFinished; + + LoaderTask(Context context, boolean isLaunching) { + mContext = context; + mIsLaunching = isLaunching; } - /** - * Runnable for the thread that loads the contents of the launcher: - * - workspace icons - * - widgets - * - all apps icons - */ - private class LoaderThread extends Thread { - private Context mContext; - private Thread mWaitThread; - private boolean mIsLaunching; - private boolean mStopped; - private boolean mLoadAndBindStepFinished; + boolean isLaunching() { + return mIsLaunching; + } - LoaderThread(Context context, Thread waitThread, boolean isLaunching) { - mContext = context; - mWaitThread = waitThread; - mIsLaunching = isLaunching; + private void loadAndBindWorkspace() { + // Load the workspace + + // For now, just always reload the workspace. It's ~100 ms vs. the + // binding which takes many hundreds of ms. + // We can reconsider. + if (DEBUG_LOADERS) { + Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded); } - - boolean isLaunching() { - return mIsLaunching; - } - - /** - * If another LoaderThread was supplied, we need to wait for that to finish before - * we start our processing. This keeps the ordering of the setting and clearing - * of the dirty flags correct by making sure we don't start processing stuff until - * they've had a chance to re-set them. We do this waiting the worker thread, not - * the ui thread to avoid ANRs. - */ - private void waitForOtherThread() { - if (mWaitThread != null) { - boolean done = false; - while (!done) { - try { - mWaitThread.join(); - done = true; - } catch (InterruptedException ex) { - // Ignore - } - } - mWaitThread = null; + if (true || !mWorkspaceLoaded) { + loadWorkspace(); + if (mStopped) { + return; } + mWorkspaceLoaded = true; } - private void loadAndBindWorkspace() { - // Load the workspace + // Bind the workspace + bindWorkspace(); + } - // Other other threads can unset mWorkspaceLoaded, so atomically set it, - // and then if they unset it, or we unset it because of mStopped, it will - // be unset. - boolean loaded; - synchronized (this) { - loaded = mWorkspaceLoaded; - mWorkspaceLoaded = true; - } + private void waitForIdle() { + // Wait until the either we're stopped or the other threads are done. + // This way we don't start loading all apps until the workspace has settled + // down. + synchronized (LoaderTask.this) { + final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - // For now, just always reload the workspace. It's ~100 ms vs. the - // binding which takes many hundreds of ms. - // We can reconsider. - if (DEBUG_LOADERS) Log.d(TAG, "loadAndBindWorkspace loaded=" + loaded); - if (true || !loaded) { - loadWorkspace(); - if (mStopped) { - mWorkspaceLoaded = false; - return; - } - } - - // Bind the workspace - bindWorkspace(); - } - - private void waitForIdle() { - // Wait until the either we're stopped or the other threads are done. - // This way we don't start loading all apps until the workspace has settled - // down. - synchronized (LoaderThread.this) { - final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - - mHandler.postIdle(new Runnable() { - public void run() { - synchronized (LoaderThread.this) { - mLoadAndBindStepFinished = true; - if (DEBUG_LOADERS) { - Log.d(TAG, "done with previous binding step"); - } - LoaderThread.this.notify(); + mHandler.postIdle(new Runnable() { + public void run() { + synchronized (LoaderTask.this) { + mLoadAndBindStepFinished = true; + if (DEBUG_LOADERS) { + Log.d(TAG, "done with previous binding step"); } + LoaderTask.this.notify(); } - }); - - while (!mStopped && !mLoadAndBindStepFinished) { - try { - this.wait(); - } catch (InterruptedException ex) { - // Ignore } - } - if (DEBUG_LOADERS) { - Log.d(TAG, "waited " - + (SystemClock.uptimeMillis()-workspaceWaitTime) - + "ms for previous step to finish binding"); + }); + + while (!mStopped && !mLoadAndBindStepFinished) { + try { + this.wait(); + } catch (InterruptedException ex) { + // Ignore } } + if (DEBUG_LOADERS) { + Log.d(TAG, "waited " + + (SystemClock.uptimeMillis()-workspaceWaitTime) + + "ms for previous step to finish binding"); + } } + } - public void run() { - waitForOtherThread(); - - // Optimize for end-user experience: if the Launcher is up and // running with the - // All Apps interface in the foreground, load All Apps first. Otherwise, load the - // workspace first (default). - final Callbacks cbk = mCallbacks.get(); - final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true; + public void run() { + // Optimize for end-user experience: if the Launcher is up and // running with the + // All Apps interface in the foreground, load All Apps first. Otherwise, load the + // workspace first (default). + final Callbacks cbk = mCallbacks.get(); + final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true; + keep_running: { // Elevate priority when Home launches for the first time to avoid // starving at boot time. Staring at a blank home is not cool. synchronized (mLock) { @@ -580,10 +470,6 @@ public class LauncherModel extends BroadcastReceiver { ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); } - if (PROFILE_LOADERS) { - android.os.Debug.startMethodTracing("/sdcard/launcher-loaders"); - } - if (loadWorkspaceFirst) { if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace"); loadAndBindWorkspace(); @@ -592,12 +478,18 @@ public class LauncherModel extends BroadcastReceiver { loadAndBindAllApps(); } - // Whew! Hard work done. + if (mStopped) { + break keep_running; + } + + // Whew! Hard work done. Slow us down, and wait until the UI thread has + // settled down. synchronized (mLock) { if (mIsLaunching) { android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } } + waitForIdle(); // second step if (loadWorkspaceFirst) { @@ -607,671 +499,760 @@ public class LauncherModel extends BroadcastReceiver { if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace"); loadAndBindWorkspace(); } + } - // Clear out this reference, otherwise we end up holding it until all of the - // callback runnables are done. - mContext = null; + // Clear out this reference, otherwise we end up holding it until all of the + // callback runnables are done. + mContext = null; - synchronized (mLock) { - // Setting the reference is atomic, but we can't do it inside the other critical - // sections. - mLoaderThread = null; + synchronized (mLock) { + // If we are still the last one to be scheduled, remove ourselves. + if (mLoaderTask == this) { + mLoaderTask = null; } + } - if (PROFILE_LOADERS) { - android.os.Debug.stopMethodTracing(); - } - - // Trigger a gc to try to clean up after the stuff is done, since the - // renderscript allocations aren't charged to the java heap. + // Trigger a gc to try to clean up after the stuff is done, since the + // renderscript allocations aren't charged to the java heap. + if (mStopped) { mHandler.post(new Runnable() { public void run() { System.gc(); } }); - } - - public void stopLocked() { - synchronized (LoaderThread.this) { - mStopped = true; - this.notify(); - } - } - - /** - * Gets the callbacks object. If we've been stopped, or if the launcher object - * has somehow been garbage collected, return null instead. Pass in the Callbacks - * object that was around when the deferred message was scheduled, and if there's - * a new Callbacks object around then also return null. This will save us from - * calling onto it with data that will be ignored. - */ - Callbacks tryGetCallbacks(Callbacks oldCallbacks) { - synchronized (mLock) { - if (mStopped) { - return null; - } - - if (mCallbacks == null) { - return null; - } - - final Callbacks callbacks = mCallbacks.get(); - if (callbacks != oldCallbacks) { - return null; - } - if (callbacks == null) { - Log.w(TAG, "no mCallbacks"); - return null; - } - - return callbacks; - } - } - - // check & update map of what's occupied; used to discard overlapping/invalid items - private boolean checkItemPlacement(ItemInfo occupied[][][], ItemInfo item) { - if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { - return true; - } - - for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { - for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { - if (occupied[item.screen][x][y] != null) { - Log.e(TAG, "Error loading shortcut " + item - + " into cell (" + item.screen + ":" - + x + "," + y - + ") occupied by " - + occupied[item.screen][x][y]); - return false; + } else { + mHandler.postIdle(new Runnable() { + public void run() { + System.gc(); } - } + }); + } + } + + public void stopLocked() { + synchronized (LoaderTask.this) { + mStopped = true; + this.notify(); + } + } + + /** + * Gets the callbacks object. If we've been stopped, or if the launcher object + * has somehow been garbage collected, return null instead. Pass in the Callbacks + * object that was around when the deferred message was scheduled, and if there's + * a new Callbacks object around then also return null. This will save us from + * calling onto it with data that will be ignored. + */ + Callbacks tryGetCallbacks(Callbacks oldCallbacks) { + synchronized (mLock) { + if (mStopped) { + return null; } - for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { - for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { - occupied[item.screen][x][y] = item; - } + + if (mCallbacks == null) { + return null; } + + final Callbacks callbacks = mCallbacks.get(); + if (callbacks != oldCallbacks) { + return null; + } + if (callbacks == null) { + Log.w(TAG, "no mCallbacks"); + return null; + } + + return callbacks; + } + } + + // check & update map of what's occupied; used to discard overlapping/invalid items + private boolean checkItemPlacement(ItemInfo occupied[][][], ItemInfo item) { + if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { return true; } - private void loadWorkspace() { - final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { + for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { + if (occupied[item.screen][x][y] != null) { + Log.e(TAG, "Error loading shortcut " + item + + " into cell (" + item.screen + ":" + + x + "," + y + + ") occupied by " + + occupied[item.screen][x][y]); + return false; + } + } + } + for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { + for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { + occupied[item.screen][x][y] = item; + } + } + return true; + } - final Context context = mContext; - final ContentResolver contentResolver = context.getContentResolver(); - final PackageManager manager = context.getPackageManager(); - final AppWidgetManager widgets = AppWidgetManager.getInstance(context); - final boolean isSafeMode = manager.isSafeMode(); + private void loadWorkspace() { + final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - mItems.clear(); - mAppWidgets.clear(); - mFolders.clear(); + final Context context = mContext; + final ContentResolver contentResolver = context.getContentResolver(); + final PackageManager manager = context.getPackageManager(); + final AppWidgetManager widgets = AppWidgetManager.getInstance(context); + final boolean isSafeMode = manager.isSafeMode(); - final ArrayList itemsToRemove = new ArrayList(); + mItems.clear(); + mAppWidgets.clear(); + mFolders.clear(); - final Cursor c = contentResolver.query( - LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); + final ArrayList itemsToRemove = new ArrayList(); - final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y]; + final Cursor c = contentResolver.query( + LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); - try { - final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); - final int intentIndex = c.getColumnIndexOrThrow - (LauncherSettings.Favorites.INTENT); - final int titleIndex = c.getColumnIndexOrThrow - (LauncherSettings.Favorites.TITLE); - final int iconTypeIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.ICON_TYPE); - final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON); - final int iconPackageIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.ICON_PACKAGE); - final int iconResourceIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.ICON_RESOURCE); - final int containerIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.CONTAINER); - final int itemTypeIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.ITEM_TYPE); - final int appWidgetIdIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.APPWIDGET_ID); - final int screenIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.SCREEN); - final int cellXIndex = c.getColumnIndexOrThrow - (LauncherSettings.Favorites.CELLX); - final int cellYIndex = c.getColumnIndexOrThrow - (LauncherSettings.Favorites.CELLY); - final int spanXIndex = c.getColumnIndexOrThrow - (LauncherSettings.Favorites.SPANX); - final int spanYIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.SPANY); - final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); - final int displayModeIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.DISPLAY_MODE); + final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y]; - ShortcutInfo info; - String intentDescription; - LauncherAppWidgetInfo appWidgetInfo; - int container; - long id; - Intent intent; + try { + final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); + final int intentIndex = c.getColumnIndexOrThrow + (LauncherSettings.Favorites.INTENT); + final int titleIndex = c.getColumnIndexOrThrow + (LauncherSettings.Favorites.TITLE); + final int iconTypeIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.ICON_TYPE); + final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON); + final int iconPackageIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.ICON_PACKAGE); + final int iconResourceIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.ICON_RESOURCE); + final int containerIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.CONTAINER); + final int itemTypeIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.ITEM_TYPE); + final int appWidgetIdIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.APPWIDGET_ID); + final int screenIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.SCREEN); + final int cellXIndex = c.getColumnIndexOrThrow + (LauncherSettings.Favorites.CELLX); + final int cellYIndex = c.getColumnIndexOrThrow + (LauncherSettings.Favorites.CELLY); + final int spanXIndex = c.getColumnIndexOrThrow + (LauncherSettings.Favorites.SPANX); + final int spanYIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.SPANY); + final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); + final int displayModeIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.DISPLAY_MODE); - while (!mStopped && c.moveToNext()) { - try { - int itemType = c.getInt(itemTypeIndex); + ShortcutInfo info; + String intentDescription; + LauncherAppWidgetInfo appWidgetInfo; + int container; + long id; + Intent intent; - switch (itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: - intentDescription = c.getString(intentIndex); - try { - intent = Intent.parseUri(intentDescription, 0); - } catch (URISyntaxException e) { - continue; - } + while (!mStopped && c.moveToNext()) { + try { + int itemType = c.getInt(itemTypeIndex); - if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { - info = getShortcutInfo(manager, intent, context, c, iconIndex, - titleIndex); - } else { - info = getShortcutInfo(c, context, iconTypeIndex, - iconPackageIndex, iconResourceIndex, iconIndex, - titleIndex); - } + switch (itemType) { + case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: + case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: + intentDescription = c.getString(intentIndex); + try { + intent = Intent.parseUri(intentDescription, 0); + } catch (URISyntaxException e) { + continue; + } - if (info != null) { - updateSavedIcon(context, info, c, iconIndex); + if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + info = getShortcutInfo(manager, intent, context, c, iconIndex, + titleIndex); + } else { + info = getShortcutInfo(c, context, iconTypeIndex, + iconPackageIndex, iconResourceIndex, iconIndex, + 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); + if (info != null) { + updateSavedIcon(context, info, c, iconIndex); - // check & update map of what's occupied - if (!checkItemPlacement(occupied, info)) { - break; - } - - switch (container) { - case LauncherSettings.Favorites.CONTAINER_DESKTOP: - mItems.add(info); - break; - default: - // Item is in a user folder - UserFolderInfo folderInfo = - findOrMakeUserFolder(mFolders, container); - folderInfo.add(info); - break; - } - } else { - // Failed to load the shortcut, probably because the - // activity manager couldn't resolve it (maybe the app - // was uninstalled), or the db row was somehow screwed up. - // Delete it. - id = c.getLong(idIndex); - Log.e(TAG, "Error loading shortcut " + id + ", removing it"); - contentResolver.delete(LauncherSettings.Favorites.getContentUri( - id, false), null, null); - } - break; - - case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: - id = c.getLong(idIndex); - UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id); - - folderInfo.title = c.getString(titleIndex); - - folderInfo.id = id; + info.intent = intent; + info.id = c.getLong(idIndex); container = c.getInt(containerIndex); - folderInfo.container = container; - folderInfo.screen = c.getInt(screenIndex); - folderInfo.cellX = c.getInt(cellXIndex); - folderInfo.cellY = c.getInt(cellYIndex); + info.container = container; + info.screen = c.getInt(screenIndex); + info.cellX = c.getInt(cellXIndex); + info.cellY = c.getInt(cellYIndex); // check & update map of what's occupied - if (!checkItemPlacement(occupied, folderInfo)) { + if (!checkItemPlacement(occupied, info)) { break; } + switch (container) { + case LauncherSettings.Favorites.CONTAINER_DESKTOP: + mItems.add(info); + break; + default: + // Item is in a user folder + UserFolderInfo folderInfo = + findOrMakeUserFolder(mFolders, container); + folderInfo.add(info); + break; + } + } else { + // Failed to load the shortcut, probably because the + // activity manager couldn't resolve it (maybe the app + // was uninstalled), or the db row was somehow screwed up. + // Delete it. + id = c.getLong(idIndex); + Log.e(TAG, "Error loading shortcut " + id + ", removing it"); + contentResolver.delete(LauncherSettings.Favorites.getContentUri( + id, false), null, null); + } + break; + + case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: + id = c.getLong(idIndex); + UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, 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); + + // check & update map of what's occupied + if (!checkItemPlacement(occupied, folderInfo)) { + break; + } + + switch (container) { + case LauncherSettings.Favorites.CONTAINER_DESKTOP: + mItems.add(folderInfo); + break; + } + + mFolders.put(folderInfo.id, folderInfo); + break; + + case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: + id = c.getLong(idIndex); + Uri uri = Uri.parse(c.getString(uriIndex)); + + // Make sure the live folder exists + final ProviderInfo providerInfo = + context.getPackageManager().resolveContentProvider( + uri.getAuthority(), 0); + + if (providerInfo == null && !isSafeMode) { + itemsToRemove.add(id); + } else { + LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id); + + intentDescription = c.getString(intentIndex); + intent = null; + if (intentDescription != null) { + try { + intent = Intent.parseUri(intentDescription, 0); + } catch (URISyntaxException e) { + // Ignore, a live folder might not have a base intent + } + } + + liveFolderInfo.title = c.getString(titleIndex); + liveFolderInfo.id = id; + liveFolderInfo.uri = uri; + container = c.getInt(containerIndex); + liveFolderInfo.container = container; + liveFolderInfo.screen = c.getInt(screenIndex); + liveFolderInfo.cellX = c.getInt(cellXIndex); + liveFolderInfo.cellY = c.getInt(cellYIndex); + liveFolderInfo.baseIntent = intent; + liveFolderInfo.displayMode = c.getInt(displayModeIndex); + + // check & update map of what's occupied + if (!checkItemPlacement(occupied, liveFolderInfo)) { + break; + } + + loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex, + iconResourceIndex, liveFolderInfo); + switch (container) { case LauncherSettings.Favorites.CONTAINER_DESKTOP: - mItems.add(folderInfo); + mItems.add(liveFolderInfo); break; } - - mFolders.put(folderInfo.id, folderInfo); - break; - - case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: - id = c.getLong(idIndex); - Uri uri = Uri.parse(c.getString(uriIndex)); - - // Make sure the live folder exists - final ProviderInfo providerInfo = - context.getPackageManager().resolveContentProvider( - uri.getAuthority(), 0); - - if (providerInfo == null && !isSafeMode) { - itemsToRemove.add(id); - } else { - LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id); - - intentDescription = c.getString(intentIndex); - intent = null; - if (intentDescription != null) { - try { - intent = Intent.parseUri(intentDescription, 0); - } catch (URISyntaxException e) { - // Ignore, a live folder might not have a base intent - } - } - - liveFolderInfo.title = c.getString(titleIndex); - liveFolderInfo.id = id; - liveFolderInfo.uri = uri; - container = c.getInt(containerIndex); - liveFolderInfo.container = container; - liveFolderInfo.screen = c.getInt(screenIndex); - liveFolderInfo.cellX = c.getInt(cellXIndex); - liveFolderInfo.cellY = c.getInt(cellYIndex); - liveFolderInfo.baseIntent = intent; - liveFolderInfo.displayMode = c.getInt(displayModeIndex); - - // check & update map of what's occupied - if (!checkItemPlacement(occupied, liveFolderInfo)) { - break; - } - - loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex, - iconResourceIndex, liveFolderInfo); - - switch (container) { - case LauncherSettings.Favorites.CONTAINER_DESKTOP: - mItems.add(liveFolderInfo); - break; - } - mFolders.put(liveFolderInfo.id, liveFolderInfo); - } - break; - - case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: - // Read all Launcher-specific widget details - int appWidgetId = c.getInt(appWidgetIdIndex); - id = c.getLong(idIndex); - - final AppWidgetProviderInfo provider = - widgets.getAppWidgetInfo(appWidgetId); - - if (!isSafeMode && (provider == null || provider.provider == null || - provider.provider.getPackageName() == null)) { - Log.e(TAG, "Deleting widget that isn't installed anymore: id=" - + id + " appWidgetId=" + appWidgetId); - itemsToRemove.add(id); - } else { - appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId); - appWidgetInfo.id = id; - appWidgetInfo.screen = c.getInt(screenIndex); - appWidgetInfo.cellX = c.getInt(cellXIndex); - appWidgetInfo.cellY = c.getInt(cellYIndex); - appWidgetInfo.spanX = c.getInt(spanXIndex); - appWidgetInfo.spanY = c.getInt(spanYIndex); - - container = c.getInt(containerIndex); - if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { - Log.e(TAG, "Widget found where container " - + "!= CONTAINER_DESKTOP -- ignoring!"); - continue; - } - appWidgetInfo.container = c.getInt(containerIndex); - - // check & update map of what's occupied - if (!checkItemPlacement(occupied, appWidgetInfo)) { - break; - } - - mAppWidgets.add(appWidgetInfo); - } - break; + mFolders.put(liveFolderInfo.id, liveFolderInfo); } - } catch (Exception e) { - Log.w(TAG, "Desktop items loading interrupted:", e); - } - } - } finally { - c.close(); - } + break; - if (itemsToRemove.size() > 0) { - ContentProviderClient client = contentResolver.acquireContentProviderClient( - LauncherSettings.Favorites.CONTENT_URI); - // Remove dead items - for (long id : itemsToRemove) { - if (DEBUG_LOADERS) { - Log.d(TAG, "Removed id = " + id); - } - // Don't notify content observers - try { - client.delete(LauncherSettings.Favorites.getContentUri(id, false), - null, null); - } catch (RemoteException e) { - Log.w(TAG, "Could not remove id = " + id); + case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + // Read all Launcher-specific widget details + int appWidgetId = c.getInt(appWidgetIdIndex); + id = c.getLong(idIndex); + + final AppWidgetProviderInfo provider = + widgets.getAppWidgetInfo(appWidgetId); + + if (!isSafeMode && (provider == null || provider.provider == null || + provider.provider.getPackageName() == null)) { + Log.e(TAG, "Deleting widget that isn't installed anymore: id=" + + id + " appWidgetId=" + appWidgetId); + itemsToRemove.add(id); + } else { + appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId); + appWidgetInfo.id = id; + appWidgetInfo.screen = c.getInt(screenIndex); + appWidgetInfo.cellX = c.getInt(cellXIndex); + appWidgetInfo.cellY = c.getInt(cellYIndex); + appWidgetInfo.spanX = c.getInt(spanXIndex); + appWidgetInfo.spanY = c.getInt(spanYIndex); + + container = c.getInt(containerIndex); + if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { + Log.e(TAG, "Widget found where container " + + "!= CONTAINER_DESKTOP -- ignoring!"); + continue; + } + appWidgetInfo.container = c.getInt(containerIndex); + + // check & update map of what's occupied + if (!checkItemPlacement(occupied, appWidgetInfo)) { + break; + } + + mAppWidgets.add(appWidgetInfo); + } + break; } + } catch (Exception e) { + Log.w(TAG, "Desktop items loading interrupted:", e); } } + } finally { + c.close(); + } - if (DEBUG_LOADERS) { - Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms"); - Log.d(TAG, "workspace layout: "); - for (int y = 0; y < Launcher.NUMBER_CELLS_Y; y++) { - String line = ""; - for (int s = 0; s < Launcher.SCREEN_COUNT; s++) { - if (s > 0) { - line += " | "; - } - for (int x = 0; x < Launcher.NUMBER_CELLS_X; x++) { - line += ((occupied[s][x][y] != null) ? "#" : "."); - } - } - Log.d(TAG, "[ " + line + " ]"); + if (itemsToRemove.size() > 0) { + ContentProviderClient client = contentResolver.acquireContentProviderClient( + LauncherSettings.Favorites.CONTENT_URI); + // Remove dead items + for (long id : itemsToRemove) { + if (DEBUG_LOADERS) { + Log.d(TAG, "Removed id = " + id); + } + // Don't notify content observers + try { + client.delete(LauncherSettings.Favorites.getContentUri(id, false), + null, null); + } catch (RemoteException e) { + Log.w(TAG, "Could not remove id = " + id); } } } - /** - * Read everything out of our database. - */ - private void bindWorkspace() { - final long t = SystemClock.uptimeMillis(); - - // Don't use these two variables in any of the callback runnables. - // Otherwise we hold a reference to them. - final Callbacks oldCallbacks = mCallbacks.get(); - if (oldCallbacks == null) { - // This launcher has exited and nobody bothered to tell us. Just bail. - Log.w(TAG, "LoaderThread running with no launcher"); - return; + if (DEBUG_LOADERS) { + Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms"); + Log.d(TAG, "workspace layout: "); + for (int y = 0; y < Launcher.NUMBER_CELLS_Y; y++) { + String line = ""; + for (int s = 0; s < Launcher.SCREEN_COUNT; s++) { + if (s > 0) { + line += " | "; + } + for (int x = 0; x < Launcher.NUMBER_CELLS_X; x++) { + line += ((occupied[s][x][y] != null) ? "#" : "."); + } + } + Log.d(TAG, "[ " + line + " ]"); } + } + } - int N; - // Tell the workspace that we're about to start firing items at it + /** + * Read everything out of our database. + */ + private void bindWorkspace() { + final long t = SystemClock.uptimeMillis(); + + // Don't use these two variables in any of the callback runnables. + // Otherwise we hold a reference to them. + final Callbacks oldCallbacks = mCallbacks.get(); + if (oldCallbacks == null) { + // This launcher has exited and nobody bothered to tell us. Just bail. + Log.w(TAG, "LoaderTask running with no launcher"); + return; + } + + int N; + // Tell the workspace that we're about to start firing items at it + mHandler.post(new Runnable() { + public void run() { + Callbacks callbacks = tryGetCallbacks(oldCallbacks); + if (callbacks != null) { + callbacks.startBinding(); + } + } + }); + // Add the items to the workspace. + N = mItems.size(); + for (int i=0; i list + = (ArrayList)mAllAppsList.data.clone(); + mHandler.post(new Runnable() { + public void run() { + final long t = SystemClock.uptimeMillis(); + final Callbacks callbacks = tryGetCallbacks(oldCallbacks); + if (callbacks != null) { + callbacks.bindAllApplications(list); + } + if (DEBUG_LOADERS) { + Log.d(TAG, "bound all " + list.size() + " apps from cache in " + + (SystemClock.uptimeMillis()-t) + "ms"); + } + } + }); + + } + + private void loadAllAppsByBatch() { + final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + + // Don't use these two variables in any of the callback runnables. + // Otherwise we hold a reference to them. + final Callbacks oldCallbacks = mCallbacks.get(); + if (oldCallbacks == null) { + // This launcher has exited and nobody bothered to tell us. Just bail. + Log.w(TAG, "LoaderTask running with no launcher (loadAllAppsByBatch)"); + return; + } + + final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); + + final PackageManager packageManager = mContext.getPackageManager(); + List apps = null; + + int N = Integer.MAX_VALUE; + + int startIndex; + int i=0; + int batchSize = -1; + while (i < N && !mStopped) { + if (i == 0) { + mAllAppsList.clear(); + final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + apps = packageManager.queryIntentActivities(mainIntent, 0); + if (DEBUG_LOADERS) { + Log.d(TAG, "queryIntentActivities took " + + (SystemClock.uptimeMillis()-qiaTime) + "ms"); + } + if (apps == null) { + return; + } + N = apps.size(); + if (DEBUG_LOADERS) { + Log.d(TAG, "queryIntentActivities got " + N + " apps"); + } + if (N == 0) { + // There are no apps?!? + return; + } + if (mBatchSize == 0) { + batchSize = N; + } else { + batchSize = mBatchSize; + } + + final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + Collections.sort(apps, + new ResolveInfo.DisplayNameComparator(packageManager)); + if (DEBUG_LOADERS) { + Log.d(TAG, "sort took " + + (SystemClock.uptimeMillis()-sortTime) + "ms"); + } + } + + final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + + startIndex = i; + for (int j=0; i added = mAllAppsList.added; + mAllAppsList.added = new ArrayList(); - // shallow copy - final ArrayList list - = (ArrayList)mAllAppsList.data.clone(); mHandler.post(new Runnable() { public void run() { final long t = SystemClock.uptimeMillis(); - final Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { - callbacks.bindAllApplications(list); - } - if (DEBUG_LOADERS) { - Log.d(TAG, "bound all " + list.size() + " apps from cache in " - + (SystemClock.uptimeMillis()-t) + "ms"); + if (first) { + callbacks.bindAllApplications(added); + } else { + callbacks.bindAppsAdded(added); + } + if (DEBUG_LOADERS) { + Log.d(TAG, "bound " + added.size() + " apps in " + + (SystemClock.uptimeMillis() - t) + "ms"); + } + } else { + Log.i(TAG, "not binding apps: no Launcher activity"); } } }); - } - - private void loadAllAppsByBatch() { - final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - - // Don't use these two variables in any of the callback runnables. - // Otherwise we hold a reference to them. - final Callbacks oldCallbacks = mCallbacks.get(); - if (oldCallbacks == null) { - // This launcher has exited and nobody bothered to tell us. Just bail. - Log.w(TAG, "LoaderThread running with no launcher (loadAllAppsByBatch)"); - return; - } - - final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); - mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); - - final PackageManager packageManager = mContext.getPackageManager(); - List apps = null; - - int N = Integer.MAX_VALUE; - - int startIndex; - int i=0; - int batchSize = -1; - while (i < N && !mStopped) { - synchronized (mAllAppsListLock) { - if (i == 0) { - // This needs to happen inside the same lock block as when we - // prepare the first batch for bindAllApplications. Otherwise - // the package changed receiver can come in and double-add - // (or miss one?). - mAllAppsList.clear(); - final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - apps = packageManager.queryIntentActivities(mainIntent, 0); - if (DEBUG_LOADERS) { - Log.d(TAG, "queryIntentActivities took " - + (SystemClock.uptimeMillis()-qiaTime) + "ms"); - } - if (apps == null) { - return; - } - N = apps.size(); - if (DEBUG_LOADERS) { - Log.d(TAG, "queryIntentActivities got " + N + " apps"); - } - if (N == 0) { - // There are no apps?!? - return; - } - if (mBatchSize == 0) { - batchSize = N; - } else { - batchSize = mBatchSize; - } - - final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - Collections.sort(apps, - new ResolveInfo.DisplayNameComparator(packageManager)); - if (DEBUG_LOADERS) { - Log.d(TAG, "sort took " - + (SystemClock.uptimeMillis()-sortTime) + "ms"); - } - } - - final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - - startIndex = i; - for (int j=0; i added = mAllAppsList.added; - mAllAppsList.added = new ArrayList(); - - mHandler.post(new Runnable() { - public void run() { - final long t = SystemClock.uptimeMillis(); - if (callbacks != null) { - if (first) { - mBeforeFirstLoad = false; - callbacks.bindAllApplications(added); - } else { - callbacks.bindAppsAdded(added); - } - if (DEBUG_LOADERS) { - Log.d(TAG, "bound " + added.size() + " apps in " - + (SystemClock.uptimeMillis() - t) + "ms"); - } - } else { - Log.i(TAG, "not binding apps: no Launcher activity"); - } - } - }); - - if (DEBUG_LOADERS) { - Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in " - + (SystemClock.uptimeMillis()-t2) + "ms"); - } - } - - if (mAllAppsLoadDelay > 0 && i < N) { - try { - if (DEBUG_LOADERS) { - Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms"); - } - Thread.sleep(mAllAppsLoadDelay); - } catch (InterruptedException exc) { } - } - } - if (DEBUG_LOADERS) { - Log.d(TAG, "cached all " + N + " apps in " - + (SystemClock.uptimeMillis()-t) + "ms" - + (mAllAppsLoadDelay > 0 ? " (including delay)" : "")); + Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in " + + (SystemClock.uptimeMillis()-t2) + "ms"); + } + + if (mAllAppsLoadDelay > 0 && i < N) { + try { + if (DEBUG_LOADERS) { + Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms"); + } + Thread.sleep(mAllAppsLoadDelay); + } catch (InterruptedException exc) { } } } - public void dumpState() { - Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext); - Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread); - Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching); - Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped); - Log.d(TAG, "mLoader.mLoaderThread.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished); + if (DEBUG_LOADERS) { + Log.d(TAG, "cached all " + N + " apps in " + + (SystemClock.uptimeMillis()-t) + "ms" + + (mAllAppsLoadDelay > 0 ? " (including delay)" : "")); } } public void dumpState() { - Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size()); - if (mLoaderThread != null) { - mLoaderThread.dumpState(); - } else { - Log.d(TAG, "mLoader.mLoaderThread=null"); + Log.d(TAG, "mLoaderTask.mContext=" + mContext); + Log.d(TAG, "mLoaderTask.mWaitThread=" + mWaitThread); + Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching); + Log.d(TAG, "mLoaderTask.mStopped=" + mStopped); + Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished); + } + } + + void enqueuePackageUpdated(PackageUpdatedTask task) { + mWorker.post(task); + } + + private class PackageUpdatedTask implements Runnable { + int mOp; + String[] mPackages; + + public static final int OP_NONE = 0; + public static final int OP_ADD = 1; + public static final int OP_UPDATE = 2; + public static final int OP_REMOVE = 3; // uninstlled + public static final int OP_UNAVAILABLE = 4; // external media unmounted + + + public PackageUpdatedTask(int op, String[] packages) { + mOp = op; + mPackages = packages; + } + + public void run() { + final Context context = mApp; + + final String[] packages = mPackages; + final int N = packages.length; + switch (mOp) { + case OP_ADD: + for (int i=0; i added = null; + ArrayList removed = null; + ArrayList modified = null; + + if (mAllAppsList.added.size() > 0) { + added = mAllAppsList.added; + mAllAppsList.added = new ArrayList(); + } + if (mAllAppsList.removed.size() > 0) { + removed = mAllAppsList.removed; + mAllAppsList.removed = new ArrayList(); + for (ApplicationInfo info: removed) { + mIconCache.remove(info.intent.getComponent()); + } + } + if (mAllAppsList.modified.size() > 0) { + modified = mAllAppsList.modified; + mAllAppsList.modified = new ArrayList(); + } + + final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null; + if (callbacks == null) { + Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading."); + return; + } + + if (added != null) { + final ArrayList addedFinal = added; + mHandler.post(new Runnable() { + public void run() { + if (callbacks == mCallbacks.get()) { + callbacks.bindAppsAdded(addedFinal); + } + } + }); + } + if (modified != null) { + final ArrayList modifiedFinal = modified; + mHandler.post(new Runnable() { + public void run() { + if (callbacks == mCallbacks.get()) { + callbacks.bindAppsUpdated(modifiedFinal); + } + } + }); + } + if (removed != null) { + final boolean permanent = mOp != OP_UNAVAILABLE; + final ArrayList removedFinal = removed; + mHandler.post(new Runnable() { + public void run() { + if (callbacks == mCallbacks.get()) { + callbacks.bindAppsRemoved(removedFinal, permanent); + } + } + }); } } } @@ -1583,12 +1564,16 @@ public class LauncherModel extends BroadcastReceiver { }; public void dumpState() { - Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad); Log.d(TAG, "mCallbacks=" + mCallbacks); ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data); ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added); ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed); ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified); - mLoader.dumpState(); + Log.d(TAG, "mItems size=" + mItems.size()); + if (mLoaderTask != null) { + mLoaderTask.dumpState(); + } else { + Log.d(TAG, "mLoaderTask=null"); + } } }