1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher; 18 19 import android.content.ComponentName; 20 import android.content.ContentResolver; 21 import android.content.ContentValues; 22 import android.content.Intent; 23 import android.content.Context; 24 import android.content.pm.ActivityInfo; 25 import android.content.pm.PackageManager; 26 import android.content.pm.ResolveInfo; 27 import android.content.res.Resources; 28 import android.database.Cursor; 29 import android.graphics.Bitmap; 30 import android.graphics.BitmapFactory; 31 import android.graphics.drawable.Drawable; 32 import android.net.Uri; 33 import static android.util.Log.*; 34 import android.os.Process; 35 36 import java.util.ArrayList; 37 import java.util.HashMap; 38 import java.util.List; 39 import java.util.Comparator; 40 import java.util.concurrent.atomic.AtomicInteger; 41 import java.lang.ref.WeakReference; 42 import java.text.Collator; 43 import java.net.URISyntaxException; 44 45 /** 46 * Maintains in-memory state of the Launcher. It is expected that there should be only one 47 * LauncherModel object held in a static. Also provide APIs for updating the database state 48 * for the Launcher. 49 */ 50 public class LauncherModel { 51 static final boolean DEBUG_LOADERS = true; 52 static final String LOG_TAG = "HomeLoaders"; 53 54 private static final int UI_NOTIFICATION_RATE = 4; 55 private static final int DEFAULT_APPLICATIONS_NUMBER = 42; 56 private static final long APPLICATION_NOT_RESPONDING_TIMEOUT = 5000; 57 private static final int INITIAL_ICON_CACHE_CAPACITY = 50; 58 59 private static final Collator sCollator = Collator.getInstance(); 60 61 private boolean mApplicationsLoaded; 62 private boolean mDesktopItemsLoaded; 63 64 private ArrayList<ItemInfo> mDesktopItems; 65 private ArrayList<LauncherAppWidgetInfo> mDesktopAppWidgets; 66 private HashMap<Long, FolderInfo> mFolders; 67 68 private ArrayList<ApplicationInfo> mApplications; 69 private ApplicationsAdapter mApplicationsAdapter; 70 private ApplicationsLoader mApplicationsLoader; 71 private DesktopItemsLoader mDesktopItemsLoader; 72 private Thread mApplicationsLoaderThread; 73 private Thread mDesktopLoaderThread; 74 75 private final HashMap<ComponentName, ApplicationInfo> mAppInfoCache = 76 new HashMap<ComponentName, ApplicationInfo>(INITIAL_ICON_CACHE_CAPACITY); 77 abortLoaders()78 synchronized void abortLoaders() { 79 if (DEBUG_LOADERS) d(LOG_TAG, "aborting loaders"); 80 81 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) { 82 if (DEBUG_LOADERS) d(LOG_TAG, " --> aborting applications loader"); 83 mApplicationsLoader.stop(); 84 mApplicationsLoaded = false; 85 } 86 87 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) { 88 if (DEBUG_LOADERS) d(LOG_TAG, " --> aborting workspace loader"); 89 mDesktopItemsLoader.stop(); 90 mDesktopItemsLoaded = false; 91 } 92 } 93 94 /** 95 * Drop our cache of components to their lables & icons. We do 96 * this from Launcher when applications are added/removed. It's a 97 * bit overkill, but it's a rare operation anyway. 98 */ dropApplicationCache()99 synchronized void dropApplicationCache() { 100 mAppInfoCache.clear(); 101 } 102 103 /** 104 * Loads the list of installed applications in mApplications. 105 * 106 * @return true if the applications loader must be started 107 * (see startApplicationsLoader()), false otherwise. 108 */ loadApplications(boolean isLaunching, Launcher launcher, boolean localeChanged)109 synchronized boolean loadApplications(boolean isLaunching, Launcher launcher, 110 boolean localeChanged) { 111 112 if (DEBUG_LOADERS) d(LOG_TAG, "load applications"); 113 114 if (isLaunching && mApplicationsLoaded && !localeChanged) { 115 mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications); 116 if (DEBUG_LOADERS) d(LOG_TAG, " --> applications loaded, return"); 117 return false; 118 } 119 120 stopAndWaitForApplicationsLoader(); 121 122 if (localeChanged) { 123 dropApplicationCache(); 124 } 125 126 if (mApplicationsAdapter == null || isLaunching || localeChanged) { 127 mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER); 128 mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications); 129 } 130 131 mApplicationsLoaded = false; 132 133 if (!isLaunching) { 134 startApplicationsLoaderLocked(launcher, false); 135 return false; 136 } 137 138 return true; 139 } 140 stopAndWaitForApplicationsLoader()141 private synchronized void stopAndWaitForApplicationsLoader() { 142 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) { 143 if (DEBUG_LOADERS) { 144 d(LOG_TAG, " --> wait for applications loader (" + mApplicationsLoader.mId + ")"); 145 } 146 147 mApplicationsLoader.stop(); 148 // Wait for the currently running thread to finish, this can take a little 149 // time but it should be well below the timeout limit 150 try { 151 mApplicationsLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT); 152 } catch (InterruptedException e) { 153 // Empty 154 } 155 } 156 } 157 startApplicationsLoader(Launcher launcher, boolean isLaunching)158 private synchronized void startApplicationsLoader(Launcher launcher, boolean isLaunching) { 159 if (DEBUG_LOADERS) d(LOG_TAG, " --> starting applications loader unlocked"); 160 startApplicationsLoaderLocked(launcher, isLaunching); 161 } 162 startApplicationsLoaderLocked(Launcher launcher, boolean isLaunching)163 private void startApplicationsLoaderLocked(Launcher launcher, boolean isLaunching) { 164 if (DEBUG_LOADERS) d(LOG_TAG, " --> starting applications loader"); 165 166 stopAndWaitForApplicationsLoader(); 167 168 mApplicationsLoader = new ApplicationsLoader(launcher, isLaunching); 169 mApplicationsLoaderThread = new Thread(mApplicationsLoader, "Applications Loader"); 170 mApplicationsLoaderThread.start(); 171 } 172 addPackage(Launcher launcher, String packageName)173 synchronized void addPackage(Launcher launcher, String packageName) { 174 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) { 175 startApplicationsLoaderLocked(launcher, false); 176 return; 177 } 178 179 if (packageName != null && packageName.length() > 0) { 180 final PackageManager packageManager = launcher.getPackageManager(); 181 final List<ResolveInfo> matches = findActivitiesForPackage(packageManager, packageName); 182 183 if (matches.size() > 0) { 184 final ApplicationsAdapter adapter = mApplicationsAdapter; 185 final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache; 186 187 for (ResolveInfo info : matches) { 188 adapter.setNotifyOnChange(false); 189 adapter.add(makeAndCacheApplicationInfo(packageManager, cache, info, launcher)); 190 } 191 192 adapter.sort(new ApplicationInfoComparator()); 193 adapter.notifyDataSetChanged(); 194 } 195 } 196 } 197 removePackage(Launcher launcher, String packageName)198 synchronized void removePackage(Launcher launcher, String packageName) { 199 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) { 200 dropApplicationCache(); // TODO: this could be optimized 201 startApplicationsLoaderLocked(launcher, false); 202 return; 203 } 204 205 if (packageName != null && packageName.length() > 0) { 206 final ApplicationsAdapter adapter = mApplicationsAdapter; 207 208 final List<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>(); 209 final int count = adapter.getCount(); 210 211 for (int i = 0; i < count; i++) { 212 final ApplicationInfo applicationInfo = adapter.getItem(i); 213 final Intent intent = applicationInfo.intent; 214 final ComponentName component = intent.getComponent(); 215 if (packageName.equals(component.getPackageName())) { 216 toRemove.add(applicationInfo); 217 } 218 } 219 220 final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache; 221 for (ApplicationInfo info : toRemove) { 222 adapter.setNotifyOnChange(false); 223 adapter.remove(info); 224 cache.remove(info.intent.getComponent()); 225 } 226 227 if (toRemove.size() > 0) { 228 adapter.sort(new ApplicationInfoComparator()); 229 adapter.notifyDataSetChanged(); 230 } 231 } 232 } 233 updatePackage(Launcher launcher, String packageName)234 synchronized void updatePackage(Launcher launcher, String packageName) { 235 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) { 236 startApplicationsLoaderLocked(launcher, false); 237 return; 238 } 239 240 if (packageName != null && packageName.length() > 0) { 241 final PackageManager packageManager = launcher.getPackageManager(); 242 final ApplicationsAdapter adapter = mApplicationsAdapter; 243 244 final List<ResolveInfo> matches = findActivitiesForPackage(packageManager, packageName); 245 final int count = matches.size(); 246 247 boolean changed = false; 248 249 for (int i = 0; i < count; i++) { 250 final ResolveInfo info = matches.get(i); 251 final ApplicationInfo applicationInfo = findIntent(adapter, 252 info.activityInfo.applicationInfo.packageName, info.activityInfo.name); 253 if (applicationInfo != null) { 254 updateAndCacheApplicationInfo(packageManager, info, applicationInfo, launcher); 255 changed = true; 256 } 257 } 258 259 if (syncLocked(launcher, packageName)) changed = true; 260 261 if (changed) { 262 adapter.sort(new ApplicationInfoComparator()); 263 adapter.notifyDataSetChanged(); 264 } 265 } 266 } 267 updateAndCacheApplicationInfo(PackageManager packageManager, ResolveInfo info, ApplicationInfo applicationInfo, Context context)268 private void updateAndCacheApplicationInfo(PackageManager packageManager, ResolveInfo info, 269 ApplicationInfo applicationInfo, Context context) { 270 271 updateApplicationInfoTitleAndIcon(packageManager, info, applicationInfo, context); 272 273 ComponentName componentName = new ComponentName( 274 info.activityInfo.applicationInfo.packageName, info.activityInfo.name); 275 mAppInfoCache.put(componentName, applicationInfo); 276 } 277 syncPackage(Launcher launcher, String packageName)278 synchronized void syncPackage(Launcher launcher, String packageName) { 279 if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) { 280 startApplicationsLoaderLocked(launcher, false); 281 return; 282 } 283 284 if (packageName != null && packageName.length() > 0) { 285 if (syncLocked(launcher, packageName)) { 286 final ApplicationsAdapter adapter = mApplicationsAdapter; 287 adapter.sort(new ApplicationInfoComparator()); 288 adapter.notifyDataSetChanged(); 289 } 290 } 291 } 292 syncLocked(Launcher launcher, String packageName)293 private boolean syncLocked(Launcher launcher, String packageName) { 294 final PackageManager packageManager = launcher.getPackageManager(); 295 final List<ResolveInfo> matches = findActivitiesForPackage(packageManager, packageName); 296 297 if (matches.size() > 0) { 298 final ApplicationsAdapter adapter = mApplicationsAdapter; 299 300 // Find disabled activities and remove them from the adapter 301 boolean removed = removeDisabledActivities(packageName, matches, adapter); 302 // Find enable activities and add them to the adapter 303 // Also updates existing activities with new labels/icons 304 boolean added = addEnabledAndUpdateActivities(matches, adapter, launcher); 305 306 return added || removed; 307 } 308 309 return false; 310 } 311 findActivitiesForPackage(PackageManager packageManager, String packageName)312 private static List<ResolveInfo> findActivitiesForPackage(PackageManager packageManager, 313 String packageName) { 314 315 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 316 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 317 318 final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0); 319 final List<ResolveInfo> matches = new ArrayList<ResolveInfo>(); 320 321 if (apps != null) { 322 // Find all activities that match the packageName 323 int count = apps.size(); 324 for (int i = 0; i < count; i++) { 325 final ResolveInfo info = apps.get(i); 326 final ActivityInfo activityInfo = info.activityInfo; 327 if (packageName.equals(activityInfo.packageName)) { 328 matches.add(info); 329 } 330 } 331 } 332 333 return matches; 334 } 335 addEnabledAndUpdateActivities(List<ResolveInfo> matches, ApplicationsAdapter adapter, Launcher launcher)336 private boolean addEnabledAndUpdateActivities(List<ResolveInfo> matches, 337 ApplicationsAdapter adapter, Launcher launcher) { 338 339 final List<ApplicationInfo> toAdd = new ArrayList<ApplicationInfo>(); 340 final int count = matches.size(); 341 342 boolean changed = false; 343 344 for (int i = 0; i < count; i++) { 345 final ResolveInfo info = matches.get(i); 346 final ApplicationInfo applicationInfo = findIntent(adapter, 347 info.activityInfo.applicationInfo.packageName, info.activityInfo.name); 348 if (applicationInfo == null) { 349 toAdd.add(makeAndCacheApplicationInfo(launcher.getPackageManager(), 350 mAppInfoCache, info, launcher)); 351 changed = true; 352 } else { 353 updateAndCacheApplicationInfo( 354 launcher.getPackageManager(), info, applicationInfo, launcher); 355 changed = true; 356 } 357 } 358 359 for (ApplicationInfo info : toAdd) { 360 adapter.setNotifyOnChange(false); 361 adapter.add(info); 362 } 363 364 return changed; 365 } 366 removeDisabledActivities(String packageName, List<ResolveInfo> matches, ApplicationsAdapter adapter)367 private boolean removeDisabledActivities(String packageName, List<ResolveInfo> matches, 368 ApplicationsAdapter adapter) { 369 370 final List<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>(); 371 final int count = adapter.getCount(); 372 373 boolean changed = false; 374 375 for (int i = 0; i < count; i++) { 376 final ApplicationInfo applicationInfo = adapter.getItem(i); 377 final Intent intent = applicationInfo.intent; 378 final ComponentName component = intent.getComponent(); 379 if (packageName.equals(component.getPackageName())) { 380 if (!findIntent(matches, component)) { 381 toRemove.add(applicationInfo); 382 changed = true; 383 } 384 } 385 } 386 387 final HashMap<ComponentName, ApplicationInfo> cache = mAppInfoCache; 388 for (ApplicationInfo info : toRemove) { 389 adapter.setNotifyOnChange(false); 390 adapter.remove(info); 391 cache.remove(info.intent.getComponent()); 392 } 393 394 return changed; 395 } 396 findIntent(ApplicationsAdapter adapter, String packageName, String name)397 private static ApplicationInfo findIntent(ApplicationsAdapter adapter, String packageName, 398 String name) { 399 400 final int count = adapter.getCount(); 401 for (int i = 0; i < count; i++) { 402 final ApplicationInfo applicationInfo = adapter.getItem(i); 403 final Intent intent = applicationInfo.intent; 404 final ComponentName component = intent.getComponent(); 405 if (packageName.equals(component.getPackageName()) && 406 name.equals(component.getClassName())) { 407 return applicationInfo; 408 } 409 } 410 411 return null; 412 } 413 findIntent(List<ResolveInfo> apps, ComponentName component)414 private static boolean findIntent(List<ResolveInfo> apps, ComponentName component) { 415 final String className = component.getClassName(); 416 for (ResolveInfo info : apps) { 417 final ActivityInfo activityInfo = info.activityInfo; 418 if (activityInfo.name.equals(className)) { 419 return true; 420 } 421 } 422 return false; 423 } 424 getApplicationInfoIcon(PackageManager manager, ApplicationInfo info)425 Drawable getApplicationInfoIcon(PackageManager manager, ApplicationInfo info) { 426 final ResolveInfo resolveInfo = manager.resolveActivity(info.intent, 0); 427 if (resolveInfo == null) { 428 return null; 429 } 430 431 ComponentName componentName = new ComponentName( 432 resolveInfo.activityInfo.applicationInfo.packageName, 433 resolveInfo.activityInfo.name); 434 ApplicationInfo application = mAppInfoCache.get(componentName); 435 436 if (application == null) { 437 return resolveInfo.activityInfo.loadIcon(manager); 438 } 439 440 return application.icon; 441 } 442 makeAndCacheApplicationInfo(PackageManager manager, HashMap<ComponentName, ApplicationInfo> appInfoCache, ResolveInfo info, Context context)443 private static ApplicationInfo makeAndCacheApplicationInfo(PackageManager manager, 444 HashMap<ComponentName, ApplicationInfo> appInfoCache, ResolveInfo info, 445 Context context) { 446 447 ComponentName componentName = new ComponentName( 448 info.activityInfo.applicationInfo.packageName, 449 info.activityInfo.name); 450 ApplicationInfo application = appInfoCache.get(componentName); 451 452 if (application == null) { 453 application = new ApplicationInfo(); 454 application.container = ItemInfo.NO_ID; 455 456 updateApplicationInfoTitleAndIcon(manager, info, application, context); 457 458 application.setActivity(componentName, 459 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 460 461 appInfoCache.put(componentName, application); 462 } 463 464 return application; 465 } 466 updateApplicationInfoTitleAndIcon(PackageManager manager, ResolveInfo info, ApplicationInfo application, Context context)467 private static void updateApplicationInfoTitleAndIcon(PackageManager manager, ResolveInfo info, 468 ApplicationInfo application, Context context) { 469 470 application.title = info.loadLabel(manager); 471 if (application.title == null) { 472 application.title = info.activityInfo.name; 473 } 474 475 application.icon = 476 Utilities.createIconThumbnail(info.activityInfo.loadIcon(manager), context); 477 application.filtered = false; 478 } 479 480 private static final AtomicInteger sAppsLoaderCount = new AtomicInteger(1); 481 private static final AtomicInteger sWorkspaceLoaderCount = new AtomicInteger(1); 482 483 private class ApplicationsLoader implements Runnable { 484 private final WeakReference<Launcher> mLauncher; 485 486 private volatile boolean mStopped; 487 private volatile boolean mRunning; 488 private final boolean mIsLaunching; 489 private final int mId; 490 ApplicationsLoader(Launcher launcher, boolean isLaunching)491 ApplicationsLoader(Launcher launcher, boolean isLaunching) { 492 mIsLaunching = isLaunching; 493 mLauncher = new WeakReference<Launcher>(launcher); 494 mRunning = true; 495 mId = sAppsLoaderCount.getAndIncrement(); 496 } 497 stop()498 void stop() { 499 mStopped = true; 500 } 501 isRunning()502 boolean isRunning() { 503 return mRunning; 504 } 505 run()506 public void run() { 507 if (DEBUG_LOADERS) d(LOG_TAG, " ----> running applications loader (" + mId + ")"); 508 509 // Elevate priority when Home launches for the first time to avoid 510 // starving at boot time. Staring at a blank home is not cool. 511 android.os.Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT : 512 Process.THREAD_PRIORITY_BACKGROUND); 513 514 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 515 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 516 517 final Launcher launcher = mLauncher.get(); 518 final PackageManager manager = launcher.getPackageManager(); 519 final List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0); 520 521 if (apps != null && !mStopped) { 522 final int count = apps.size(); 523 // Can be set to null on the UI thread by the unbind() method 524 // Do not access without checking for null first 525 final ApplicationsAdapter applicationList = mApplicationsAdapter; 526 527 ChangeNotifier action = new ChangeNotifier(applicationList, true); 528 final HashMap<ComponentName, ApplicationInfo> appInfoCache = mAppInfoCache; 529 530 for (int i = 0; i < count && !mStopped; i++) { 531 ResolveInfo info = apps.get(i); 532 ApplicationInfo application = 533 makeAndCacheApplicationInfo(manager, appInfoCache, info, launcher); 534 535 if (action.add(application) && !mStopped) { 536 launcher.runOnUiThread(action); 537 action = new ChangeNotifier(applicationList, false); 538 } 539 } 540 541 launcher.runOnUiThread(action); 542 } 543 544 if (!mStopped) { 545 mApplicationsLoaded = true; 546 } else { 547 if (DEBUG_LOADERS) d(LOG_TAG, " ----> applications loader stopped (" + mId + ")"); 548 } 549 mRunning = false; 550 } 551 } 552 553 private static class ChangeNotifier implements Runnable { 554 private final ApplicationsAdapter mApplicationList; 555 private final ArrayList<ApplicationInfo> mBuffer; 556 557 private boolean mFirst = true; 558 ChangeNotifier(ApplicationsAdapter applicationList, boolean first)559 ChangeNotifier(ApplicationsAdapter applicationList, boolean first) { 560 mApplicationList = applicationList; 561 mFirst = first; 562 mBuffer = new ArrayList<ApplicationInfo>(UI_NOTIFICATION_RATE); 563 } 564 run()565 public void run() { 566 final ApplicationsAdapter applicationList = mApplicationList; 567 // Can be set to null on the UI thread by the unbind() method 568 if (applicationList == null) return; 569 570 if (mFirst) { 571 applicationList.setNotifyOnChange(false); 572 applicationList.clear(); 573 if (DEBUG_LOADERS) d(LOG_TAG, " ----> cleared application list"); 574 mFirst = false; 575 } 576 577 final ArrayList<ApplicationInfo> buffer = mBuffer; 578 final int count = buffer.size(); 579 580 for (int i = 0; i < count; i++) { 581 applicationList.setNotifyOnChange(false); 582 applicationList.add(buffer.get(i)); 583 } 584 585 buffer.clear(); 586 587 applicationList.sort(new ApplicationInfoComparator()); 588 applicationList.notifyDataSetChanged(); 589 } 590 add(ApplicationInfo application)591 boolean add(ApplicationInfo application) { 592 final ArrayList<ApplicationInfo> buffer = mBuffer; 593 buffer.add(application); 594 return buffer.size() >= UI_NOTIFICATION_RATE; 595 } 596 } 597 598 static class ApplicationInfoComparator implements Comparator<ApplicationInfo> { compare(ApplicationInfo a, ApplicationInfo b)599 public final int compare(ApplicationInfo a, ApplicationInfo b) { 600 return sCollator.compare(a.title.toString(), b.title.toString()); 601 } 602 } 603 isDesktopLoaded()604 boolean isDesktopLoaded() { 605 return mDesktopItems != null && mDesktopAppWidgets != null && mDesktopItemsLoaded; 606 } 607 608 /** 609 * Loads all of the items on the desktop, in folders, or in the dock. 610 * These can be apps, shortcuts or widgets 611 */ loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged, boolean loadApplications)612 void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged, 613 boolean loadApplications) { 614 if (DEBUG_LOADERS) d(LOG_TAG, "loading user items"); 615 616 if (isLaunching && isDesktopLoaded()) { 617 if (DEBUG_LOADERS) d(LOG_TAG, " --> items loaded, return"); 618 if (loadApplications) startApplicationsLoader(launcher, true); 619 // We have already loaded our data from the DB 620 launcher.onDesktopItemsLoaded(mDesktopItems, mDesktopAppWidgets); 621 return; 622 } 623 624 if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) { 625 if (DEBUG_LOADERS) d(LOG_TAG, " --> stopping workspace loader"); 626 mDesktopItemsLoader.stop(); 627 // Wait for the currently running thread to finish, this can take a little 628 // time but it should be well below the timeout limit 629 try { 630 mDesktopLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT); 631 } catch (InterruptedException e) { 632 // Empty 633 } 634 635 // If the thread we are interrupting was tasked to load the list of 636 // applications make sure we keep that information in the new loader 637 // spawned below 638 // note: we don't apply this to localeChanged because the thread can 639 // only be stopped *after* the localeChanged handling has occured 640 loadApplications = mDesktopItemsLoader.mLoadApplications; 641 } 642 643 if (DEBUG_LOADERS) d(LOG_TAG, " --> starting workspace loader"); 644 mDesktopItemsLoaded = false; 645 mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications, 646 isLaunching); 647 mDesktopLoaderThread = new Thread(mDesktopItemsLoader, "Desktop Items Loader"); 648 mDesktopLoaderThread.start(); 649 } 650 updateShortcutLabels(ContentResolver resolver, PackageManager manager)651 private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) { 652 final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI, 653 new String[] { LauncherSettings.Favorites._ID, LauncherSettings.Favorites.TITLE, 654 LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE }, 655 null, null, null); 656 657 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); 658 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT); 659 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); 660 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE); 661 662 // boolean changed = false; 663 664 try { 665 while (c.moveToNext()) { 666 try { 667 if (c.getInt(itemTypeIndex) != 668 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { 669 continue; 670 } 671 672 final String intentUri = c.getString(intentIndex); 673 if (intentUri != null) { 674 final Intent shortcut = Intent.parseUri(intentUri, 0); 675 if (Intent.ACTION_MAIN.equals(shortcut.getAction())) { 676 final ComponentName name = shortcut.getComponent(); 677 if (name != null) { 678 final ActivityInfo activityInfo = manager.getActivityInfo(name, 0); 679 final String title = c.getString(titleIndex); 680 String label = getLabel(manager, activityInfo); 681 682 if (title == null || !title.equals(label)) { 683 final ContentValues values = new ContentValues(); 684 values.put(LauncherSettings.Favorites.TITLE, label); 685 686 resolver.update( 687 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, 688 values, "_id=?", 689 new String[] { String.valueOf(c.getLong(idIndex)) }); 690 691 // changed = true; 692 } 693 } 694 } 695 } 696 } catch (URISyntaxException e) { 697 // Ignore 698 } catch (PackageManager.NameNotFoundException e) { 699 // Ignore 700 } 701 } 702 } finally { 703 c.close(); 704 } 705 706 // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null); 707 } 708 getLabel(PackageManager manager, ActivityInfo activityInfo)709 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) { 710 String label = activityInfo.loadLabel(manager).toString(); 711 if (label == null) { 712 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString(); 713 if (label == null) { 714 label = activityInfo.name; 715 } 716 } 717 return label; 718 } 719 720 private class DesktopItemsLoader implements Runnable { 721 private volatile boolean mStopped; 722 private volatile boolean mRunning; 723 724 private final WeakReference<Launcher> mLauncher; 725 private final boolean mLocaleChanged; 726 private final boolean mLoadApplications; 727 private final boolean mIsLaunching; 728 private final int mId; 729 DesktopItemsLoader(Launcher launcher, boolean localeChanged, boolean loadApplications, boolean isLaunching)730 DesktopItemsLoader(Launcher launcher, boolean localeChanged, boolean loadApplications, 731 boolean isLaunching) { 732 mLoadApplications = loadApplications; 733 mIsLaunching = isLaunching; 734 mLauncher = new WeakReference<Launcher>(launcher); 735 mLocaleChanged = localeChanged; 736 mId = sWorkspaceLoaderCount.getAndIncrement(); 737 } 738 stop()739 void stop() { 740 mStopped = true; 741 } 742 isRunning()743 boolean isRunning() { 744 return mRunning; 745 } 746 run()747 public void run() { 748 if (DEBUG_LOADERS) d(LOG_TAG, " ----> running workspace loader (" + mId + ")"); 749 750 mRunning = true; 751 752 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); 753 754 final Launcher launcher = mLauncher.get(); 755 final ContentResolver contentResolver = launcher.getContentResolver(); 756 final PackageManager manager = launcher.getPackageManager(); 757 758 if (mLocaleChanged) { 759 updateShortcutLabels(contentResolver, manager); 760 } 761 762 mDesktopItems = new ArrayList<ItemInfo>(); 763 mDesktopAppWidgets = new ArrayList<LauncherAppWidgetInfo>(); 764 mFolders = new HashMap<Long, FolderInfo>(); 765 766 final ArrayList<ItemInfo> desktopItems = mDesktopItems; 767 final ArrayList<LauncherAppWidgetInfo> desktopAppWidgets = mDesktopAppWidgets; 768 769 final Cursor c = contentResolver.query( 770 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); 771 772 try { 773 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); 774 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT); 775 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE); 776 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE); 777 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON); 778 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE); 779 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE); 780 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); 781 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); 782 final int appWidgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.APPWIDGET_ID); 783 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); 784 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); 785 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); 786 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX); 787 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY); 788 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); 789 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE); 790 791 ApplicationInfo info; 792 String intentDescription; 793 Widget widgetInfo; 794 LauncherAppWidgetInfo appWidgetInfo; 795 int container; 796 long id; 797 Intent intent; 798 799 final HashMap<Long, FolderInfo> folders = mFolders; 800 801 while (!mStopped && c.moveToNext()) { 802 try { 803 int itemType = c.getInt(itemTypeIndex); 804 805 switch (itemType) { 806 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 807 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 808 intentDescription = c.getString(intentIndex); 809 try { 810 intent = Intent.parseUri(intentDescription, 0); 811 } catch (java.net.URISyntaxException e) { 812 continue; 813 } 814 815 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { 816 info = getApplicationInfo(manager, intent, launcher); 817 } else { 818 info = getApplicationInfoShortcut(c, launcher, iconTypeIndex, 819 iconPackageIndex, iconResourceIndex, iconIndex); 820 } 821 822 if (info == null) { 823 info = new ApplicationInfo(); 824 info.icon = manager.getDefaultActivityIcon(); 825 } 826 827 if (info != null) { 828 info.title = c.getString(titleIndex); 829 info.intent = intent; 830 831 info.id = c.getLong(idIndex); 832 container = c.getInt(containerIndex); 833 info.container = container; 834 info.screen = c.getInt(screenIndex); 835 info.cellX = c.getInt(cellXIndex); 836 info.cellY = c.getInt(cellYIndex); 837 838 switch (container) { 839 case LauncherSettings.Favorites.CONTAINER_DESKTOP: 840 desktopItems.add(info); 841 break; 842 default: 843 // Item is in a user folder 844 UserFolderInfo folderInfo = 845 findOrMakeUserFolder(folders, container); 846 folderInfo.add(info); 847 break; 848 } 849 } 850 break; 851 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 852 853 id = c.getLong(idIndex); 854 UserFolderInfo folderInfo = findOrMakeUserFolder(folders, id); 855 856 folderInfo.title = c.getString(titleIndex); 857 858 folderInfo.id = id; 859 container = c.getInt(containerIndex); 860 folderInfo.container = container; 861 folderInfo.screen = c.getInt(screenIndex); 862 folderInfo.cellX = c.getInt(cellXIndex); 863 folderInfo.cellY = c.getInt(cellYIndex); 864 865 switch (container) { 866 case LauncherSettings.Favorites.CONTAINER_DESKTOP: 867 desktopItems.add(folderInfo); 868 break; 869 } 870 break; 871 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: 872 873 id = c.getLong(idIndex); 874 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(folders, id); 875 876 intentDescription = c.getString(intentIndex); 877 intent = null; 878 if (intentDescription != null) { 879 try { 880 intent = Intent.parseUri(intentDescription, 0); 881 } catch (java.net.URISyntaxException e) { 882 // Ignore, a live folder might not have a base intent 883 } 884 } 885 886 liveFolderInfo.title = c.getString(titleIndex); 887 liveFolderInfo.id = id; 888 container = c.getInt(containerIndex); 889 liveFolderInfo.container = container; 890 liveFolderInfo.screen = c.getInt(screenIndex); 891 liveFolderInfo.cellX = c.getInt(cellXIndex); 892 liveFolderInfo.cellY = c.getInt(cellYIndex); 893 liveFolderInfo.uri = Uri.parse(c.getString(uriIndex)); 894 liveFolderInfo.baseIntent = intent; 895 liveFolderInfo.displayMode = c.getInt(displayModeIndex); 896 897 loadLiveFolderIcon(launcher, c, iconTypeIndex, iconPackageIndex, 898 iconResourceIndex, liveFolderInfo); 899 900 switch (container) { 901 case LauncherSettings.Favorites.CONTAINER_DESKTOP: 902 desktopItems.add(liveFolderInfo); 903 break; 904 } 905 break; 906 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH: 907 widgetInfo = Widget.makeSearch(); 908 909 container = c.getInt(containerIndex); 910 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { 911 e(Launcher.LOG_TAG, "Widget found where container " 912 + "!= CONTAINER_DESKTOP ignoring!"); 913 continue; 914 } 915 916 widgetInfo.id = c.getLong(idIndex); 917 widgetInfo.screen = c.getInt(screenIndex); 918 widgetInfo.container = container; 919 widgetInfo.cellX = c.getInt(cellXIndex); 920 widgetInfo.cellY = c.getInt(cellYIndex); 921 922 desktopItems.add(widgetInfo); 923 break; 924 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 925 // Read all Launcher-specific widget details 926 int appWidgetId = c.getInt(appWidgetIdIndex); 927 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId); 928 appWidgetInfo.id = c.getLong(idIndex); 929 appWidgetInfo.screen = c.getInt(screenIndex); 930 appWidgetInfo.cellX = c.getInt(cellXIndex); 931 appWidgetInfo.cellY = c.getInt(cellYIndex); 932 appWidgetInfo.spanX = c.getInt(spanXIndex); 933 appWidgetInfo.spanY = c.getInt(spanYIndex); 934 935 container = c.getInt(containerIndex); 936 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { 937 e(Launcher.LOG_TAG, "Widget found where container " 938 + "!= CONTAINER_DESKTOP -- ignoring!"); 939 continue; 940 } 941 appWidgetInfo.container = c.getInt(containerIndex); 942 943 desktopAppWidgets.add(appWidgetInfo); 944 break; 945 } 946 } catch (Exception e) { 947 w(Launcher.LOG_TAG, "Desktop items loading interrupted:", e); 948 } 949 } 950 } finally { 951 c.close(); 952 } 953 954 if (!mStopped) { 955 if (DEBUG_LOADERS) { 956 d(LOG_TAG, " --> done loading workspace"); 957 d(LOG_TAG, " ----> worskpace items=" + desktopItems.size()); 958 d(LOG_TAG, " ----> worskpace widgets=" + desktopAppWidgets.size()); 959 } 960 961 // Create a copy of the lists in case the workspace loader is restarted 962 // and the list are cleared before the UI can go through them 963 final ArrayList<ItemInfo> uiDesktopItems = 964 new ArrayList<ItemInfo>(desktopItems); 965 final ArrayList<LauncherAppWidgetInfo> uiDesktopWidgets = 966 new ArrayList<LauncherAppWidgetInfo>(desktopAppWidgets); 967 968 if (!mStopped) { 969 d(LOG_TAG, " ----> items cloned, ready to refresh UI"); 970 launcher.runOnUiThread(new Runnable() { 971 public void run() { 972 if (DEBUG_LOADERS) d(LOG_TAG, " ----> onDesktopItemsLoaded()"); 973 launcher.onDesktopItemsLoaded(uiDesktopItems, uiDesktopWidgets); 974 } 975 }); 976 } 977 978 if (mLoadApplications) { 979 if (DEBUG_LOADERS) { 980 d(LOG_TAG, " ----> loading applications from workspace loader"); 981 } 982 startApplicationsLoader(launcher, mIsLaunching); 983 } 984 985 mDesktopItemsLoaded = true; 986 } else { 987 if (DEBUG_LOADERS) d(LOG_TAG, " ----> worskpace loader was stopped"); 988 } 989 mRunning = false; 990 } 991 } 992 loadLiveFolderIcon(Launcher launcher, Cursor c, int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo)993 private static void loadLiveFolderIcon(Launcher launcher, Cursor c, int iconTypeIndex, 994 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) { 995 996 int iconType = c.getInt(iconTypeIndex); 997 switch (iconType) { 998 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE: 999 String packageName = c.getString(iconPackageIndex); 1000 String resourceName = c.getString(iconResourceIndex); 1001 PackageManager packageManager = launcher.getPackageManager(); 1002 try { 1003 Resources resources = packageManager.getResourcesForApplication(packageName); 1004 final int id = resources.getIdentifier(resourceName, null, null); 1005 liveFolderInfo.icon = resources.getDrawable(id); 1006 } catch (Exception e) { 1007 liveFolderInfo.icon = 1008 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder); 1009 } 1010 liveFolderInfo.iconResource = new Intent.ShortcutIconResource(); 1011 liveFolderInfo.iconResource.packageName = packageName; 1012 liveFolderInfo.iconResource.resourceName = resourceName; 1013 break; 1014 default: 1015 liveFolderInfo.icon = 1016 launcher.getResources().getDrawable(R.drawable.ic_launcher_folder); 1017 } 1018 } 1019 1020 /** 1021 * Finds the user folder defined by the specified id. 1022 * 1023 * @param id The id of the folder to look for. 1024 * 1025 * @return A UserFolderInfo if the folder exists or null otherwise. 1026 */ findFolderById(long id)1027 FolderInfo findFolderById(long id) { 1028 return mFolders.get(id); 1029 } 1030 addFolder(FolderInfo info)1031 void addFolder(FolderInfo info) { 1032 mFolders.put(info.id, info); 1033 } 1034 1035 /** 1036 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a 1037 * new one. 1038 */ findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id)1039 private UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) { 1040 // See if a placeholder was created for us already 1041 FolderInfo folderInfo = folders.get(id); 1042 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) { 1043 // No placeholder -- create a new instance 1044 folderInfo = new UserFolderInfo(); 1045 folders.put(id, folderInfo); 1046 } 1047 return (UserFolderInfo) folderInfo; 1048 } 1049 1050 /** 1051 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a 1052 * new one. 1053 */ findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id)1054 private LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) { 1055 // See if a placeholder was created for us already 1056 FolderInfo folderInfo = folders.get(id); 1057 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) { 1058 // No placeholder -- create a new instance 1059 folderInfo = new LiveFolderInfo(); 1060 folders.put(id, folderInfo); 1061 } 1062 return (LiveFolderInfo) folderInfo; 1063 } 1064 1065 /** 1066 * Remove the callback for the cached drawables or we leak the previous 1067 * Home screen on orientation change. 1068 */ unbind()1069 void unbind() { 1070 // Interrupt the applications loader before setting the adapter to null 1071 stopAndWaitForApplicationsLoader(); 1072 mApplicationsAdapter = null; 1073 unbindAppDrawables(mApplications); 1074 unbindDrawables(mDesktopItems); 1075 unbindAppWidgetHostViews(mDesktopAppWidgets); 1076 unbindCachedIconDrawables(); 1077 } 1078 1079 /** 1080 * Remove the callback for the cached drawables or we leak the previous 1081 * Home screen on orientation change. 1082 */ unbindDrawables(ArrayList<ItemInfo> desktopItems)1083 private void unbindDrawables(ArrayList<ItemInfo> desktopItems) { 1084 if (desktopItems != null) { 1085 final int count = desktopItems.size(); 1086 for (int i = 0; i < count; i++) { 1087 ItemInfo item = desktopItems.get(i); 1088 switch (item.itemType) { 1089 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1090 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1091 ((ApplicationInfo)item).icon.setCallback(null); 1092 break; 1093 } 1094 } 1095 } 1096 } 1097 1098 /** 1099 * Remove the callback for the cached drawables or we leak the previous 1100 * Home screen on orientation change. 1101 */ unbindAppDrawables(ArrayList<ApplicationInfo> applications)1102 private void unbindAppDrawables(ArrayList<ApplicationInfo> applications) { 1103 if (applications != null) { 1104 final int count = applications.size(); 1105 for (int i = 0; i < count; i++) { 1106 applications.get(i).icon.setCallback(null); 1107 } 1108 } 1109 } 1110 1111 /** 1112 * Remove any {@link LauncherAppWidgetHostView} references in our widgets. 1113 */ unbindAppWidgetHostViews(ArrayList<LauncherAppWidgetInfo> appWidgets)1114 private void unbindAppWidgetHostViews(ArrayList<LauncherAppWidgetInfo> appWidgets) { 1115 if (appWidgets != null) { 1116 final int count = appWidgets.size(); 1117 for (int i = 0; i < count; i++) { 1118 LauncherAppWidgetInfo launcherInfo = appWidgets.get(i); 1119 launcherInfo.hostView = null; 1120 } 1121 } 1122 } 1123 1124 /** 1125 * Remove the callback for the cached drawables or we leak the previous 1126 * Home screen on orientation change. 1127 */ unbindCachedIconDrawables()1128 private void unbindCachedIconDrawables() { 1129 for (ApplicationInfo appInfo : mAppInfoCache.values()) { 1130 appInfo.icon.setCallback(null); 1131 } 1132 } 1133 1134 /** 1135 * @return The current list of applications 1136 */ getApplicationsAdapter()1137 ApplicationsAdapter getApplicationsAdapter() { 1138 return mApplicationsAdapter; 1139 } 1140 1141 /** 1142 * @return The current list of desktop items 1143 */ getDesktopItems()1144 ArrayList<ItemInfo> getDesktopItems() { 1145 return mDesktopItems; 1146 } 1147 1148 /** 1149 * @return The current list of desktop items 1150 */ getDesktopAppWidgets()1151 ArrayList<LauncherAppWidgetInfo> getDesktopAppWidgets() { 1152 return mDesktopAppWidgets; 1153 } 1154 1155 /** 1156 * Add an item to the desktop 1157 * @param info 1158 */ addDesktopItem(ItemInfo info)1159 void addDesktopItem(ItemInfo info) { 1160 // TODO: write to DB; also check that folder has been added to folders list 1161 mDesktopItems.add(info); 1162 } 1163 1164 /** 1165 * Remove an item from the desktop 1166 * @param info 1167 */ removeDesktopItem(ItemInfo info)1168 void removeDesktopItem(ItemInfo info) { 1169 // TODO: write to DB; figure out if we should remove folder from folders list 1170 mDesktopItems.remove(info); 1171 } 1172 1173 /** 1174 * Add a widget to the desktop 1175 */ addDesktopAppWidget(LauncherAppWidgetInfo info)1176 void addDesktopAppWidget(LauncherAppWidgetInfo info) { 1177 mDesktopAppWidgets.add(info); 1178 } 1179 1180 /** 1181 * Remove a widget from the desktop 1182 */ removeDesktopAppWidget(LauncherAppWidgetInfo info)1183 void removeDesktopAppWidget(LauncherAppWidgetInfo info) { 1184 mDesktopAppWidgets.remove(info); 1185 } 1186 1187 /** 1188 * Make an ApplicationInfo object for an application 1189 */ getApplicationInfo(PackageManager manager, Intent intent, Context context)1190 private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent, 1191 Context context) { 1192 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0); 1193 1194 if (resolveInfo == null) { 1195 return null; 1196 } 1197 1198 final ApplicationInfo info = new ApplicationInfo(); 1199 final ActivityInfo activityInfo = resolveInfo.activityInfo; 1200 info.icon = Utilities.createIconThumbnail(activityInfo.loadIcon(manager), context); 1201 if (info.title == null || info.title.length() == 0) { 1202 info.title = activityInfo.loadLabel(manager); 1203 } 1204 if (info.title == null) { 1205 info.title = ""; 1206 } 1207 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; 1208 return info; 1209 } 1210 1211 /** 1212 * Make an ApplicationInfo object for a sortcut 1213 */ getApplicationInfoShortcut(Cursor c, Context context, int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex)1214 private ApplicationInfo getApplicationInfoShortcut(Cursor c, Context context, 1215 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) { 1216 1217 final ApplicationInfo info = new ApplicationInfo(); 1218 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; 1219 1220 int iconType = c.getInt(iconTypeIndex); 1221 switch (iconType) { 1222 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE: 1223 String packageName = c.getString(iconPackageIndex); 1224 String resourceName = c.getString(iconResourceIndex); 1225 PackageManager packageManager = context.getPackageManager(); 1226 try { 1227 Resources resources = packageManager.getResourcesForApplication(packageName); 1228 final int id = resources.getIdentifier(resourceName, null, null); 1229 info.icon = Utilities.createIconThumbnail(resources.getDrawable(id), context); 1230 } catch (Exception e) { 1231 info.icon = packageManager.getDefaultActivityIcon(); 1232 } 1233 info.iconResource = new Intent.ShortcutIconResource(); 1234 info.iconResource.packageName = packageName; 1235 info.iconResource.resourceName = resourceName; 1236 info.customIcon = false; 1237 break; 1238 case LauncherSettings.Favorites.ICON_TYPE_BITMAP: 1239 byte[] data = c.getBlob(iconIndex); 1240 try { 1241 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); 1242 info.icon = new FastBitmapDrawable( 1243 Utilities.createBitmapThumbnail(bitmap, context)); 1244 } catch (Exception e) { 1245 packageManager = context.getPackageManager(); 1246 info.icon = packageManager.getDefaultActivityIcon(); 1247 } 1248 info.filtered = true; 1249 info.customIcon = true; 1250 break; 1251 default: 1252 info.icon = context.getPackageManager().getDefaultActivityIcon(); 1253 info.customIcon = false; 1254 break; 1255 } 1256 return info; 1257 } 1258 1259 /** 1260 * Remove an item from the in-memory represention of a user folder. Does not change the DB. 1261 */ removeUserFolderItem(UserFolderInfo folder, ItemInfo info)1262 void removeUserFolderItem(UserFolderInfo folder, ItemInfo info) { 1263 //noinspection SuspiciousMethodCalls 1264 folder.contents.remove(info); 1265 } 1266 1267 /** 1268 * Removes a UserFolder from the in-memory list of folders. Does not change the DB. 1269 * @param userFolderInfo 1270 */ removeUserFolder(UserFolderInfo userFolderInfo)1271 void removeUserFolder(UserFolderInfo userFolderInfo) { 1272 mFolders.remove(userFolderInfo.id); 1273 } 1274 1275 /** 1276 * Adds an item to the DB if it was not created previously, or move it to a new 1277 * <container, screen, cellX, cellY> 1278 */ addOrMoveItemInDatabase(Context context, ItemInfo item, long container, int screen, int cellX, int cellY)1279 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container, 1280 int screen, int cellX, int cellY) { 1281 if (item.container == ItemInfo.NO_ID) { 1282 // From all apps 1283 addItemToDatabase(context, item, container, screen, cellX, cellY, false); 1284 } else { 1285 // From somewhere else 1286 moveItemInDatabase(context, item, container, screen, cellX, cellY); 1287 } 1288 } 1289 1290 /** 1291 * Move an item in the DB to a new <container, screen, cellX, cellY> 1292 */ moveItemInDatabase(Context context, ItemInfo item, long container, int screen, int cellX, int cellY)1293 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen, 1294 int cellX, int cellY) { 1295 item.container = container; 1296 item.screen = screen; 1297 item.cellX = cellX; 1298 item.cellY = cellY; 1299 1300 final ContentValues values = new ContentValues(); 1301 final ContentResolver cr = context.getContentResolver(); 1302 1303 values.put(LauncherSettings.Favorites.CONTAINER, item.container); 1304 values.put(LauncherSettings.Favorites.CELLX, item.cellX); 1305 values.put(LauncherSettings.Favorites.CELLY, item.cellY); 1306 values.put(LauncherSettings.Favorites.SCREEN, item.screen); 1307 1308 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null); 1309 } 1310 1311 /** 1312 * Returns true if the shortcuts already exists in the database. 1313 * we identify a shortcut by its title and intent. 1314 */ shortcutExists(Context context, String title, Intent intent)1315 static boolean shortcutExists(Context context, String title, Intent intent) { 1316 final ContentResolver cr = context.getContentResolver(); 1317 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, 1318 new String[] { "title", "intent" }, "title=? and intent=?", 1319 new String[] { title, intent.toUri(0) }, null); 1320 boolean result = false; 1321 try { 1322 result = c.moveToFirst(); 1323 } finally { 1324 c.close(); 1325 } 1326 return result; 1327 } 1328 getFolderById(Context context, long id)1329 FolderInfo getFolderById(Context context, long id) { 1330 final ContentResolver cr = context.getContentResolver(); 1331 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null, 1332 "_id=? and (itemType=? or itemType=?)", 1333 new String[] { String.valueOf(id), 1334 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER), 1335 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null); 1336 1337 try { 1338 if (c.moveToFirst()) { 1339 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); 1340 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE); 1341 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); 1342 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); 1343 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); 1344 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); 1345 1346 FolderInfo folderInfo = null; 1347 switch (c.getInt(itemTypeIndex)) { 1348 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1349 folderInfo = findOrMakeUserFolder(mFolders, id); 1350 break; 1351 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: 1352 folderInfo = findOrMakeLiveFolder(mFolders, id); 1353 break; 1354 } 1355 1356 folderInfo.title = c.getString(titleIndex); 1357 folderInfo.id = id; 1358 folderInfo.container = c.getInt(containerIndex); 1359 folderInfo.screen = c.getInt(screenIndex); 1360 folderInfo.cellX = c.getInt(cellXIndex); 1361 folderInfo.cellY = c.getInt(cellYIndex); 1362 1363 return folderInfo; 1364 } 1365 } finally { 1366 c.close(); 1367 } 1368 1369 return null; 1370 } 1371 1372 /** 1373 * Add an item to the database in a specified container. Sets the container, screen, cellX and 1374 * cellY fields of the item. Also assigns an ID to the item. 1375 */ addItemToDatabase(Context context, ItemInfo item, long container, int screen, int cellX, int cellY, boolean notify)1376 static void addItemToDatabase(Context context, ItemInfo item, long container, 1377 int screen, int cellX, int cellY, boolean notify) { 1378 item.container = container; 1379 item.screen = screen; 1380 item.cellX = cellX; 1381 item.cellY = cellY; 1382 1383 final ContentValues values = new ContentValues(); 1384 final ContentResolver cr = context.getContentResolver(); 1385 1386 item.onAddToDatabase(values); 1387 1388 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI : 1389 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values); 1390 1391 if (result != null) { 1392 item.id = Integer.parseInt(result.getPathSegments().get(1)); 1393 } 1394 } 1395 1396 /** 1397 * Add an item to the database in a specified container. Sets the container, screen, cellX and 1398 * cellY fields of the item. Also assigns an ID to the item. 1399 */ addGestureToDatabase(Context context, ItemInfo item, boolean notify)1400 static boolean addGestureToDatabase(Context context, ItemInfo item, boolean notify) { 1401 final ContentValues values = new ContentValues(); 1402 final ContentResolver cr = context.getContentResolver(); 1403 1404 item.onAddToDatabase(values); 1405 1406 Uri result = cr.insert(notify ? LauncherSettings.Gestures.CONTENT_URI : 1407 LauncherSettings.Gestures.CONTENT_URI_NO_NOTIFICATION, values); 1408 1409 if (result != null) { 1410 item.id = Integer.parseInt(result.getPathSegments().get(1)); 1411 } 1412 1413 return result != null; 1414 } 1415 1416 /** 1417 * Update an item to the database in a specified container. 1418 */ updateItemInDatabase(Context context, ItemInfo item)1419 static void updateItemInDatabase(Context context, ItemInfo item) { 1420 final ContentValues values = new ContentValues(); 1421 final ContentResolver cr = context.getContentResolver(); 1422 1423 item.onAddToDatabase(values); 1424 1425 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null); 1426 } 1427 1428 /** 1429 * Removes the specified item from the database 1430 * @param context 1431 * @param item 1432 */ deleteItemFromDatabase(Context context, ItemInfo item)1433 static void deleteItemFromDatabase(Context context, ItemInfo item) { 1434 final ContentResolver cr = context.getContentResolver(); 1435 1436 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null); 1437 } 1438 1439 1440 /** 1441 * Remove the contents of the specified folder from the database 1442 */ deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info)1443 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) { 1444 final ContentResolver cr = context.getContentResolver(); 1445 1446 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null); 1447 cr.delete(LauncherSettings.Favorites.CONTENT_URI, 1448 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null); 1449 } 1450 deleteGestureFromDatabase(Context context, ItemInfo item)1451 static void deleteGestureFromDatabase(Context context, ItemInfo item) { 1452 final ContentResolver cr = context.getContentResolver(); 1453 1454 cr.delete(LauncherSettings.Gestures.getContentUri(item.id, false), null, null); 1455 } 1456 updateGestureInDatabase(Context context, ItemInfo item)1457 static void updateGestureInDatabase(Context context, ItemInfo item) { 1458 final ContentValues values = new ContentValues(); 1459 final ContentResolver cr = context.getContentResolver(); 1460 1461 item.onAddToDatabase(values); 1462 1463 cr.update(LauncherSettings.Gestures.getContentUri(item.id, false), values, null, null); 1464 } 1465 1466 queryGesture(Context context, String id)1467 ApplicationInfo queryGesture(Context context, String id) { 1468 final ContentResolver contentResolver = context.getContentResolver(); 1469 final PackageManager manager = context.getPackageManager(); 1470 final Cursor c = contentResolver.query( 1471 LauncherSettings.Gestures.CONTENT_URI, null, LauncherSettings.Gestures._ID + "=?", 1472 new String[] { id }, null); 1473 1474 ApplicationInfo info = null; 1475 1476 try { 1477 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures._ID); 1478 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.INTENT); 1479 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.TITLE); 1480 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ICON_TYPE); 1481 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ICON); 1482 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ICON_PACKAGE); 1483 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ICON_RESOURCE); 1484 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Gestures.ITEM_TYPE); 1485 1486 String intentDescription; 1487 Intent intent; 1488 1489 if (c.moveToNext()) { 1490 int itemType = c.getInt(itemTypeIndex); 1491 1492 switch (itemType) { 1493 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1494 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1495 intentDescription = c.getString(intentIndex); 1496 try { 1497 intent = Intent.parseUri(intentDescription, 0); 1498 } catch (java.net.URISyntaxException e) { 1499 return null; 1500 } 1501 1502 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { 1503 info = getApplicationInfo(manager, intent, context); 1504 } else { 1505 info = getApplicationInfoShortcut(c, context, iconTypeIndex, 1506 iconPackageIndex, iconResourceIndex, iconIndex); 1507 } 1508 1509 if (info == null) { 1510 info = new ApplicationInfo(); 1511 info.icon = manager.getDefaultActivityIcon(); 1512 } 1513 1514 info.isGesture = true; 1515 info.title = c.getString(titleIndex); 1516 info.intent = intent; 1517 info.id = c.getLong(idIndex); 1518 1519 break; 1520 } 1521 } 1522 } catch (Exception e) { 1523 w(LOG_TAG, "Could not load gesture with name " + id); 1524 } finally { 1525 c.close(); 1526 } 1527 1528 return info; 1529 } 1530 } 1531