diff options
Diffstat (limited to 'src/com/android/launcher3/LauncherModel.java')
-rw-r--r-- | src/com/android/launcher3/LauncherModel.java | 400 |
1 files changed, 240 insertions, 160 deletions
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index d09880542..0b69a09c9 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -48,6 +48,7 @@ import java.net.URISyntaxException; import java.text.Collator; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -68,8 +69,6 @@ public class LauncherModel extends BroadcastReceiver { private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons private final boolean mAppsCanBeOnExternalStorage; - private int mBatchSize; // 0 is all apps at once - private int mAllAppsLoadDelay; // milliseconds between batches private final LauncherAppState mApp; private final Object mLock = new Object(); @@ -149,24 +148,27 @@ public class LauncherModel extends BroadcastReceiver { public boolean setLoadOnResume(); public int getCurrentWorkspaceScreen(); public void startBinding(); - public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end); + public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end, + boolean forceAnimateIcons); public void bindScreens(ArrayList<Long> orderedScreenIds); + public void bindAddScreens(ArrayList<Long> orderedScreenIds); public void bindFolders(HashMap<Long,FolderInfo> folders); public void finishBindingItems(boolean upgradePath); public void bindAppWidget(LauncherAppWidgetInfo info); public void bindAllApplications(ArrayList<ApplicationInfo> apps); - public void bindAppsAdded(ArrayList<ApplicationInfo> apps); public void bindAppsUpdated(ArrayList<ApplicationInfo> apps); public void bindComponentsRemoved(ArrayList<String> packageNames, ArrayList<ApplicationInfo> appInfos, boolean matchPackageNamesOnly); public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts); - public boolean isAllAppsVisible(); - public boolean isAllAppsButtonRank(int rank); public void bindSearchablesChanged(); public void onPageBoundSynchronously(int page); } + public interface ItemInfoFilter { + public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn); + } + LauncherModel(LauncherAppState app, IconCache iconCache) { final Context context = app.getContext(); @@ -179,8 +181,6 @@ public class LauncherModel extends BroadcastReceiver { mIconCache.getFullResDefaultActivityIcon(), context); final Resources res = context.getResources(); - mAllAppsLoadDelay = res.getInteger(R.integer.config_allAppsBatchLoadDelay); - mBatchSize = res.getInteger(R.integer.config_allAppsBatchSize); Configuration config = res.getConfiguration(); mPreviousConfigMcc = config.mcc; } @@ -243,12 +243,13 @@ public class LauncherModel extends BroadcastReceiver { LauncherModel model = app.getModel(); boolean found = false; synchronized (app) { - // Flush the LauncherModel worker thread, so that if we just did another - // processInstallShortcut, we give it time for its shortcut to get added to the - // database (getItemsInLocalCoordinates reads the database) - model.flushWorkerThread(); + if (sWorkerThread.getThreadId() != Process.myTid()) { + // Flush the LauncherModel worker thread, so that if we just did another + // processInstallShortcut, we give it time for its shortcut to get added to the + // database (getItemsInLocalCoordinates reads the database) + model.flushWorkerThread(); + } final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context); - final boolean shortcutExists = LauncherModel.shortcutExists(context, name, launchIntent); // Try adding to the workspace screens incrementally, starting at the default or center // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1)) @@ -265,6 +266,74 @@ public class LauncherModel extends BroadcastReceiver { return null; } + public void addAndBindAddedApps(final Context context, final ArrayList<ApplicationInfo> added, + final Callbacks callbacks) { + // Process the newly added applications and add them to the database first + Runnable r = new Runnable() { + public void run() { + final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>(); + final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>(); + + synchronized(sBgLock) { + Iterator<ApplicationInfo> iter = added.iterator(); + while (iter.hasNext()) { + ApplicationInfo a = iter.next(); + final String name = a.title.toString(); + final Intent launchIntent = a.intent; + + // Short-circuit this logic if the icon exists somewhere on the workspace + if (LauncherModel.shortcutExists(context, name, launchIntent)) { + continue; + } + + // Add this icon to the db, creating a new page if necessary + Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context, + name, launchIntent); + if (coords == null) { + // If we can't find a valid position, then just add a new screen. + // This takes time so we need to re-queue the add until the new + // page is added. + LauncherAppState appState = LauncherAppState.getInstance(); + LauncherProvider lp = appState.getLauncherProvider(); + long screenId = lp.generateNewScreenId(); + // Update the model + sBgWorkspaceScreens.add(screenId); + updateWorkspaceScreenOrder(context, sBgWorkspaceScreens); + // Save the screen id for binding in the workspace + addedWorkspaceScreensFinal.add(screenId); + // Find the coordinate again + coords = LauncherModel.findNextAvailableIconSpace(context, + a.title.toString(), a.intent); + } + if (coords == null) { + throw new RuntimeException("Coordinates should not be null"); + } + + final ShortcutInfo shortcutInfo = a.makeShortcut(); + // Add the shortcut to the db + addItemToDatabase(context, shortcutInfo, + LauncherSettings.Favorites.CONTAINER_DESKTOP, + coords.first, coords.second[0], coords.second[1], false); + // Save the ShortcutInfo for binding in the workspace + addedShortcutsFinal.add(shortcutInfo); + } + } + + runOnMainThread(new Runnable() { + public void run() { + Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; + if (callbacks == cb && cb != null) { + callbacks.bindAddScreens(addedWorkspaceScreensFinal); + callbacks.bindItems(addedShortcutsFinal, 0, + addedShortcutsFinal.size(), true); + } + } + }); + } + }; + runOnWorkerThread(r); + } + public Bitmap getFallbackIcon() { return Bitmap.createBitmap(mDefaultIcon); } @@ -868,14 +937,12 @@ public class LauncherModel extends BroadcastReceiver { * a list of screen ids in the order that they should appear. */ void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) { - updateWorkspaceScreenOrder(context, screens, null); - } - void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens, final Runnable mainThreadCb) { + final ArrayList<Long> screensCopy = new ArrayList<Long>(screens); final ContentResolver cr = context.getContentResolver(); final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI; // Remove any negative screen ids -- these aren't persisted - Iterator<Long> iter = screens.iterator(); + Iterator<Long> iter = screensCopy.iterator(); while (iter.hasNext()) { long id = iter.next(); if (id < 0) { @@ -886,8 +953,6 @@ public class LauncherModel extends BroadcastReceiver { Runnable r = new Runnable() { @Override public void run() { - final ArrayList<Long> screensCopy = new ArrayList<Long>(); - // Clear the table cr.delete(uri, null, null); int count = screens.size(); @@ -897,7 +962,6 @@ public class LauncherModel extends BroadcastReceiver { long screenId = screens.get(i); v.put(LauncherSettings.WorkspaceScreens._ID, screenId); v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i); - screensCopy.add(screenId); values[i] = v; } cr.bulkInsert(uri, values); @@ -906,14 +970,6 @@ public class LauncherModel extends BroadcastReceiver { } }; runOnWorkerThread(r); - if (mainThreadCb != null) { - runOnWorkerThread(new Runnable() { - @Override - public void run() { - runOnMainThread(mainThreadCb); - } - }); - } } /** @@ -1279,8 +1335,6 @@ public class LauncherModel extends BroadcastReceiver { // 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. @@ -1290,13 +1344,8 @@ public class LauncherModel extends BroadcastReceiver { android.os.Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); } - if (loadWorkspaceFirst) { - if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace"); - loadAndBindWorkspace(); - } else { - if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps"); - loadAndBindAllApps(); - } + if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace"); + loadAndBindWorkspace(); if (mStopped) { break keep_running; @@ -1313,13 +1362,8 @@ public class LauncherModel extends BroadcastReceiver { waitForIdle(); // second step - if (loadWorkspaceFirst) { - if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps"); - loadAndBindAllApps(); - } else { - if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace"); - loadAndBindWorkspace(); - } + if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps"); + loadAndBindAllApps(); // Restore the default thread priority after we are done loading items synchronized (mLock) { @@ -1952,7 +1996,8 @@ public class LauncherModel extends BroadcastReceiver { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { - callbacks.bindItems(workspaceItems, start, start+chunkSize); + callbacks.bindItems(workspaceItems, start, start+chunkSize, + false); } } }; @@ -2119,7 +2164,7 @@ public class LauncherModel extends BroadcastReceiver { Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded); } if (!mAllAppsLoaded) { - loadAllAppsByBatch(); + loadAllApps(); synchronized (LoaderTask.this) { if (mStopped) { return; @@ -2157,124 +2202,84 @@ public class LauncherModel extends BroadcastReceiver { } }; boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid()); - if (oldCallbacks.isAllAppsVisible() && isRunningOnMainThread) { + if (isRunningOnMainThread) { r.run(); } else { mHandler.post(r); } } - private void loadAllAppsByBatch() { - final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + private void loadAllApps() { + final long loadTime = 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)"); + Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)"); return; } + final PackageManager packageManager = mContext.getPackageManager(); 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) { - if (i == 0) { - mBgAllAppsList.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; - } + // Clear the list of apps + mBgAllAppsList.clear(); - final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - Collections.sort(apps, - new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache)); - if (DEBUG_LOADERS) { - Log.d(TAG, "sort took " - + (SystemClock.uptimeMillis()-sortTime) + "ms"); - } - } - - final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - - startIndex = i; - for (int j=0; i<N && j<batchSize; j++) { - // This builds the icon bitmaps. - mBgAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i), - mIconCache, mLabelCache)); - i++; - } - - final boolean first = i <= batchSize; - final Callbacks callbacks = tryGetCallbacks(oldCallbacks); - final ArrayList<ApplicationInfo> added = mBgAllAppsList.added; - mBgAllAppsList.added = new ArrayList<ApplicationInfo>(); + // Query for the set of apps + final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0); + if (DEBUG_LOADERS) { + Log.d(TAG, "queryIntentActivities took " + + (SystemClock.uptimeMillis()-qiaTime) + "ms"); + Log.d(TAG, "queryIntentActivities got " + apps.size() + " apps"); + } + // Fail if we don't have any apps + if (apps == null || apps.isEmpty()) { + return; + } + // Sort the applications by name + final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + Collections.sort(apps, + new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache)); + if (DEBUG_LOADERS) { + Log.d(TAG, "sort took " + + (SystemClock.uptimeMillis()-sortTime) + "ms"); + } - mHandler.post(new Runnable() { - public void run() { - final long t = SystemClock.uptimeMillis(); - if (callbacks != null) { - 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"); - } - } - }); + // Create the ApplicationInfos + final long addTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; + for (int i = 0; i < apps.size(); i++) { + // This builds the icon bitmaps. + mBgAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i), + mIconCache, mLabelCache)); + } - if (DEBUG_LOADERS) { - Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in " - + (SystemClock.uptimeMillis()-t2) + "ms"); - } + final Callbacks callbacks = tryGetCallbacks(oldCallbacks); + final ArrayList<ApplicationInfo> added = mBgAllAppsList.added; + mBgAllAppsList.added = new ArrayList<ApplicationInfo>(); - if (mAllAppsLoadDelay > 0 && i < N) { - try { + // Post callback on main thread + mHandler.post(new Runnable() { + public void run() { + final long bindTime = SystemClock.uptimeMillis(); + if (callbacks != null) { + callbacks.bindAllApplications(added); if (DEBUG_LOADERS) { - Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms"); + Log.d(TAG, "bound " + added.size() + " apps in " + + (SystemClock.uptimeMillis() - bindTime) + "ms"); } - Thread.sleep(mAllAppsLoadDelay); - } catch (InterruptedException exc) { } + } else { + Log.i(TAG, "not binding apps: no Launcher activity"); + } } - } + }); if (DEBUG_LOADERS) { - Log.d(TAG, "cached all " + N + " apps in " - + (SystemClock.uptimeMillis()-t) + "ms" - + (mAllAppsLoadDelay > 0 ? " (including delay)" : "")); + Log.d(TAG, "Icons processed in " + + (SystemClock.uptimeMillis() - loadTime) + "ms"); } } @@ -2364,18 +2369,26 @@ public class LauncherModel extends BroadcastReceiver { } if (added != null) { - final ArrayList<ApplicationInfo> addedFinal = added; - mHandler.post(new Runnable() { - public void run() { - Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; - if (callbacks == cb && cb != null) { - callbacks.bindAppsAdded(addedFinal); - } - } - }); + // Ensure that we add all the workspace applications to the db + Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; + addAndBindAddedApps(context, added, cb); } if (modified != null) { final ArrayList<ApplicationInfo> modifiedFinal = modified; + + // Update the launcher db to reflect the changes + for (ApplicationInfo a : modifiedFinal) { + ArrayList<ItemInfo> infos = + getItemInfoForComponentName(a.componentName); + for (ItemInfo i : infos) { + if (isShortcutInfoUpdateable(i)) { + ShortcutInfo info = (ShortcutInfo) i; + info.title = a.title.toString(); + updateItemInDatabase(context, info); + } + } + } + mHandler.post(new Runnable() { public void run() { Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; @@ -2388,16 +2401,34 @@ public class LauncherModel extends BroadcastReceiver { // If a package has been removed, or an app has been removed as a result of // an update (for example), make the removed callback. if (mOp == OP_REMOVE || !removedApps.isEmpty()) { - final boolean permanent = (mOp == OP_REMOVE); + final boolean packageRemoved = (mOp == OP_REMOVE); final ArrayList<String> removedPackageNames = new ArrayList<String>(Arrays.asList(packages)); + // Update the launcher db to reflect the removal of apps + if (packageRemoved) { + for (String pn : removedPackageNames) { + ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn); + for (ItemInfo i : infos) { + deleteItemFromDatabase(context, i); + } + } + } else { + for (ApplicationInfo a : removedApps) { + ArrayList<ItemInfo> infos = + getItemInfoForComponentName(a.componentName); + for (ItemInfo i : infos) { + deleteItemFromDatabase(context, i); + } + } + } + mHandler.post(new Runnable() { public void run() { Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; if (callbacks == cb && cb != null) { callbacks.bindComponentsRemoved(removedPackageNames, - removedApps, permanent); + removedApps, packageRemoved); } } }); @@ -2532,22 +2563,71 @@ public class LauncherModel extends BroadcastReceiver { return info; } - /** - * Returns the set of workspace ShortcutInfos with the specified intent. - */ - static ArrayList<ItemInfo> getWorkspaceShortcutItemInfosWithIntent(Intent intent) { - ArrayList<ItemInfo> items = new ArrayList<ItemInfo>(); - synchronized (sBgLock) { - for (ItemInfo info : sBgWorkspaceItems) { - if (info instanceof ShortcutInfo) { - ShortcutInfo shortcut = (ShortcutInfo) info; - if (shortcut.intent.toUri(0).equals(intent.toUri(0))) { - items.add(shortcut); + static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos, + ItemInfoFilter f) { + HashSet<ItemInfo> filtered = new HashSet<ItemInfo>(); + for (ItemInfo i : infos) { + if (i instanceof ShortcutInfo) { + ShortcutInfo info = (ShortcutInfo) i; + ComponentName cn = info.intent.getComponent(); + if (cn != null && f.filterItem(null, info, cn)) { + filtered.add(info); + } + } else if (i instanceof FolderInfo) { + FolderInfo info = (FolderInfo) i; + for (ShortcutInfo s : info.contents) { + ComponentName cn = s.intent.getComponent(); + if (cn != null && f.filterItem(info, s, cn)) { + filtered.add(s); } } + } else if (i instanceof LauncherAppWidgetInfo) { + LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i; + ComponentName cn = info.providerName; + if (cn != null && f.filterItem(null, info, cn)) { + filtered.add(info); + } } } - return items; + return new ArrayList<ItemInfo>(filtered); + } + + private ArrayList<ItemInfo> getItemInfoForPackageName(final String pn) { + HashSet<ItemInfo> infos = new HashSet<ItemInfo>(); + ItemInfoFilter filter = new ItemInfoFilter() { + @Override + public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) { + return cn.getPackageName().equals(pn); + } + }; + return filterItemInfos(sBgItemsIdMap.values(), filter); + } + + private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname) { + HashSet<ItemInfo> infos = new HashSet<ItemInfo>(); + ItemInfoFilter filter = new ItemInfoFilter() { + @Override + public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) { + return cn.equals(cname); + } + }; + return filterItemInfos(sBgItemsIdMap.values(), filter); + } + + public static boolean isShortcutInfoUpdateable(ItemInfo i) { + if (i instanceof ShortcutInfo) { + ShortcutInfo info = (ShortcutInfo) i; + // We need to check for ACTION_MAIN otherwise getComponent() might + // return null for some shortcuts (for instance, for shortcuts to + // web pages.) + Intent intent = info.intent; + ComponentName name = intent.getComponent(); + if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && + Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { + return true; + } + } + return false; } /** |