1 /* 2 * Copyright (C) 2014 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 android.content.pm; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.TestApi; 23 import android.content.ActivityNotFoundException; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.PackageManager.ApplicationInfoFlags; 28 import android.content.pm.PackageManager.NameNotFoundException; 29 import android.content.res.Resources; 30 import android.graphics.Bitmap; 31 import android.graphics.BitmapFactory; 32 import android.graphics.Rect; 33 import android.graphics.drawable.BitmapDrawable; 34 import android.graphics.drawable.Drawable; 35 import android.os.Bundle; 36 import android.os.Handler; 37 import android.os.Looper; 38 import android.os.Message; 39 import android.os.ParcelFileDescriptor; 40 import android.os.RemoteException; 41 import android.os.ServiceManager; 42 import android.os.UserHandle; 43 import android.os.UserManager; 44 import android.util.DisplayMetrics; 45 import android.util.Log; 46 47 import java.io.IOException; 48 import java.lang.annotation.Retention; 49 import java.lang.annotation.RetentionPolicy; 50 import java.util.ArrayList; 51 import java.util.Arrays; 52 import java.util.Collections; 53 import java.util.List; 54 55 /** 56 * Class for retrieving a list of launchable activities for the current user and any associated 57 * managed profiles. This is mainly for use by launchers. Apps can be queried for each user profile. 58 * Since the PackageManager will not deliver package broadcasts for other profiles, you can register 59 * for package changes here. 60 * <p> 61 * To watch for managed profiles being added or removed, register for the following broadcasts: 62 * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}. 63 * <p> 64 * You can retrieve the list of profiles associated with this user with 65 * {@link UserManager#getUserProfiles()}. 66 */ 67 public class LauncherApps { 68 69 static final String TAG = "LauncherApps"; 70 static final boolean DEBUG = false; 71 72 private Context mContext; 73 private ILauncherApps mService; 74 private PackageManager mPm; 75 76 private List<CallbackMessageHandler> mCallbacks 77 = new ArrayList<CallbackMessageHandler>(); 78 79 /** 80 * Callbacks for package changes to this and related managed profiles. 81 */ 82 public static abstract class Callback { 83 /** 84 * Indicates that a package was removed from the specified profile. 85 * 86 * If a package is removed while being updated onPackageChanged will be 87 * called instead. 88 * 89 * @param packageName The name of the package that was removed. 90 * @param user The UserHandle of the profile that generated the change. 91 */ 92 abstract public void onPackageRemoved(String packageName, UserHandle user); 93 94 /** 95 * Indicates that a package was added to the specified profile. 96 * 97 * If a package is added while being updated then onPackageChanged will be 98 * called instead. 99 * 100 * @param packageName The name of the package that was added. 101 * @param user The UserHandle of the profile that generated the change. 102 */ 103 abstract public void onPackageAdded(String packageName, UserHandle user); 104 105 /** 106 * Indicates that a package was modified in the specified profile. 107 * This can happen, for example, when the package is updated or when 108 * one or more components are enabled or disabled. 109 * 110 * @param packageName The name of the package that has changed. 111 * @param user The UserHandle of the profile that generated the change. 112 */ 113 abstract public void onPackageChanged(String packageName, UserHandle user); 114 115 /** 116 * Indicates that one or more packages have become available. For 117 * example, this can happen when a removable storage card has 118 * reappeared. 119 * 120 * @param packageNames The names of the packages that have become 121 * available. 122 * @param user The UserHandle of the profile that generated the change. 123 * @param replacing Indicates whether these packages are replacing 124 * existing ones. 125 */ 126 abstract public void onPackagesAvailable(String[] packageNames, UserHandle user, 127 boolean replacing); 128 129 /** 130 * Indicates that one or more packages have become unavailable. For 131 * example, this can happen when a removable storage card has been 132 * removed. 133 * 134 * @param packageNames The names of the packages that have become 135 * unavailable. 136 * @param user The UserHandle of the profile that generated the change. 137 * @param replacing Indicates whether the packages are about to be 138 * replaced with new versions. 139 */ 140 abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user, 141 boolean replacing); 142 143 /** 144 * Indicates that one or more packages have been suspended. For 145 * example, this can happen when a Device Administrator suspends 146 * an applicaton. 147 * 148 * @param packageNames The names of the packages that have just been 149 * suspended. 150 * @param user The UserHandle of the profile that generated the change. 151 */ 152 public void onPackagesSuspended(String[] packageNames, UserHandle user) { 153 } 154 155 /** 156 * Indicates that one or more packages have been unsuspended. For 157 * example, this can happen when a Device Administrator unsuspends 158 * an applicaton. 159 * 160 * @param packageNames The names of the packages that have just been 161 * unsuspended. 162 * @param user The UserHandle of the profile that generated the change. 163 */ 164 public void onPackagesUnsuspended(String[] packageNames, UserHandle user) { 165 } 166 167 /** 168 * Indicates that one or more shortcuts of any kind (dynamic, pinned, or manifest) 169 * have been added, updated or removed. 170 * 171 * <p>Only the applications that are allowed to access the shortcut information, 172 * as defined in {@link #hasShortcutHostPermission()}, will receive it. 173 * 174 * @param packageName The name of the package that has the shortcuts. 175 * @param shortcuts All shortcuts from the package (dynamic, manifest and/or pinned). 176 * Only "key" information will be provided, as defined in 177 * {@link ShortcutInfo#hasKeyFieldsOnly()}. 178 * @param user The UserHandle of the profile that generated the change. 179 * 180 * @see ShortcutManager 181 */ 182 public void onShortcutsChanged(@NonNull String packageName, 183 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { 184 } 185 } 186 187 /** 188 * Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}. 189 */ 190 public static class ShortcutQuery { 191 /** 192 * Include dynamic shortcuts in the result. 193 */ 194 public static final int FLAG_MATCH_DYNAMIC = 1 << 0; 195 196 /** @hide kept for unit tests */ 197 @Deprecated 198 public static final int FLAG_GET_DYNAMIC = FLAG_MATCH_DYNAMIC; 199 200 /** 201 * Include pinned shortcuts in the result. 202 */ 203 public static final int FLAG_MATCH_PINNED = 1 << 1; 204 205 /** @hide kept for unit tests */ 206 @Deprecated 207 public static final int FLAG_GET_PINNED = FLAG_MATCH_PINNED; 208 209 /** 210 * Include manifest shortcuts in the result. 211 */ 212 public static final int FLAG_MATCH_MANIFEST = 1 << 3; 213 214 /** @hide kept for unit tests */ 215 @Deprecated 216 public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST; 217 218 /** @hide */ 219 public static final int FLAG_MATCH_ALL_KINDS = 220 FLAG_GET_DYNAMIC | FLAG_GET_PINNED | FLAG_GET_MANIFEST; 221 222 /** @hide kept for unit tests */ 223 @Deprecated 224 public static final int FLAG_GET_ALL_KINDS = FLAG_MATCH_ALL_KINDS; 225 226 /** 227 * Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()}'s javadoc to 228 * see which fields fields "key". 229 * This allows quicker access to shortcut information in order to 230 * determine whether the caller's in-memory cache needs to be updated. 231 * 232 * <p>Typically, launcher applications cache all or most shortcut information 233 * in memory in order to show shortcuts without a delay. 234 * 235 * When a given launcher application wants to update its cache, such as when its process 236 * restarts, it can fetch shortcut information with this flag. 237 * The application can then check {@link ShortcutInfo#getLastChangedTimestamp()} for each 238 * shortcut, fetching a shortcut's non-key information only if that shortcut has been 239 * updated. 240 * 241 * @see ShortcutManager 242 */ 243 public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2; 244 245 /** @hide */ 246 @IntDef(flag = true, 247 value = { 248 FLAG_MATCH_DYNAMIC, 249 FLAG_MATCH_PINNED, 250 FLAG_MATCH_MANIFEST, 251 FLAG_GET_KEY_FIELDS_ONLY, 252 }) 253 @Retention(RetentionPolicy.SOURCE) 254 public @interface QueryFlags {} 255 256 long mChangedSince; 257 258 @Nullable 259 String mPackage; 260 261 @Nullable 262 List<String> mShortcutIds; 263 264 @Nullable 265 ComponentName mActivity; 266 267 @QueryFlags 268 int mQueryFlags; 269 270 public ShortcutQuery() { 271 } 272 273 /** 274 * If non-zero, returns only shortcuts that have been added or updated 275 * since the given timestamp, expressed in milliseconds since the Epoch—see 276 * {@link System#currentTimeMillis()}. 277 */ 278 public ShortcutQuery setChangedSince(long changedSince) { 279 mChangedSince = changedSince; 280 return this; 281 } 282 283 /** 284 * If non-null, returns only shortcuts from the package. 285 */ 286 public ShortcutQuery setPackage(@Nullable String packageName) { 287 mPackage = packageName; 288 return this; 289 } 290 291 /** 292 * If non-null, return only the specified shortcuts by ID. When setting this field, 293 * a package name must also be set with {@link #setPackage}. 294 */ 295 public ShortcutQuery setShortcutIds(@Nullable List<String> shortcutIds) { 296 mShortcutIds = shortcutIds; 297 return this; 298 } 299 300 /** 301 * If non-null, returns only shortcuts associated with the activity; i.e. 302 * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal 303 * to {@code activity}. 304 */ 305 public ShortcutQuery setActivity(@Nullable ComponentName activity) { 306 mActivity = activity; 307 return this; 308 } 309 310 /** 311 * Set query options. At least one of the {@code MATCH} flags should be set. Otherwise, 312 * no shortcuts will be returned. 313 * 314 * <ul> 315 * <li>{@link #FLAG_MATCH_DYNAMIC} 316 * <li>{@link #FLAG_MATCH_PINNED} 317 * <li>{@link #FLAG_MATCH_MANIFEST} 318 * <li>{@link #FLAG_GET_KEY_FIELDS_ONLY} 319 * </ul> 320 */ 321 public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) { 322 mQueryFlags = queryFlags; 323 return this; 324 } 325 } 326 327 /** @hide */ 328 public LauncherApps(Context context, ILauncherApps service) { 329 mContext = context; 330 mService = service; 331 mPm = context.getPackageManager(); 332 } 333 334 /** @hide */ 335 @TestApi 336 public LauncherApps(Context context) { 337 this(context, ILauncherApps.Stub.asInterface( 338 ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE))); 339 } 340 341 /** 342 * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and 343 * {@link Intent#CATEGORY_LAUNCHER}, for a specified user. 344 * 345 * @param packageName The specific package to query. If null, it checks all installed packages 346 * in the profile. 347 * @param user The UserHandle of the profile. 348 * @return List of launchable activities. Can be an empty list but will not be null. 349 */ 350 public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) { 351 ParceledListSlice<ResolveInfo> activities = null; 352 try { 353 activities = mService.getLauncherActivities(packageName, user); 354 } catch (RemoteException re) { 355 throw re.rethrowFromSystemServer(); 356 } 357 if (activities == null) { 358 return Collections.EMPTY_LIST; 359 } 360 ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>(); 361 for (ResolveInfo ri : activities.getList()) { 362 LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user); 363 if (DEBUG) { 364 Log.v(TAG, "Returning activity for profile " + user + " : " 365 + lai.getComponentName()); 366 } 367 lais.add(lai); 368 } 369 return lais; 370 } 371 372 /** 373 * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it 374 * returns null. 375 * 376 * @param intent The intent to find a match for. 377 * @param user The profile to look in for a match. 378 * @return An activity info object if there is a match. 379 */ 380 public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) { 381 try { 382 ActivityInfo ai = mService.resolveActivity(intent.getComponent(), user); 383 if (ai != null) { 384 LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user); 385 return info; 386 } 387 } catch (RemoteException re) { 388 throw re.rethrowFromSystemServer(); 389 } 390 return null; 391 } 392 393 /** 394 * Starts a Main activity in the specified profile. 395 * 396 * @param component The ComponentName of the activity to launch 397 * @param user The UserHandle of the profile 398 * @param sourceBounds The Rect containing the source bounds of the clicked icon 399 * @param opts Options to pass to startActivity 400 */ 401 public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds, 402 Bundle opts) { 403 if (DEBUG) { 404 Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier()); 405 } 406 try { 407 mService.startActivityAsUser(component, sourceBounds, opts, user); 408 } catch (RemoteException re) { 409 throw re.rethrowFromSystemServer(); 410 } 411 } 412 413 /** 414 * Starts the settings activity to show the application details for a 415 * package in the specified profile. 416 * 417 * @param component The ComponentName of the package to launch settings for. 418 * @param user The UserHandle of the profile 419 * @param sourceBounds The Rect containing the source bounds of the clicked icon 420 * @param opts Options to pass to startActivity 421 */ 422 public void startAppDetailsActivity(ComponentName component, UserHandle user, 423 Rect sourceBounds, Bundle opts) { 424 try { 425 mService.showAppDetailsAsUser(component, sourceBounds, opts, user); 426 } catch (RemoteException re) { 427 throw re.rethrowFromSystemServer(); 428 } 429 } 430 431 /** 432 * Checks if the package is installed and enabled for a profile. 433 * 434 * @param packageName The package to check. 435 * @param user The UserHandle of the profile. 436 * 437 * @return true if the package exists and is enabled. 438 */ 439 public boolean isPackageEnabled(String packageName, UserHandle user) { 440 try { 441 return mService.isPackageEnabled(packageName, user); 442 } catch (RemoteException re) { 443 throw re.rethrowFromSystemServer(); 444 } 445 } 446 447 /** 448 * Retrieve all of the information we know about a particular package / application. 449 * 450 * @param packageName The package of the application 451 * @param flags Additional option flags {@link PackageManager#getApplicationInfo} 452 * @param user The UserHandle of the profile. 453 * 454 * @return An {@link ApplicationInfo} containing information about the package or 455 * null of the package isn't found. 456 * @hide 457 */ 458 public ApplicationInfo getApplicationInfo(String packageName, @ApplicationInfoFlags int flags, 459 UserHandle user) { 460 try { 461 return mService.getApplicationInfo(packageName, flags, user); 462 } catch (RemoteException re) { 463 throw re.rethrowFromSystemServer(); 464 } 465 } 466 467 /** 468 * Checks if the activity exists and it enabled for a profile. 469 * 470 * @param component The activity to check. 471 * @param user The UserHandle of the profile. 472 * 473 * @return true if the activity exists and is enabled. 474 */ 475 public boolean isActivityEnabled(ComponentName component, UserHandle user) { 476 try { 477 return mService.isActivityEnabled(component, user); 478 } catch (RemoteException re) { 479 throw re.rethrowFromSystemServer(); 480 } 481 } 482 483 /** 484 * Returns whether the caller can access the shortcut information. 485 * 486 * <p>Only the default launcher can access the shortcut information. 487 * 488 * <p>Note when this method returns {@code false}, it may be a temporary situation because 489 * the user is trying a new launcher application. The user may decide to change the default 490 * launcher back to the calling application again, so even if a launcher application loses 491 * this permission, it does <b>not</b> have to purge pinned shortcut information. 492 * If the calling launcher application contains pinned shortcuts, they will still work, 493 * even though the caller no longer has the shortcut host permission. 494 * 495 * @throws IllegalStateException when the user is locked. 496 * 497 * @see ShortcutManager 498 */ 499 public boolean hasShortcutHostPermission() { 500 try { 501 return mService.hasShortcutHostPermission(mContext.getPackageName()); 502 } catch (RemoteException re) { 503 throw re.rethrowFromSystemServer(); 504 } 505 } 506 507 /** 508 * Returns {@link ShortcutInfo}s that match {@code query}. 509 * 510 * <p>Callers must be allowed to access the shortcut information, as defined in {@link 511 * #hasShortcutHostPermission()}. 512 * 513 * @param query result includes shortcuts matching this query. 514 * @param user The UserHandle of the profile. 515 * 516 * @return the IDs of {@link ShortcutInfo}s that match the query. 517 * @throws IllegalStateException when the user is locked, or when the {@code user} user 518 * is locked or not running. 519 * 520 * @see ShortcutManager 521 */ 522 @Nullable 523 public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query, 524 @NonNull UserHandle user) { 525 try { 526 return mService.getShortcuts(mContext.getPackageName(), 527 query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity, 528 query.mQueryFlags, user) 529 .getList(); 530 } catch (RemoteException e) { 531 throw e.rethrowFromSystemServer(); 532 } 533 } 534 535 /** 536 * @hide // No longer used. Use getShortcuts() instead. Kept for unit tests. 537 */ 538 @Nullable 539 @Deprecated 540 public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName, 541 @NonNull List<String> ids, @NonNull UserHandle user) { 542 final ShortcutQuery q = new ShortcutQuery(); 543 q.setPackage(packageName); 544 q.setShortcutIds(ids); 545 q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS); 546 return getShortcuts(q, user); 547 } 548 549 /** 550 * Pin shortcuts on a package. 551 * 552 * <p>This API is <b>NOT</b> cumulative; this will replace all pinned shortcuts for the package. 553 * However, different launchers may have different set of pinned shortcuts. 554 * 555 * <p>The calling launcher application must be allowed to access the shortcut information, 556 * as defined in {@link #hasShortcutHostPermission()}. 557 * 558 * @param packageName The target package name. 559 * @param shortcutIds The IDs of the shortcut to be pinned. 560 * @param user The UserHandle of the profile. 561 * @throws IllegalStateException when the user is locked, or when the {@code user} user 562 * is locked or not running. 563 * 564 * @see ShortcutManager 565 */ 566 public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds, 567 @NonNull UserHandle user) { 568 try { 569 mService.pinShortcuts(mContext.getPackageName(), packageName, shortcutIds, user); 570 } catch (RemoteException e) { 571 throw e.rethrowFromSystemServer(); 572 } 573 } 574 575 /** 576 * @hide kept for testing. 577 */ 578 @Deprecated 579 public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) { 580 return shortcut.getIconResourceId(); 581 } 582 583 /** 584 * @hide kept for testing. 585 */ 586 @Deprecated 587 public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId, 588 @NonNull UserHandle user) { 589 final ShortcutQuery q = new ShortcutQuery(); 590 q.setPackage(packageName); 591 q.setShortcutIds(Arrays.asList(shortcutId)); 592 q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS); 593 final List<ShortcutInfo> shortcuts = getShortcuts(q, user); 594 595 return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0; 596 } 597 598 /** 599 * @hide internal/unit tests only 600 */ 601 public ParcelFileDescriptor getShortcutIconFd( 602 @NonNull ShortcutInfo shortcut) { 603 return getShortcutIconFd(shortcut.getPackage(), shortcut.getId(), 604 shortcut.getUserId()); 605 } 606 607 /** 608 * @hide internal/unit tests only 609 */ 610 public ParcelFileDescriptor getShortcutIconFd( 611 @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) { 612 return getShortcutIconFd(packageName, shortcutId, user.getIdentifier()); 613 } 614 615 private ParcelFileDescriptor getShortcutIconFd( 616 @NonNull String packageName, @NonNull String shortcutId, int userId) { 617 try { 618 return mService.getShortcutIconFd(mContext.getPackageName(), 619 packageName, shortcutId, userId); 620 } catch (RemoteException e) { 621 throw e.rethrowFromSystemServer(); 622 } 623 } 624 625 /** 626 * Returns the icon for this shortcut, without any badging for the profile. 627 * 628 * <p>The calling launcher application must be allowed to access the shortcut information, 629 * as defined in {@link #hasShortcutHostPermission()}. 630 * 631 * @param density The preferred density of the icon, zero for default density. Use 632 * density DPI values from {@link DisplayMetrics}. 633 * 634 * @return The drawable associated with the shortcut. 635 * @throws IllegalStateException when the user is locked, or when the {@code user} user 636 * is locked or not running. 637 * 638 * @see ShortcutManager 639 * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int) 640 * @see DisplayMetrics 641 */ 642 public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) { 643 if (shortcut.hasIconFile()) { 644 final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut); 645 if (pfd == null) { 646 return null; 647 } 648 try { 649 final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor()); 650 return (bmp == null) ? null : new BitmapDrawable(mContext.getResources(), bmp); 651 } finally { 652 try { 653 pfd.close(); 654 } catch (IOException ignore) { 655 } 656 } 657 } else if (shortcut.hasIconResource()) { 658 try { 659 final int resId = shortcut.getIconResourceId(); 660 if (resId == 0) { 661 return null; // Shouldn't happen but just in case. 662 } 663 final ApplicationInfo ai = getApplicationInfo(shortcut.getPackage(), 664 /* flags =*/ 0, shortcut.getUserHandle()); 665 final Resources res = mContext.getPackageManager().getResourcesForApplication(ai); 666 return res.getDrawableForDensity(resId, density); 667 } catch (NameNotFoundException | Resources.NotFoundException e) { 668 return null; 669 } 670 } else { 671 return null; // Has no icon. 672 } 673 } 674 675 /** 676 * Returns the shortcut icon with badging appropriate for the profile. 677 * 678 * <p>The calling launcher application must be allowed to access the shortcut information, 679 * as defined in {@link #hasShortcutHostPermission()}. 680 * 681 * @param density Optional density for the icon, or 0 to use the default density. Use 682 * @return A badged icon for the shortcut. 683 * @throws IllegalStateException when the user is locked, or when the {@code user} user 684 * is locked or not running. 685 * 686 * @see ShortcutManager 687 * @see #getShortcutIconDrawable(ShortcutInfo, int) 688 * @see DisplayMetrics 689 */ 690 public Drawable getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density) { 691 final Drawable originalIcon = getShortcutIconDrawable(shortcut, density); 692 693 return (originalIcon == null) ? null : mContext.getPackageManager().getUserBadgedIcon( 694 originalIcon, shortcut.getUserHandle()); 695 } 696 697 /** 698 * Starts a shortcut. 699 * 700 * <p>The calling launcher application must be allowed to access the shortcut information, 701 * as defined in {@link #hasShortcutHostPermission()}. 702 * 703 * @param packageName The target shortcut package name. 704 * @param shortcutId The target shortcut ID. 705 * @param sourceBounds The Rect containing the source bounds of the clicked icon. 706 * @param startActivityOptions Options to pass to startActivity. 707 * @param user The UserHandle of the profile. 708 * @throws IllegalStateException when the user is locked, or when the {@code user} user 709 * is locked or not running. 710 * 711 * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g. 712 * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc) 713 */ 714 public void startShortcut(@NonNull String packageName, @NonNull String shortcutId, 715 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, 716 @NonNull UserHandle user) { 717 startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions, 718 user.getIdentifier()); 719 } 720 721 /** 722 * Launches a shortcut. 723 * 724 * <p>The calling launcher application must be allowed to access the shortcut information, 725 * as defined in {@link #hasShortcutHostPermission()}. 726 * 727 * @param shortcut The target shortcut. 728 * @param sourceBounds The Rect containing the source bounds of the clicked icon. 729 * @param startActivityOptions Options to pass to startActivity. 730 * @throws IllegalStateException when the user is locked, or when the {@code user} user 731 * is locked or not running. 732 * 733 * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g. 734 * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc) 735 */ 736 public void startShortcut(@NonNull ShortcutInfo shortcut, 737 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) { 738 startShortcut(shortcut.getPackage(), shortcut.getId(), 739 sourceBounds, startActivityOptions, 740 shortcut.getUserId()); 741 } 742 743 private void startShortcut(@NonNull String packageName, @NonNull String shortcutId, 744 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, 745 int userId) { 746 try { 747 final boolean success = 748 mService.startShortcut(mContext.getPackageName(), packageName, shortcutId, 749 sourceBounds, startActivityOptions, userId); 750 if (!success) { 751 throw new ActivityNotFoundException("Shortcut could not be started"); 752 } 753 } catch (RemoteException e) { 754 throw e.rethrowFromSystemServer(); 755 } 756 } 757 758 /** 759 * Registers a callback for changes to packages in current and managed profiles. 760 * 761 * @param callback The callback to register. 762 */ 763 public void registerCallback(Callback callback) { 764 registerCallback(callback, null); 765 } 766 767 /** 768 * Registers a callback for changes to packages in current and managed profiles. 769 * 770 * @param callback The callback to register. 771 * @param handler that should be used to post callbacks on, may be null. 772 */ 773 public void registerCallback(Callback callback, Handler handler) { 774 synchronized (this) { 775 if (callback != null && findCallbackLocked(callback) < 0) { 776 boolean addedFirstCallback = mCallbacks.size() == 0; 777 addCallbackLocked(callback, handler); 778 if (addedFirstCallback) { 779 try { 780 mService.addOnAppsChangedListener(mContext.getPackageName(), 781 mAppsChangedListener); 782 } catch (RemoteException re) { 783 throw re.rethrowFromSystemServer(); 784 } 785 } 786 } 787 } 788 } 789 790 /** 791 * Unregisters a callback that was previously registered. 792 * 793 * @param callback The callback to unregister. 794 * @see #registerCallback(Callback) 795 */ 796 public void unregisterCallback(Callback callback) { 797 synchronized (this) { 798 removeCallbackLocked(callback); 799 if (mCallbacks.size() == 0) { 800 try { 801 mService.removeOnAppsChangedListener(mAppsChangedListener); 802 } catch (RemoteException re) { 803 throw re.rethrowFromSystemServer(); 804 } 805 } 806 } 807 } 808 809 /** @return position in mCallbacks for callback or -1 if not present. */ 810 private int findCallbackLocked(Callback callback) { 811 if (callback == null) { 812 throw new IllegalArgumentException("Callback cannot be null"); 813 } 814 final int size = mCallbacks.size(); 815 for (int i = 0; i < size; ++i) { 816 if (mCallbacks.get(i).mCallback == callback) { 817 return i; 818 } 819 } 820 return -1; 821 } 822 823 private void removeCallbackLocked(Callback callback) { 824 int pos = findCallbackLocked(callback); 825 if (pos >= 0) { 826 mCallbacks.remove(pos); 827 } 828 } 829 830 private void addCallbackLocked(Callback callback, Handler handler) { 831 // Remove if already present. 832 removeCallbackLocked(callback); 833 if (handler == null) { 834 handler = new Handler(); 835 } 836 CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback); 837 mCallbacks.add(toAdd); 838 } 839 840 private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() { 841 842 @Override 843 public void onPackageRemoved(UserHandle user, String packageName) 844 throws RemoteException { 845 if (DEBUG) { 846 Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName); 847 } 848 synchronized (LauncherApps.this) { 849 for (CallbackMessageHandler callback : mCallbacks) { 850 callback.postOnPackageRemoved(packageName, user); 851 } 852 } 853 } 854 855 @Override 856 public void onPackageChanged(UserHandle user, String packageName) throws RemoteException { 857 if (DEBUG) { 858 Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName); 859 } 860 synchronized (LauncherApps.this) { 861 for (CallbackMessageHandler callback : mCallbacks) { 862 callback.postOnPackageChanged(packageName, user); 863 } 864 } 865 } 866 867 @Override 868 public void onPackageAdded(UserHandle user, String packageName) throws RemoteException { 869 if (DEBUG) { 870 Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName); 871 } 872 synchronized (LauncherApps.this) { 873 for (CallbackMessageHandler callback : mCallbacks) { 874 callback.postOnPackageAdded(packageName, user); 875 } 876 } 877 } 878 879 @Override 880 public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing) 881 throws RemoteException { 882 if (DEBUG) { 883 Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames); 884 } 885 synchronized (LauncherApps.this) { 886 for (CallbackMessageHandler callback : mCallbacks) { 887 callback.postOnPackagesAvailable(packageNames, user, replacing); 888 } 889 } 890 } 891 892 @Override 893 public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing) 894 throws RemoteException { 895 if (DEBUG) { 896 Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames); 897 } 898 synchronized (LauncherApps.this) { 899 for (CallbackMessageHandler callback : mCallbacks) { 900 callback.postOnPackagesUnavailable(packageNames, user, replacing); 901 } 902 } 903 } 904 905 @Override 906 public void onPackagesSuspended(UserHandle user, String[] packageNames) 907 throws RemoteException { 908 if (DEBUG) { 909 Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames); 910 } 911 synchronized (LauncherApps.this) { 912 for (CallbackMessageHandler callback : mCallbacks) { 913 callback.postOnPackagesSuspended(packageNames, user); 914 } 915 } 916 } 917 918 @Override 919 public void onPackagesUnsuspended(UserHandle user, String[] packageNames) 920 throws RemoteException { 921 if (DEBUG) { 922 Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames); 923 } 924 synchronized (LauncherApps.this) { 925 for (CallbackMessageHandler callback : mCallbacks) { 926 callback.postOnPackagesUnsuspended(packageNames, user); 927 } 928 } 929 } 930 931 @Override 932 public void onShortcutChanged(UserHandle user, String packageName, 933 ParceledListSlice shortcuts) { 934 if (DEBUG) { 935 Log.d(TAG, "onShortcutChanged " + user.getIdentifier() + "," + packageName); 936 } 937 final List<ShortcutInfo> list = shortcuts.getList(); 938 synchronized (LauncherApps.this) { 939 for (CallbackMessageHandler callback : mCallbacks) { 940 callback.postOnShortcutChanged(packageName, user, list); 941 } 942 } 943 } 944 }; 945 946 private static class CallbackMessageHandler extends Handler { 947 private static final int MSG_ADDED = 1; 948 private static final int MSG_REMOVED = 2; 949 private static final int MSG_CHANGED = 3; 950 private static final int MSG_AVAILABLE = 4; 951 private static final int MSG_UNAVAILABLE = 5; 952 private static final int MSG_SUSPENDED = 6; 953 private static final int MSG_UNSUSPENDED = 7; 954 private static final int MSG_SHORTCUT_CHANGED = 8; 955 956 private LauncherApps.Callback mCallback; 957 958 private static class CallbackInfo { 959 String[] packageNames; 960 String packageName; 961 boolean replacing; 962 UserHandle user; 963 List<ShortcutInfo> shortcuts; 964 } 965 966 public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) { 967 super(looper, null, true); 968 mCallback = callback; 969 } 970 971 @Override 972 public void handleMessage(Message msg) { 973 if (mCallback == null || !(msg.obj instanceof CallbackInfo)) { 974 return; 975 } 976 CallbackInfo info = (CallbackInfo) msg.obj; 977 switch (msg.what) { 978 case MSG_ADDED: 979 mCallback.onPackageAdded(info.packageName, info.user); 980 break; 981 case MSG_REMOVED: 982 mCallback.onPackageRemoved(info.packageName, info.user); 983 break; 984 case MSG_CHANGED: 985 mCallback.onPackageChanged(info.packageName, info.user); 986 break; 987 case MSG_AVAILABLE: 988 mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing); 989 break; 990 case MSG_UNAVAILABLE: 991 mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing); 992 break; 993 case MSG_SUSPENDED: 994 mCallback.onPackagesSuspended(info.packageNames, info.user); 995 break; 996 case MSG_UNSUSPENDED: 997 mCallback.onPackagesUnsuspended(info.packageNames, info.user); 998 break; 999 case MSG_SHORTCUT_CHANGED: 1000 mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user); 1001 break; 1002 } 1003 } 1004 1005 public void postOnPackageAdded(String packageName, UserHandle user) { 1006 CallbackInfo info = new CallbackInfo(); 1007 info.packageName = packageName; 1008 info.user = user; 1009 obtainMessage(MSG_ADDED, info).sendToTarget(); 1010 } 1011 1012 public void postOnPackageRemoved(String packageName, UserHandle user) { 1013 CallbackInfo info = new CallbackInfo(); 1014 info.packageName = packageName; 1015 info.user = user; 1016 obtainMessage(MSG_REMOVED, info).sendToTarget(); 1017 } 1018 1019 public void postOnPackageChanged(String packageName, UserHandle user) { 1020 CallbackInfo info = new CallbackInfo(); 1021 info.packageName = packageName; 1022 info.user = user; 1023 obtainMessage(MSG_CHANGED, info).sendToTarget(); 1024 } 1025 1026 public void postOnPackagesAvailable(String[] packageNames, UserHandle user, 1027 boolean replacing) { 1028 CallbackInfo info = new CallbackInfo(); 1029 info.packageNames = packageNames; 1030 info.replacing = replacing; 1031 info.user = user; 1032 obtainMessage(MSG_AVAILABLE, info).sendToTarget(); 1033 } 1034 1035 public void postOnPackagesUnavailable(String[] packageNames, UserHandle user, 1036 boolean replacing) { 1037 CallbackInfo info = new CallbackInfo(); 1038 info.packageNames = packageNames; 1039 info.replacing = replacing; 1040 info.user = user; 1041 obtainMessage(MSG_UNAVAILABLE, info).sendToTarget(); 1042 } 1043 1044 public void postOnPackagesSuspended(String[] packageNames, UserHandle user) { 1045 CallbackInfo info = new CallbackInfo(); 1046 info.packageNames = packageNames; 1047 info.user = user; 1048 obtainMessage(MSG_SUSPENDED, info).sendToTarget(); 1049 } 1050 1051 public void postOnPackagesUnsuspended(String[] packageNames, UserHandle user) { 1052 CallbackInfo info = new CallbackInfo(); 1053 info.packageNames = packageNames; 1054 info.user = user; 1055 obtainMessage(MSG_UNSUSPENDED, info).sendToTarget(); 1056 } 1057 1058 public void postOnShortcutChanged(String packageName, UserHandle user, 1059 List<ShortcutInfo> shortcuts) { 1060 CallbackInfo info = new CallbackInfo(); 1061 info.packageName = packageName; 1062 info.user = user; 1063 info.shortcuts = shortcuts; 1064 obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget(); 1065 } 1066 } 1067 } 1068