diff options
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/android/launcher2/Launcher.java | 6 | ||||
-rw-r--r-- | src/com/android/launcher2/LauncherModel.java | 1583 |
2 files changed, 788 insertions, 801 deletions
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java index 6bd915a..a5988bf 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<ApplicationInfo> apps) { + public void bindAppsRemoved(ArrayList<ApplicationInfo> 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 17f7573..eb341f6 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<Callbacks> mCallbacks; - private final Object mAllAppsListLock = new Object(); - private AllAppsList mAllAppsList; + private AllAppsList mAllAppsList; // only access in worker thread private IconCache mIconCache; + final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>(); + final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>(); + final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>(); private Bitmap mDefaultIcon; @@ -96,7 +101,7 @@ public class LauncherModel extends BroadcastReceiver { public void bindAllApplications(ArrayList<ApplicationInfo> apps); public void bindAppsAdded(ArrayList<ApplicationInfo> apps); public void bindAppsUpdated(ArrayList<ApplicationInfo> apps); - public void bindAppsRemoved(ArrayList<ApplicationInfo> apps); + public void bindAppsRemoved(ArrayList<ApplicationInfo> 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; - - ArrayList<ApplicationInfo> added = null; - ArrayList<ApplicationInfo> removed = null; - ArrayList<ApplicationInfo> modified = null; - - 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(); + if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent); + + final String action = intent.getAction(); - 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 (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; - } + int op = PackageUpdatedTask.OP_NONE; - 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); - } - } + if (packageName == null || packageName.length() == 0) { + // they sent us a bad intent + return; + } - if (mAllAppsList.added.size() > 0) { - added = mAllAppsList.added; - mAllAppsList.added = new ArrayList<ApplicationInfo>(); + 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; } - if (mAllAppsList.removed.size() > 0) { - removed = mAllAppsList.removed; - mAllAppsList.removed = new ArrayList<ApplicationInfo>(); - for (ApplicationInfo info: removed) { - mIconCache.remove(info.intent.getComponent()); - } - } - if (mAllAppsList.modified.size() > 0) { - modified = mAllAppsList.modified; - mAllAppsList.modified = new ArrayList<ApplicationInfo>(); - } - - 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; + // 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 (added != null) { - final ArrayList<ApplicationInfo> addedFinal = added; - mHandler.post(new Runnable() { - public void run() { - callbacks.bindAppsAdded(addedFinal); - } - }); - } - if (modified != null) { - final ArrayList<ApplicationInfo> modifiedFinal = modified; - mHandler.post(new Runnable() { - public void run() { - callbacks.bindAppsUpdated(modifiedFinal); - } - }); - } - if (removed != null) { - final ArrayList<ApplicationInfo> 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); - } + if (op != PackageUpdatedTask.OP_NONE) { + enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName })); } - } - } - public class Loader { - private static final int ITEMS_CHUNK = 6; + } 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)); - private LoaderThread mLoaderThread; + } 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)); - final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>(); - final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>(); - final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>(); - - /** - * 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); - } + 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(); + // 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; } - mLoaderThread = new LoaderThread(context, oldThread, isLaunching); - mLoaderThread.start(); + oldTask.stopLocked(); } + mLoaderTask = new LoaderTask(context, isLaunching); + mWorker.post(mLoaderTask); } } + } - public void stopLoader() { - synchronized (mLock) { - if (mLoaderThread != null) { - mLoaderThread.stopLocked(); - } + public void stopLoader() { + synchronized (mLock) { + if (mLoaderTask != null) { + mLoaderTask.stopLocked(); } } + } - /** - * 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; - - LoaderThread(Context context, Thread waitThread, boolean isLaunching) { - mContext = context; - mWaitThread = waitThread; - mIsLaunching = isLaunching; - } - - boolean isLaunching() { - return mIsLaunching; - } + /** + * 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; + } - /** - * 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; - } - } + boolean isLaunching() { + return mIsLaunching; + } - private void loadAndBindWorkspace() { - // Load the workspace + private void loadAndBindWorkspace() { + // Load the workspace - // 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; + // 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); + } + if (true || !mWorkspaceLoaded) { + loadWorkspace(); + if (mStopped) { + return; } + mWorkspaceLoaded = true; + } - // 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(); + } - // 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 (LoaderTask.this) { + final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - 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 } + }); + + 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"); - } + } + 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; - - synchronized (mLock) { - // Setting the reference is atomic, but we can't do it inside the other critical - // sections. - mLoaderThread = null; - } + // Clear out this reference, otherwise we end up holding it until all of the + // callback runnables are done. + mContext = null; - if (PROFILE_LOADERS) { - android.os.Debug.stopMethodTracing(); + synchronized (mLock) { + // If we are still the last one to be scheduled, remove ourselves. + if (mLoaderTask == this) { + mLoaderTask = null; } + } - // 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(); } }); + } else { + mHandler.postIdle(new Runnable() { + public void run() { + System.gc(); + } + }); } + } - public void stopLocked() { - synchronized (LoaderThread.this) { - mStopped = true; - this.notify(); - } + 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; - } - - if (mCallbacks == null) { - return null; - } + /** + * 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; + } - final Callbacks callbacks = mCallbacks.get(); - if (callbacks != oldCallbacks) { - return null; - } - if (callbacks == null) { - Log.w(TAG, "no mCallbacks"); - return null; - } + if (mCallbacks == null) { + return null; + } - return callbacks; + 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; - } + // 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; - } + 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; - } + } + 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; } + return true; + } - private void loadWorkspace() { - final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + private void loadWorkspace() { + final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - 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 Context context = mContext; + final ContentResolver contentResolver = context.getContentResolver(); + final PackageManager manager = context.getPackageManager(); + final AppWidgetManager widgets = AppWidgetManager.getInstance(context); + final boolean isSafeMode = manager.isSafeMode(); - mItems.clear(); - mAppWidgets.clear(); - mFolders.clear(); + mItems.clear(); + mAppWidgets.clear(); + mFolders.clear(); - final ArrayList<Long> itemsToRemove = new ArrayList<Long>(); + final ArrayList<Long> itemsToRemove = new ArrayList<Long>(); - final Cursor c = contentResolver.query( - LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); + final Cursor c = contentResolver.query( + LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); - final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y]; + final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y]; - 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); - - ShortcutInfo info; - String intentDescription; - LauncherAppWidgetInfo appWidgetInfo; - int container; - long id; - Intent intent; - - while (!mStopped && c.moveToNext()) { - try { - int itemType = c.getInt(itemTypeIndex); - - switch (itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: - intentDescription = c.getString(intentIndex); - try { - intent = Intent.parseUri(intentDescription, 0); - } catch (URISyntaxException e) { - continue; + 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); + + ShortcutInfo info; + String intentDescription; + LauncherAppWidgetInfo appWidgetInfo; + int container; + long id; + Intent intent; + + while (!mStopped && c.moveToNext()) { + try { + int itemType = c.getInt(itemTypeIndex); + + switch (itemType) { + case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: + case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: + intentDescription = c.getString(intentIndex); + try { + intent = Intent.parseUri(intentDescription, 0); + } catch (URISyntaxException e) { + continue; + } + + if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + info = getShortcutInfo(manager, intent, context, c, iconIndex, + titleIndex); + } else { + info = getShortcutInfo(c, context, iconTypeIndex, + iconPackageIndex, iconResourceIndex, iconIndex, + titleIndex); + } + + if (info != null) { + updateSavedIcon(context, info, c, iconIndex); + + 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); + + // check & update map of what's occupied + if (!checkItemPlacement(occupied, info)) { + break; } - 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 (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; - if (info != null) { - updateSavedIcon(context, info, c, iconIndex); + case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: + id = c.getLong(idIndex); + UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id); - 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); + folderInfo.title = c.getString(titleIndex); - // check & update map of what's occupied - if (!checkItemPlacement(occupied, info)) { - break; - } + folderInfo.id = id; + container = c.getInt(containerIndex); + folderInfo.container = container; + folderInfo.screen = c.getInt(screenIndex); + folderInfo.cellX = c.getInt(cellXIndex); + folderInfo.cellY = c.getInt(cellYIndex); - switch (container) { - case LauncherSettings.Favorites.CONTAINER_DESKTOP: - mItems.add(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); - } + // check & update map of what's occupied + if (!checkItemPlacement(occupied, folderInfo)) { break; + } - case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: - id = c.getLong(idIndex); - UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id); + 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)); - folderInfo.title = c.getString(titleIndex); + // 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 + } + } - folderInfo.id = id; + liveFolderInfo.title = c.getString(titleIndex); + liveFolderInfo.id = id; + liveFolderInfo.uri = uri; container = c.getInt(containerIndex); - folderInfo.container = container; - folderInfo.screen = c.getInt(screenIndex); - folderInfo.cellX = c.getInt(cellXIndex); - folderInfo.cellY = c.getInt(cellYIndex); + 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, folderInfo)) { + 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(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); - 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); + container = c.getInt(containerIndex); + if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { + Log.e(TAG, "Widget found where container " + + "!= CONTAINER_DESKTOP -- ignoring!"); + continue; } - 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; - } + appWidgetInfo.container = c.getInt(containerIndex); - mAppWidgets.add(appWidgetInfo); + // check & update map of what's occupied + if (!checkItemPlacement(occupied, appWidgetInfo)) { + break; } - break; + + mAppWidgets.add(appWidgetInfo); } - } catch (Exception e) { - Log.w(TAG, "Desktop items loading interrupted:", e); + break; } + } catch (Exception e) { + Log.w(TAG, "Desktop items loading interrupted:", e); } - } finally { - c.close(); } + } finally { + c.close(); + } - 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); - } + 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); } } + } - 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) ? "#" : "."); - } + 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 + " ]"); } + Log.d(TAG, "[ " + line + " ]"); } } + } - /** - * 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; - } + /** + * 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 + 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<N; i+=ITEMS_CHUNK) { + final int start = i; + final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i); mHandler.post(new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { - callbacks.startBinding(); + callbacks.bindItems(mItems, start, start+chunkSize); } } }); - // Add the items to the workspace. - N = mItems.size(); - for (int i=0; i<N; i+=ITEMS_CHUNK) { - final int start = i; - final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i); + } + mHandler.post(new Runnable() { + public void run() { + Callbacks callbacks = tryGetCallbacks(oldCallbacks); + if (callbacks != null) { + callbacks.bindFolders(mFolders); + } + } + }); + // Wait until the queue goes empty. + mHandler.post(new Runnable() { + public void run() { + if (DEBUG_LOADERS) { + Log.d(TAG, "Going to start binding widgets soon."); + } + } + }); + // Bind the widgets, one at a time. + // WARNING: this is calling into the workspace from the background thread, + // but since getCurrentScreen() just returns the int, we should be okay. This + // is just a hint for the order, and if it's wrong, we'll be okay. + // TODO: instead, we should have that push the current screen into here. + final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen(); + N = mAppWidgets.size(); + // once for the current screen + for (int i=0; i<N; i++) { + final LauncherAppWidgetInfo widget = mAppWidgets.get(i); + if (widget.screen == currentScreen) { mHandler.post(new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { - callbacks.bindItems(mItems, start, start+chunkSize); + callbacks.bindAppWidget(widget); } } }); } - mHandler.post(new Runnable() { - public void run() { - Callbacks callbacks = tryGetCallbacks(oldCallbacks); - if (callbacks != null) { - callbacks.bindFolders(mFolders); - } - } - }); - // Wait until the queue goes empty. - mHandler.post(new Runnable() { - public void run() { - if (DEBUG_LOADERS) { - Log.d(TAG, "Going to start binding widgets soon."); - } - } - }); - // Bind the widgets, one at a time. - // WARNING: this is calling into the workspace from the background thread, - // but since getCurrentScreen() just returns the int, we should be okay. This - // is just a hint for the order, and if it's wrong, we'll be okay. - // TODO: instead, we should have that push the current screen into here. - final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen(); - N = mAppWidgets.size(); - // once for the current screen - for (int i=0; i<N; i++) { - final LauncherAppWidgetInfo widget = mAppWidgets.get(i); - if (widget.screen == currentScreen) { - mHandler.post(new Runnable() { - public void run() { - Callbacks callbacks = tryGetCallbacks(oldCallbacks); - if (callbacks != null) { - callbacks.bindAppWidget(widget); - } + } + // once for the other screens + for (int i=0; i<N; i++) { + final LauncherAppWidgetInfo widget = mAppWidgets.get(i); + if (widget.screen != currentScreen) { + mHandler.post(new Runnable() { + public void run() { + Callbacks callbacks = tryGetCallbacks(oldCallbacks); + if (callbacks != null) { + callbacks.bindAppWidget(widget); } - }); + } + }); + } + } + // Tell the workspace that we're done. + mHandler.post(new Runnable() { + public void run() { + Callbacks callbacks = tryGetCallbacks(oldCallbacks); + if (callbacks != null) { + callbacks.finishBindingItems(); } } - // once for the other screens - for (int i=0; i<N; i++) { - final LauncherAppWidgetInfo widget = mAppWidgets.get(i); - if (widget.screen != currentScreen) { - mHandler.post(new Runnable() { - public void run() { - Callbacks callbacks = tryGetCallbacks(oldCallbacks); - if (callbacks != null) { - callbacks.bindAppWidget(widget); - } - } - }); + }); + // If we're profiling, this is the last thing in the queue. + mHandler.post(new Runnable() { + public void run() { + if (DEBUG_LOADERS) { + Log.d(TAG, "bound workspace in " + + (SystemClock.uptimeMillis()-t) + "ms"); } } - // Tell the workspace that we're done. - mHandler.post(new Runnable() { - public void run() { - Callbacks callbacks = tryGetCallbacks(oldCallbacks); - if (callbacks != null) { - callbacks.finishBindingItems(); - } + }); + } + + private void loadAndBindAllApps() { + if (DEBUG_LOADERS) { + Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded); + } + if (!mAllAppsLoaded) { + loadAllAppsByBatch(); + if (mStopped) { + return; + } + mAllAppsLoaded = true; + } else { + onlyBindAllApps(); + } + } + + private void onlyBindAllApps() { + 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 (onlyBindAllApps)"); + return; + } + + // shallow copy + final ArrayList<ApplicationInfo> list + = (ArrayList<ApplicationInfo>)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 we're profiling, this is the last thing in the queue. - mHandler.post(new Runnable() { - public void run() { - if (DEBUG_LOADERS) { - Log.d(TAG, "bound workspace in " + 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; } - private void loadAndBindAllApps() { - // Other other threads can unset mAllAppsLoaded, 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 = mAllAppsLoaded; - mAllAppsLoaded = true; - } + final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); - if (DEBUG_LOADERS) Log.d(TAG, "loadAndBindAllApps loaded=" + loaded); - if (!loaded) { - loadAllAppsByBatch(); - if (mStopped) { - mAllAppsLoaded = false; + final PackageManager packageManager = mContext.getPackageManager(); + List<ResolveInfo> 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; } - } else { - onlyBindAllApps(); + 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"); + } } - } - private void onlyBindAllApps() { - 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 (onlyBindAllApps)"); - return; + final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + + startIndex = i; + for (int j=0; i<N && j<batchSize; j++) { + // This builds the icon bitmaps. + mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache)); + i++; } - // shallow copy - final ArrayList<ApplicationInfo> list - = (ArrayList<ApplicationInfo>)mAllAppsList.data.clone(); + final boolean first = i <= batchSize; + final Callbacks callbacks = tryGetCallbacks(oldCallbacks); + final ArrayList<ApplicationInfo> added = mAllAppsList.added; + mAllAppsList.added = new ArrayList<ApplicationInfo>(); + 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"); } } }); + 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) { } + } } - private void loadAllAppsByBatch() { - final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + if (DEBUG_LOADERS) { + Log.d(TAG, "cached all " + N + " apps in " + + (SystemClock.uptimeMillis()-t) + "ms" + + (mAllAppsLoadDelay > 0 ? " (including delay)" : "")); + } + } - // 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; - } + public void dumpState() { + 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); + } + } - final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); - mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); - - final PackageManager packageManager = mContext.getPackageManager(); - List<ResolveInfo> 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; - } + void enqueuePackageUpdated(PackageUpdatedTask task) { + mWorker.post(task); + } - 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"); - } - } + private class PackageUpdatedTask implements Runnable { + int mOp; + String[] mPackages; - final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + 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 - startIndex = i; - for (int j=0; i<N && j<batchSize; j++) { - // This builds the icon bitmaps. - mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache)); - i++; - } - final boolean first = i <= batchSize; - final Callbacks callbacks = tryGetCallbacks(oldCallbacks); - final ArrayList<ApplicationInfo> added = mAllAppsList.added; - mAllAppsList.added = new ArrayList<ApplicationInfo>(); - - 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"); - } - } - }); + public PackageUpdatedTask(int op, String[] packages) { + mOp = op; + mPackages = packages; + } - if (DEBUG_LOADERS) { - Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in " - + (SystemClock.uptimeMillis()-t2) + "ms"); - } - } + public void run() { + final Context context = mApp; - if (mAllAppsLoadDelay > 0 && i < N) { - try { - if (DEBUG_LOADERS) { - Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms"); - } - Thread.sleep(mAllAppsLoadDelay); - } catch (InterruptedException exc) { } + final String[] packages = mPackages; + final int N = packages.length; + switch (mOp) { + case OP_ADD: + for (int i=0; i<N; i++) { + if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]); + mAllAppsList.addPackage(context, packages[i]); } - } + break; + case OP_UPDATE: + for (int i=0; i<N; i++) { + if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]); + mAllAppsList.updatePackage(context, packages[i]); + } + break; + case OP_REMOVE: + case OP_UNAVAILABLE: + for (int i=0; i<N; i++) { + if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]); + mAllAppsList.removePackage(packages[i]); + } + break; + } - if (DEBUG_LOADERS) { - Log.d(TAG, "cached all " + N + " apps in " - + (SystemClock.uptimeMillis()-t) + "ms" - + (mAllAppsLoadDelay > 0 ? " (including delay)" : "")); + ArrayList<ApplicationInfo> added = null; + ArrayList<ApplicationInfo> removed = null; + ArrayList<ApplicationInfo> modified = null; + + if (mAllAppsList.added.size() > 0) { + added = mAllAppsList.added; + mAllAppsList.added = new ArrayList<ApplicationInfo>(); + } + if (mAllAppsList.removed.size() > 0) { + removed = mAllAppsList.removed; + mAllAppsList.removed = new ArrayList<ApplicationInfo>(); + for (ApplicationInfo info: removed) { + mIconCache.remove(info.intent.getComponent()); } } + if (mAllAppsList.modified.size() > 0) { + modified = mAllAppsList.modified; + mAllAppsList.modified = new ArrayList<ApplicationInfo>(); + } - 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); + 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; } - } - public void dumpState() { - Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size()); - if (mLoaderThread != null) { - mLoaderThread.dumpState(); - } else { - Log.d(TAG, "mLoader.mLoaderThread=null"); + if (added != null) { + final ArrayList<ApplicationInfo> addedFinal = added; + mHandler.post(new Runnable() { + public void run() { + if (callbacks == mCallbacks.get()) { + callbacks.bindAppsAdded(addedFinal); + } + } + }); + } + if (modified != null) { + final ArrayList<ApplicationInfo> 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<ApplicationInfo> 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"); + } } } |