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