summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJoe Onorato <joeo@android.com>2010-06-17 13:28:48 -0400
committerJoe Onorato <joeo@android.com>2010-06-18 14:40:26 -0400
commit3611578579b4bfb25616085dafdb1a45207394f9 (patch)
tree3aff14280002bacd3e2aa9fd2a48ede4296b4acc /src
parentab66ed2e067cbd1bc415b6cf66f3b5191b166461 (diff)
downloadandroid_packages_apps_Trebuchet-3611578579b4bfb25616085dafdb1a45207394f9.tar.gz
android_packages_apps_Trebuchet-3611578579b4bfb25616085dafdb1a45207394f9.tar.bz2
android_packages_apps_Trebuchet-3611578579b4bfb25616085dafdb1a45207394f9.zip
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
Diffstat (limited to 'src')
-rw-r--r--src/com/android/launcher2/Launcher.java6
-rw-r--r--src/com/android/launcher2/LauncherModel.java1583
2 files changed, 788 insertions, 801 deletions
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 6bd915aa0..a5988bf6c 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 17f75732a..eb341f68a 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");
+ }
}
}