LauncherApps.java revision 19b41f34a5cb29c621848e352220017b46cf66f1
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 17package android.content.pm; 18 19import android.annotation.IntDef; 20import android.annotation.NonNull; 21import android.annotation.Nullable; 22import android.annotation.SdkConstant; 23import android.annotation.SdkConstant.SdkConstantType; 24import android.annotation.SystemService; 25import android.annotation.TestApi; 26import android.app.PendingIntent; 27import android.appwidget.AppWidgetManager; 28import android.appwidget.AppWidgetProviderInfo; 29import android.content.ActivityNotFoundException; 30import android.content.ComponentName; 31import android.content.Context; 32import android.content.Intent; 33import android.content.IntentSender; 34import android.content.pm.PackageManager.ApplicationInfoFlags; 35import android.content.pm.PackageManager.NameNotFoundException; 36import android.content.res.Resources; 37import android.graphics.Bitmap; 38import android.graphics.BitmapFactory; 39import android.graphics.Rect; 40import android.graphics.drawable.AdaptiveIconDrawable; 41import android.graphics.drawable.BitmapDrawable; 42import android.graphics.drawable.Drawable; 43import android.graphics.drawable.Icon; 44import android.os.Bundle; 45import android.os.Handler; 46import android.os.Looper; 47import android.os.Message; 48import android.os.Parcel; 49import android.os.ParcelFileDescriptor; 50import android.os.Parcelable; 51import android.os.RemoteException; 52import android.os.ServiceManager; 53import android.os.UserHandle; 54import android.os.UserManager; 55import android.util.DisplayMetrics; 56import android.util.Log; 57 58import com.android.internal.util.Preconditions; 59 60import java.io.IOException; 61import java.lang.annotation.Retention; 62import java.lang.annotation.RetentionPolicy; 63import java.util.ArrayList; 64import java.util.Arrays; 65import java.util.Collections; 66import java.util.List; 67 68/** 69 * Class for retrieving a list of launchable activities for the current user and any associated 70 * managed profiles that are visible to the current user, which can be retrieved with 71 * {@link #getProfiles}. This is mainly for use by launchers. 72 * 73 * Apps can be queried for each user profile. 74 * Since the PackageManager will not deliver package broadcasts for other profiles, you can register 75 * for package changes here. 76 * <p> 77 * To watch for managed profiles being added or removed, register for the following broadcasts: 78 * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}. 79 * <p> 80 * Note as of Android O, apps on a managed profile are no longer allowed to access apps on the 81 * main profile. Apps can only access profiles returned by {@link #getProfiles()}. 82 */ 83@SystemService(Context.LAUNCHER_APPS_SERVICE) 84public class LauncherApps { 85 86 static final String TAG = "LauncherApps"; 87 static final boolean DEBUG = false; 88 89 /** 90 * Activity Action: For the default launcher to show the confirmation dialog to create 91 * a pinned shortcut. 92 * 93 * <p>See the {@link ShortcutManager} javadoc for details. 94 * 95 * <p> 96 * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object, 97 * and call {@link PinItemRequest#accept(Bundle)} 98 * if the user accepts. If the user doesn't accept, no further action is required. 99 * 100 * @see #EXTRA_PIN_ITEM_REQUEST 101 */ 102 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 103 public static final String ACTION_CONFIRM_PIN_SHORTCUT = 104 "android.content.pm.action.CONFIRM_PIN_SHORTCUT"; 105 106 /** 107 * Activity Action: For the default launcher to show the confirmation dialog to create 108 * a pinned app widget. 109 * 110 * <p>See the {@link android.appwidget.AppWidgetManager#requestPinAppWidget} javadoc for 111 * details. 112 * 113 * <p> 114 * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object, 115 * and call {@link PinItemRequest#accept(Bundle)} 116 * if the user accepts. If the user doesn't accept, no further action is required. 117 * 118 * @see #EXTRA_PIN_ITEM_REQUEST 119 */ 120 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 121 public static final String ACTION_CONFIRM_PIN_APPWIDGET = 122 "android.content.pm.action.CONFIRM_PIN_APPWIDGET"; 123 124 /** 125 * An extra for {@link #ACTION_CONFIRM_PIN_SHORTCUT} & {@link #ACTION_CONFIRM_PIN_APPWIDGET} 126 * containing a {@link PinItemRequest} of appropriate type asked to pin. 127 * 128 * <p>A helper function {@link #getPinItemRequest(Intent)} can be used 129 * instead of using this constant directly. 130 * 131 * @see #ACTION_CONFIRM_PIN_SHORTCUT 132 * @see #ACTION_CONFIRM_PIN_APPWIDGET 133 */ 134 public static final String EXTRA_PIN_ITEM_REQUEST = 135 "android.content.pm.extra.PIN_ITEM_REQUEST"; 136 137 private final Context mContext; 138 private final ILauncherApps mService; 139 private final PackageManager mPm; 140 private final UserManager mUserManager; 141 142 private List<CallbackMessageHandler> mCallbacks 143 = new ArrayList<CallbackMessageHandler>(); 144 145 /** 146 * Callbacks for package changes to this and related managed profiles. 147 */ 148 public static abstract class Callback { 149 /** 150 * Indicates that a package was removed from the specified profile. 151 * 152 * If a package is removed while being updated onPackageChanged will be 153 * called instead. 154 * 155 * @param packageName The name of the package that was removed. 156 * @param user The UserHandle of the profile that generated the change. 157 */ 158 abstract public void onPackageRemoved(String packageName, UserHandle user); 159 160 /** 161 * Indicates that a package was added to the specified profile. 162 * 163 * If a package is added while being updated then onPackageChanged will be 164 * called instead. 165 * 166 * @param packageName The name of the package that was added. 167 * @param user The UserHandle of the profile that generated the change. 168 */ 169 abstract public void onPackageAdded(String packageName, UserHandle user); 170 171 /** 172 * Indicates that a package was modified in the specified profile. 173 * This can happen, for example, when the package is updated or when 174 * one or more components are enabled or disabled. 175 * 176 * @param packageName The name of the package that has changed. 177 * @param user The UserHandle of the profile that generated the change. 178 */ 179 abstract public void onPackageChanged(String packageName, UserHandle user); 180 181 /** 182 * Indicates that one or more packages have become available. For 183 * example, this can happen when a removable storage card has 184 * reappeared. 185 * 186 * @param packageNames The names of the packages that have become 187 * available. 188 * @param user The UserHandle of the profile that generated the change. 189 * @param replacing Indicates whether these packages are replacing 190 * existing ones. 191 */ 192 abstract public void onPackagesAvailable(String[] packageNames, UserHandle user, 193 boolean replacing); 194 195 /** 196 * Indicates that one or more packages have become unavailable. For 197 * example, this can happen when a removable storage card has been 198 * removed. 199 * 200 * @param packageNames The names of the packages that have become 201 * unavailable. 202 * @param user The UserHandle of the profile that generated the change. 203 * @param replacing Indicates whether the packages are about to be 204 * replaced with new versions. 205 */ 206 abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user, 207 boolean replacing); 208 209 /** 210 * Indicates that one or more packages have been suspended. For 211 * example, this can happen when a Device Administrator suspends 212 * an applicaton. 213 * 214 * <p>Note: On devices running {@link android.os.Build.VERSION_CODES#P Android P} or higher, 215 * any apps that override {@link #onPackagesSuspended(String[], Bundle, UserHandle)} will 216 * not receive this callback. 217 * 218 * @param packageNames The names of the packages that have just been 219 * suspended. 220 * @param user The UserHandle of the profile that generated the change. 221 */ 222 public void onPackagesSuspended(String[] packageNames, UserHandle user) { 223 } 224 225 /** 226 * Indicates that one or more packages have been suspended. A device administrator or an app 227 * with {@code android.permission.SUSPEND_APPS} can do this. 228 * 229 * @param packageNames The names of the packages that have just been suspended. 230 * @param launcherExtras A {@link Bundle} of extras for the launcher. 231 * @param user the user for which the given packages were suspended. 232 * 233 * @see PackageManager#isPackageSuspended() 234 * @see #getSuspendedPackageLauncherExtras(String, UserHandle) 235 */ 236 public void onPackagesSuspended(String[] packageNames, @Nullable Bundle launcherExtras, 237 UserHandle user) { 238 onPackagesSuspended(packageNames, user); 239 } 240 241 /** 242 * Indicates that one or more packages have been unsuspended. For 243 * example, this can happen when a Device Administrator unsuspends 244 * an applicaton. 245 * 246 * @param packageNames The names of the packages that have just been 247 * unsuspended. 248 * @param user The UserHandle of the profile that generated the change. 249 */ 250 public void onPackagesUnsuspended(String[] packageNames, UserHandle user) { 251 } 252 253 /** 254 * Indicates that one or more shortcuts of any kind (dynamic, pinned, or manifest) 255 * have been added, updated or removed. 256 * 257 * <p>Only the applications that are allowed to access the shortcut information, 258 * as defined in {@link #hasShortcutHostPermission()}, will receive it. 259 * 260 * @param packageName The name of the package that has the shortcuts. 261 * @param shortcuts All shortcuts from the package (dynamic, manifest and/or pinned). 262 * Only "key" information will be provided, as defined in 263 * {@link ShortcutInfo#hasKeyFieldsOnly()}. 264 * @param user The UserHandle of the profile that generated the change. 265 * 266 * @see ShortcutManager 267 */ 268 public void onShortcutsChanged(@NonNull String packageName, 269 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { 270 } 271 } 272 273 /** 274 * Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}. 275 */ 276 public static class ShortcutQuery { 277 /** 278 * Include dynamic shortcuts in the result. 279 */ 280 public static final int FLAG_MATCH_DYNAMIC = 1 << 0; 281 282 /** @hide kept for unit tests */ 283 @Deprecated 284 public static final int FLAG_GET_DYNAMIC = FLAG_MATCH_DYNAMIC; 285 286 /** 287 * Include pinned shortcuts in the result. 288 * 289 * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the 290 * user owns on the launcher (or by other launchers, in case the user has multiple), use 291 * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead. 292 * 293 * <p>If you're a regular launcher app, there's no way to get shortcuts pinned by other 294 * launchers, and {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} will be ignored. So use this 295 * flag to get own pinned shortcuts. 296 */ 297 public static final int FLAG_MATCH_PINNED = 1 << 1; 298 299 /** @hide kept for unit tests */ 300 @Deprecated 301 public static final int FLAG_GET_PINNED = FLAG_MATCH_PINNED; 302 303 /** 304 * Include manifest shortcuts in the result. 305 */ 306 public static final int FLAG_MATCH_MANIFEST = 1 << 3; 307 308 /** @hide kept for unit tests */ 309 @Deprecated 310 public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST; 311 312 /** 313 * Include all pinned shortcuts by any launchers, not just by the caller, 314 * in the result. 315 * 316 * <p>The caller must be the selected assistant app to use this flag, or have the system 317 * {@code ACCESS_SHORTCUTS} permission. 318 * 319 * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the 320 * user owns on the launcher (or by other launchers, in case the user has multiple), use 321 * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead. 322 * 323 * <p>If you're a regular launcher app (or any app that's not the selected assistant app) 324 * then this flag will be ignored. 325 */ 326 public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1 << 10; 327 328 /** 329 * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST 330 * @hide 331 */ 332 public static final int FLAG_MATCH_ALL_KINDS = 333 FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST; 334 335 /** 336 * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_ALL_PINNED 337 * @hide 338 */ 339 public static final int FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED = 340 FLAG_MATCH_ALL_KINDS | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER; 341 342 /** @hide kept for unit tests */ 343 @Deprecated 344 public static final int FLAG_GET_ALL_KINDS = FLAG_MATCH_ALL_KINDS; 345 346 /** 347 * Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()}'s javadoc to 348 * see which fields fields "key". 349 * This allows quicker access to shortcut information in order to 350 * determine whether the caller's in-memory cache needs to be updated. 351 * 352 * <p>Typically, launcher applications cache all or most shortcut information 353 * in memory in order to show shortcuts without a delay. 354 * 355 * When a given launcher application wants to update its cache, such as when its process 356 * restarts, it can fetch shortcut information with this flag. 357 * The application can then check {@link ShortcutInfo#getLastChangedTimestamp()} for each 358 * shortcut, fetching a shortcut's non-key information only if that shortcut has been 359 * updated. 360 * 361 * @see ShortcutManager 362 */ 363 public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2; 364 365 /** @hide */ 366 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 367 FLAG_MATCH_DYNAMIC, 368 FLAG_MATCH_PINNED, 369 FLAG_MATCH_MANIFEST, 370 FLAG_GET_KEY_FIELDS_ONLY, 371 FLAG_MATCH_MANIFEST, 372 }) 373 @Retention(RetentionPolicy.SOURCE) 374 public @interface QueryFlags {} 375 376 long mChangedSince; 377 378 @Nullable 379 String mPackage; 380 381 @Nullable 382 List<String> mShortcutIds; 383 384 @Nullable 385 ComponentName mActivity; 386 387 @QueryFlags 388 int mQueryFlags; 389 390 public ShortcutQuery() { 391 } 392 393 /** 394 * If non-zero, returns only shortcuts that have been added or updated 395 * since the given timestamp, expressed in milliseconds since the Epoch—see 396 * {@link System#currentTimeMillis()}. 397 */ 398 public ShortcutQuery setChangedSince(long changedSince) { 399 mChangedSince = changedSince; 400 return this; 401 } 402 403 /** 404 * If non-null, returns only shortcuts from the package. 405 */ 406 public ShortcutQuery setPackage(@Nullable String packageName) { 407 mPackage = packageName; 408 return this; 409 } 410 411 /** 412 * If non-null, return only the specified shortcuts by ID. When setting this field, 413 * a package name must also be set with {@link #setPackage}. 414 */ 415 public ShortcutQuery setShortcutIds(@Nullable List<String> shortcutIds) { 416 mShortcutIds = shortcutIds; 417 return this; 418 } 419 420 /** 421 * If non-null, returns only shortcuts associated with the activity; i.e. 422 * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal 423 * to {@code activity}. 424 */ 425 public ShortcutQuery setActivity(@Nullable ComponentName activity) { 426 mActivity = activity; 427 return this; 428 } 429 430 /** 431 * Set query options. At least one of the {@code MATCH} flags should be set. Otherwise, 432 * no shortcuts will be returned. 433 * 434 * <ul> 435 * <li>{@link #FLAG_MATCH_DYNAMIC} 436 * <li>{@link #FLAG_MATCH_PINNED} 437 * <li>{@link #FLAG_MATCH_MANIFEST} 438 * <li>{@link #FLAG_GET_KEY_FIELDS_ONLY} 439 * </ul> 440 */ 441 public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) { 442 mQueryFlags = queryFlags; 443 return this; 444 } 445 } 446 447 /** @hide */ 448 public LauncherApps(Context context, ILauncherApps service) { 449 mContext = context; 450 mService = service; 451 mPm = context.getPackageManager(); 452 mUserManager = context.getSystemService(UserManager.class); 453 } 454 455 /** @hide */ 456 @TestApi 457 public LauncherApps(Context context) { 458 this(context, ILauncherApps.Stub.asInterface( 459 ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE))); 460 } 461 462 /** 463 * Show an error log on logcat, when the calling user is a managed profile, and the target 464 * user is different from the calling user, in order to help developers to detect it. 465 */ 466 private void logErrorForInvalidProfileAccess(@NonNull UserHandle target) { 467 if (UserHandle.myUserId() != target.getIdentifier() && mUserManager.isManagedProfile()) { 468 Log.w(TAG, "Accessing other profiles/users from managed profile is no longer allowed."); 469 } 470 } 471 472 /** 473 * Return a list of profiles that the caller can access via the {@link LauncherApps} APIs. 474 * 475 * <p>If the caller is running on a managed profile, it'll return only the current profile. 476 * Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would. 477 */ 478 public List<UserHandle> getProfiles() { 479 if (mUserManager.isManagedProfile()) { 480 // If it's a managed profile, only return the current profile. 481 final List result = new ArrayList(1); 482 result.add(android.os.Process.myUserHandle()); 483 return result; 484 } else { 485 return mUserManager.getUserProfiles(); 486 } 487 } 488 489 /** 490 * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and 491 * {@link Intent#CATEGORY_LAUNCHER}, for a specified user. 492 * 493 * @param packageName The specific package to query. If null, it checks all installed packages 494 * in the profile. 495 * @param user The UserHandle of the profile. 496 * @return List of launchable activities. Can be an empty list but will not be null. 497 */ 498 public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) { 499 logErrorForInvalidProfileAccess(user); 500 try { 501 return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(), 502 packageName, user), user); 503 } catch (RemoteException re) { 504 throw re.rethrowFromSystemServer(); 505 } 506 } 507 508 /** 509 * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it 510 * returns null. 511 * 512 * @param intent The intent to find a match for. 513 * @param user The profile to look in for a match. 514 * @return An activity info object if there is a match. 515 */ 516 public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) { 517 logErrorForInvalidProfileAccess(user); 518 try { 519 ActivityInfo ai = mService.resolveActivity(mContext.getPackageName(), 520 intent.getComponent(), user); 521 if (ai != null) { 522 LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user); 523 return info; 524 } 525 } catch (RemoteException re) { 526 throw re.rethrowFromSystemServer(); 527 } 528 return null; 529 } 530 531 /** 532 * Starts a Main activity in the specified profile. 533 * 534 * @param component The ComponentName of the activity to launch 535 * @param user The UserHandle of the profile 536 * @param sourceBounds The Rect containing the source bounds of the clicked icon 537 * @param opts Options to pass to startActivity 538 */ 539 public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds, 540 Bundle opts) { 541 logErrorForInvalidProfileAccess(user); 542 if (DEBUG) { 543 Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier()); 544 } 545 try { 546 mService.startActivityAsUser(mContext.getPackageName(), 547 component, sourceBounds, opts, user); 548 } catch (RemoteException re) { 549 throw re.rethrowFromSystemServer(); 550 } 551 } 552 553 /** 554 * Starts the settings activity to show the application details for a 555 * package in the specified profile. 556 * 557 * @param component The ComponentName of the package to launch settings for. 558 * @param user The UserHandle of the profile 559 * @param sourceBounds The Rect containing the source bounds of the clicked icon 560 * @param opts Options to pass to startActivity 561 */ 562 public void startAppDetailsActivity(ComponentName component, UserHandle user, 563 Rect sourceBounds, Bundle opts) { 564 logErrorForInvalidProfileAccess(user); 565 try { 566 mService.showAppDetailsAsUser(mContext.getPackageName(), 567 component, sourceBounds, opts, user); 568 } catch (RemoteException re) { 569 throw re.rethrowFromSystemServer(); 570 } 571 } 572 573 /** 574 * Retrieves a list of config activities for creating {@link ShortcutInfo}. 575 * 576 * @param packageName The specific package to query. If null, it checks all installed packages 577 * in the profile. 578 * @param user The UserHandle of the profile. 579 * @return List of config activities. Can be an empty list but will not be null. 580 * 581 * @see Intent#ACTION_CREATE_SHORTCUT 582 * @see #getShortcutConfigActivityIntent(LauncherActivityInfo) 583 */ 584 public List<LauncherActivityInfo> getShortcutConfigActivityList(@Nullable String packageName, 585 @NonNull UserHandle user) { 586 logErrorForInvalidProfileAccess(user); 587 try { 588 return convertToActivityList(mService.getShortcutConfigActivities( 589 mContext.getPackageName(), packageName, user), 590 user); 591 } catch (RemoteException re) { 592 throw re.rethrowFromSystemServer(); 593 } 594 } 595 596 private List<LauncherActivityInfo> convertToActivityList( 597 @Nullable ParceledListSlice<ResolveInfo> activities, UserHandle user) { 598 if (activities == null) { 599 return Collections.EMPTY_LIST; 600 } 601 ArrayList<LauncherActivityInfo> lais = new ArrayList<>(); 602 for (ResolveInfo ri : activities.getList()) { 603 LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user); 604 if (DEBUG) { 605 Log.v(TAG, "Returning activity for profile " + user + " : " 606 + lai.getComponentName()); 607 } 608 lais.add(lai); 609 } 610 return lais; 611 } 612 613 /** 614 * Returns an intent sender which can be used to start the configure activity for creating 615 * custom shortcuts. Use this method if the provider is in another profile as you are not 616 * allowed to start an activity in another profile. 617 * 618 * <p>The caller should receive {@link PinItemRequest} in onActivityResult on 619 * {@link android.app.Activity#RESULT_OK}. 620 * 621 * <p>Callers must be allowed to access the shortcut information, as defined in {@link 622 * #hasShortcutHostPermission()}. 623 * 624 * @param info a configuration activity returned by {@link #getShortcutConfigActivityList} 625 * 626 * @throws IllegalStateException when the user is locked or not running. 627 * @throws SecurityException if {@link #hasShortcutHostPermission()} is false. 628 * 629 * @see #getPinItemRequest(Intent) 630 * @see Intent#ACTION_CREATE_SHORTCUT 631 * @see android.app.Activity#startIntentSenderForResult 632 */ 633 @Nullable 634 public IntentSender getShortcutConfigActivityIntent(@NonNull LauncherActivityInfo info) { 635 try { 636 return mService.getShortcutConfigActivityIntent( 637 mContext.getPackageName(), info.getComponentName(), info.getUser()); 638 } catch (RemoteException re) { 639 throw re.rethrowFromSystemServer(); 640 } 641 } 642 643 /** 644 * Checks if the package is installed and enabled for a profile. 645 * 646 * @param packageName The package to check. 647 * @param user The UserHandle of the profile. 648 * 649 * @return true if the package exists and is enabled. 650 */ 651 public boolean isPackageEnabled(String packageName, UserHandle user) { 652 logErrorForInvalidProfileAccess(user); 653 try { 654 return mService.isPackageEnabled(mContext.getPackageName(), packageName, user); 655 } catch (RemoteException re) { 656 throw re.rethrowFromSystemServer(); 657 } 658 } 659 660 /** 661 * Gets the launcher extras supplied to the system when the given package was suspended via 662 * {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle, 663 * PersistableBundle, String)}. 664 * 665 * <p>Note: This just returns whatever extras were provided to the system, <em>which might 666 * even be {@code null}.</em> 667 * 668 * @param packageName The package for which to fetch the launcher extras. 669 * @param user The {@link UserHandle} of the profile. 670 * @return A {@link Bundle} of launcher extras. Or {@code null} if the package is not currently 671 * suspended. 672 * 673 * @see Callback#onPackagesSuspended(String[], Bundle, UserHandle) 674 * @see PackageManager#isPackageSuspended() 675 */ 676 public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) { 677 logErrorForInvalidProfileAccess(user); 678 try { 679 return mService.getSuspendedPackageLauncherExtras(packageName, user); 680 } catch (RemoteException re) { 681 throw re.rethrowFromSystemServer(); 682 } 683 } 684 685 /** 686 * Returns {@link ApplicationInfo} about an application installed for a specific user profile. 687 * 688 * @param packageName The package name of the application 689 * @param flags Additional option flags {@link PackageManager#getApplicationInfo} 690 * @param user The UserHandle of the profile. 691 * 692 * @return {@link ApplicationInfo} containing information about the package. Returns 693 * {@code null} if the package isn't installed for the given profile, or the profile 694 * isn't enabled. 695 */ 696 public ApplicationInfo getApplicationInfo(@NonNull String packageName, 697 @ApplicationInfoFlags int flags, @NonNull UserHandle user) 698 throws PackageManager.NameNotFoundException { 699 Preconditions.checkNotNull(packageName, "packageName"); 700 Preconditions.checkNotNull(user, "user"); 701 logErrorForInvalidProfileAccess(user); 702 try { 703 final ApplicationInfo ai = mService 704 .getApplicationInfo(mContext.getPackageName(), packageName, flags, user); 705 if (ai == null) { 706 throw new NameNotFoundException("Package " + packageName + " not found for user " 707 + user.getIdentifier()); 708 } 709 return ai; 710 } catch (RemoteException re) { 711 throw re.rethrowFromSystemServer(); 712 } 713 } 714 715 /** 716 * Checks if the activity exists and it enabled for a profile. 717 * 718 * @param component The activity to check. 719 * @param user The UserHandle of the profile. 720 * 721 * @return true if the activity exists and is enabled. 722 */ 723 public boolean isActivityEnabled(ComponentName component, UserHandle user) { 724 logErrorForInvalidProfileAccess(user); 725 try { 726 return mService.isActivityEnabled(mContext.getPackageName(), component, user); 727 } catch (RemoteException re) { 728 throw re.rethrowFromSystemServer(); 729 } 730 } 731 732 /** 733 * Returns whether the caller can access the shortcut information. Access is currently 734 * available to: 735 * 736 * <ul> 737 * <li>The current launcher (or default launcher if there is no set current launcher).</li> 738 * <li>The currently active voice interaction service.</li> 739 * </ul> 740 * 741 * <p>Note when this method returns {@code false}, it may be a temporary situation because 742 * the user is trying a new launcher application. The user may decide to change the default 743 * launcher back to the calling application again, so even if a launcher application loses 744 * this permission, it does <b>not</b> have to purge pinned shortcut information. 745 * If the calling launcher application contains pinned shortcuts, they will still work, 746 * even though the caller no longer has the shortcut host permission. 747 * 748 * @throws IllegalStateException when the user is locked. 749 * 750 * @see ShortcutManager 751 */ 752 public boolean hasShortcutHostPermission() { 753 try { 754 return mService.hasShortcutHostPermission(mContext.getPackageName()); 755 } catch (RemoteException re) { 756 throw re.rethrowFromSystemServer(); 757 } 758 } 759 760 private List<ShortcutInfo> maybeUpdateDisabledMessage(List<ShortcutInfo> shortcuts) { 761 if (shortcuts == null) { 762 return null; 763 } 764 for (int i = shortcuts.size() - 1; i >= 0; i--) { 765 final ShortcutInfo si = shortcuts.get(i); 766 final String message = ShortcutInfo.getDisabledReasonForRestoreIssue(mContext, 767 si.getDisabledReason()); 768 if (message != null) { 769 si.setDisabledMessage(message); 770 } 771 } 772 return shortcuts; 773 } 774 775 /** 776 * Returns {@link ShortcutInfo}s that match {@code query}. 777 * 778 * <p>Callers must be allowed to access the shortcut information, as defined in {@link 779 * #hasShortcutHostPermission()}. 780 * 781 * @param query result includes shortcuts matching this query. 782 * @param user The UserHandle of the profile. 783 * 784 * @return the IDs of {@link ShortcutInfo}s that match the query. 785 * @throws IllegalStateException when the user is locked, or when the {@code user} user 786 * is locked or not running. 787 * 788 * @see ShortcutManager 789 */ 790 @Nullable 791 public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query, 792 @NonNull UserHandle user) { 793 logErrorForInvalidProfileAccess(user); 794 try { 795 // Note this is the only case we need to update the disabled message for shortcuts 796 // that weren't restored. 797 // The restore problem messages are only shown by the user, and publishers will never 798 // see them. The only other API that the launcher gets shortcuts is the shortcut 799 // changed callback, but that only returns shortcuts with the "key" information, so 800 // that won't return disabled message. 801 return maybeUpdateDisabledMessage(mService.getShortcuts(mContext.getPackageName(), 802 query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity, 803 query.mQueryFlags, user) 804 .getList()); 805 } catch (RemoteException e) { 806 throw e.rethrowFromSystemServer(); 807 } 808 } 809 810 /** 811 * @hide // No longer used. Use getShortcuts() instead. Kept for unit tests. 812 */ 813 @Nullable 814 @Deprecated 815 public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName, 816 @NonNull List<String> ids, @NonNull UserHandle user) { 817 final ShortcutQuery q = new ShortcutQuery(); 818 q.setPackage(packageName); 819 q.setShortcutIds(ids); 820 q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS); 821 return getShortcuts(q, user); 822 } 823 824 /** 825 * Pin shortcuts on a package. 826 * 827 * <p>This API is <b>NOT</b> cumulative; this will replace all pinned shortcuts for the package. 828 * However, different launchers may have different set of pinned shortcuts. 829 * 830 * <p>The calling launcher application must be allowed to access the shortcut information, 831 * as defined in {@link #hasShortcutHostPermission()}. 832 * 833 * @param packageName The target package name. 834 * @param shortcutIds The IDs of the shortcut to be pinned. 835 * @param user The UserHandle of the profile. 836 * @throws IllegalStateException when the user is locked, or when the {@code user} user 837 * is locked or not running. 838 * 839 * @see ShortcutManager 840 */ 841 public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds, 842 @NonNull UserHandle user) { 843 logErrorForInvalidProfileAccess(user); 844 try { 845 mService.pinShortcuts(mContext.getPackageName(), packageName, shortcutIds, user); 846 } catch (RemoteException e) { 847 throw e.rethrowFromSystemServer(); 848 } 849 } 850 851 /** 852 * @hide kept for testing. 853 */ 854 @Deprecated 855 public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) { 856 return shortcut.getIconResourceId(); 857 } 858 859 /** 860 * @hide kept for testing. 861 */ 862 @Deprecated 863 public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId, 864 @NonNull UserHandle user) { 865 final ShortcutQuery q = new ShortcutQuery(); 866 q.setPackage(packageName); 867 q.setShortcutIds(Arrays.asList(shortcutId)); 868 q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS); 869 final List<ShortcutInfo> shortcuts = getShortcuts(q, user); 870 871 return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0; 872 } 873 874 /** 875 * @hide internal/unit tests only 876 */ 877 public ParcelFileDescriptor getShortcutIconFd( 878 @NonNull ShortcutInfo shortcut) { 879 return getShortcutIconFd(shortcut.getPackage(), shortcut.getId(), 880 shortcut.getUserId()); 881 } 882 883 /** 884 * @hide internal/unit tests only 885 */ 886 public ParcelFileDescriptor getShortcutIconFd( 887 @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) { 888 return getShortcutIconFd(packageName, shortcutId, user.getIdentifier()); 889 } 890 891 private ParcelFileDescriptor getShortcutIconFd( 892 @NonNull String packageName, @NonNull String shortcutId, int userId) { 893 try { 894 return mService.getShortcutIconFd(mContext.getPackageName(), 895 packageName, shortcutId, userId); 896 } catch (RemoteException e) { 897 throw e.rethrowFromSystemServer(); 898 } 899 } 900 901 /** 902 * Returns the icon for this shortcut, without any badging for the profile. 903 * 904 * <p>The calling launcher application must be allowed to access the shortcut information, 905 * as defined in {@link #hasShortcutHostPermission()}. 906 * 907 * @param density The preferred density of the icon, zero for default density. Use 908 * density DPI values from {@link DisplayMetrics}. 909 * 910 * @return The drawable associated with the shortcut. 911 * @throws IllegalStateException when the user is locked, or when the {@code user} user 912 * is locked or not running. 913 * 914 * @see ShortcutManager 915 * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int) 916 * @see DisplayMetrics 917 */ 918 public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) { 919 if (shortcut.hasIconFile()) { 920 final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut); 921 if (pfd == null) { 922 return null; 923 } 924 try { 925 final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor()); 926 if (bmp != null) { 927 BitmapDrawable dr = new BitmapDrawable(mContext.getResources(), bmp); 928 if (shortcut.hasAdaptiveBitmap()) { 929 return new AdaptiveIconDrawable(null, dr); 930 } else { 931 return dr; 932 } 933 } 934 return null; 935 } finally { 936 try { 937 pfd.close(); 938 } catch (IOException ignore) { 939 } 940 } 941 } else if (shortcut.hasIconResource()) { 942 return loadDrawableResourceFromPackage(shortcut.getPackage(), 943 shortcut.getIconResourceId(), shortcut.getUserHandle(), density); 944 } else if (shortcut.getIcon() != null) { 945 // This happens if a shortcut is pending-approval. 946 final Icon icon = shortcut.getIcon(); 947 switch (icon.getType()) { 948 case Icon.TYPE_RESOURCE: { 949 return loadDrawableResourceFromPackage(shortcut.getPackage(), 950 icon.getResId(), shortcut.getUserHandle(), density); 951 } 952 case Icon.TYPE_BITMAP: 953 case Icon.TYPE_ADAPTIVE_BITMAP: { 954 return icon.loadDrawable(mContext); 955 } 956 default: 957 return null; // Shouldn't happen though. 958 } 959 } else { 960 return null; // Has no icon. 961 } 962 } 963 964 private Drawable loadDrawableResourceFromPackage(String packageName, int resId, 965 UserHandle user, int density) { 966 try { 967 if (resId == 0) { 968 return null; // Shouldn't happen but just in case. 969 } 970 final ApplicationInfo ai = getApplicationInfo(packageName, /* flags =*/ 0, user); 971 final Resources res = mContext.getPackageManager().getResourcesForApplication(ai); 972 return res.getDrawableForDensity(resId, density); 973 } catch (NameNotFoundException | Resources.NotFoundException e) { 974 return null; 975 } 976 } 977 978 /** 979 * Returns the shortcut icon with badging appropriate for the profile. 980 * 981 * <p>The calling launcher application must be allowed to access the shortcut information, 982 * as defined in {@link #hasShortcutHostPermission()}. 983 * 984 * @param density Optional density for the icon, or 0 to use the default density. Use 985 * @return A badged icon for the shortcut. 986 * @throws IllegalStateException when the user is locked, or when the {@code user} user 987 * is locked or not running. 988 * 989 * @see ShortcutManager 990 * @see #getShortcutIconDrawable(ShortcutInfo, int) 991 * @see DisplayMetrics 992 */ 993 public Drawable getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density) { 994 final Drawable originalIcon = getShortcutIconDrawable(shortcut, density); 995 996 return (originalIcon == null) ? null : mContext.getPackageManager().getUserBadgedIcon( 997 originalIcon, shortcut.getUserHandle()); 998 } 999 1000 /** 1001 * Starts a shortcut. 1002 * 1003 * <p>The calling launcher application must be allowed to access the shortcut information, 1004 * as defined in {@link #hasShortcutHostPermission()}. 1005 * 1006 * @param packageName The target shortcut package name. 1007 * @param shortcutId The target shortcut ID. 1008 * @param sourceBounds The Rect containing the source bounds of the clicked icon. 1009 * @param startActivityOptions Options to pass to startActivity. 1010 * @param user The UserHandle of the profile. 1011 * @throws IllegalStateException when the user is locked, or when the {@code user} user 1012 * is locked or not running. 1013 * 1014 * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g. 1015 * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc) 1016 */ 1017 public void startShortcut(@NonNull String packageName, @NonNull String shortcutId, 1018 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, 1019 @NonNull UserHandle user) { 1020 logErrorForInvalidProfileAccess(user); 1021 1022 startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions, 1023 user.getIdentifier()); 1024 } 1025 1026 /** 1027 * Launches a shortcut. 1028 * 1029 * <p>The calling launcher application must be allowed to access the shortcut information, 1030 * as defined in {@link #hasShortcutHostPermission()}. 1031 * 1032 * @param shortcut The target shortcut. 1033 * @param sourceBounds The Rect containing the source bounds of the clicked icon. 1034 * @param startActivityOptions Options to pass to startActivity. 1035 * @throws IllegalStateException when the user is locked, or when the {@code user} user 1036 * is locked or not running. 1037 * 1038 * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g. 1039 * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc) 1040 */ 1041 public void startShortcut(@NonNull ShortcutInfo shortcut, 1042 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) { 1043 startShortcut(shortcut.getPackage(), shortcut.getId(), 1044 sourceBounds, startActivityOptions, 1045 shortcut.getUserId()); 1046 } 1047 1048 private void startShortcut(@NonNull String packageName, @NonNull String shortcutId, 1049 @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, 1050 int userId) { 1051 try { 1052 final boolean success = 1053 mService.startShortcut(mContext.getPackageName(), packageName, shortcutId, 1054 sourceBounds, startActivityOptions, userId); 1055 if (!success) { 1056 throw new ActivityNotFoundException("Shortcut could not be started"); 1057 } 1058 } catch (RemoteException e) { 1059 throw e.rethrowFromSystemServer(); 1060 } 1061 } 1062 1063 /** 1064 * Registers a callback for changes to packages in current and managed profiles. 1065 * 1066 * @param callback The callback to register. 1067 */ 1068 public void registerCallback(Callback callback) { 1069 registerCallback(callback, null); 1070 } 1071 1072 /** 1073 * Registers a callback for changes to packages in current and managed profiles. 1074 * 1075 * @param callback The callback to register. 1076 * @param handler that should be used to post callbacks on, may be null. 1077 */ 1078 public void registerCallback(Callback callback, Handler handler) { 1079 synchronized (this) { 1080 if (callback != null && findCallbackLocked(callback) < 0) { 1081 boolean addedFirstCallback = mCallbacks.size() == 0; 1082 addCallbackLocked(callback, handler); 1083 if (addedFirstCallback) { 1084 try { 1085 mService.addOnAppsChangedListener(mContext.getPackageName(), 1086 mAppsChangedListener); 1087 } catch (RemoteException re) { 1088 throw re.rethrowFromSystemServer(); 1089 } 1090 } 1091 } 1092 } 1093 } 1094 1095 /** 1096 * Unregisters a callback that was previously registered. 1097 * 1098 * @param callback The callback to unregister. 1099 * @see #registerCallback(Callback) 1100 */ 1101 public void unregisterCallback(Callback callback) { 1102 synchronized (this) { 1103 removeCallbackLocked(callback); 1104 if (mCallbacks.size() == 0) { 1105 try { 1106 mService.removeOnAppsChangedListener(mAppsChangedListener); 1107 } catch (RemoteException re) { 1108 throw re.rethrowFromSystemServer(); 1109 } 1110 } 1111 } 1112 } 1113 1114 /** @return position in mCallbacks for callback or -1 if not present. */ 1115 private int findCallbackLocked(Callback callback) { 1116 if (callback == null) { 1117 throw new IllegalArgumentException("Callback cannot be null"); 1118 } 1119 final int size = mCallbacks.size(); 1120 for (int i = 0; i < size; ++i) { 1121 if (mCallbacks.get(i).mCallback == callback) { 1122 return i; 1123 } 1124 } 1125 return -1; 1126 } 1127 1128 private void removeCallbackLocked(Callback callback) { 1129 int pos = findCallbackLocked(callback); 1130 if (pos >= 0) { 1131 mCallbacks.remove(pos); 1132 } 1133 } 1134 1135 private void addCallbackLocked(Callback callback, Handler handler) { 1136 // Remove if already present. 1137 removeCallbackLocked(callback); 1138 if (handler == null) { 1139 handler = new Handler(); 1140 } 1141 CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback); 1142 mCallbacks.add(toAdd); 1143 } 1144 1145 private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() { 1146 1147 @Override 1148 public void onPackageRemoved(UserHandle user, String packageName) 1149 throws RemoteException { 1150 if (DEBUG) { 1151 Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName); 1152 } 1153 synchronized (LauncherApps.this) { 1154 for (CallbackMessageHandler callback : mCallbacks) { 1155 callback.postOnPackageRemoved(packageName, user); 1156 } 1157 } 1158 } 1159 1160 @Override 1161 public void onPackageChanged(UserHandle user, String packageName) throws RemoteException { 1162 if (DEBUG) { 1163 Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName); 1164 } 1165 synchronized (LauncherApps.this) { 1166 for (CallbackMessageHandler callback : mCallbacks) { 1167 callback.postOnPackageChanged(packageName, user); 1168 } 1169 } 1170 } 1171 1172 @Override 1173 public void onPackageAdded(UserHandle user, String packageName) throws RemoteException { 1174 if (DEBUG) { 1175 Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName); 1176 } 1177 synchronized (LauncherApps.this) { 1178 for (CallbackMessageHandler callback : mCallbacks) { 1179 callback.postOnPackageAdded(packageName, user); 1180 } 1181 } 1182 } 1183 1184 @Override 1185 public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing) 1186 throws RemoteException { 1187 if (DEBUG) { 1188 Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames); 1189 } 1190 synchronized (LauncherApps.this) { 1191 for (CallbackMessageHandler callback : mCallbacks) { 1192 callback.postOnPackagesAvailable(packageNames, user, replacing); 1193 } 1194 } 1195 } 1196 1197 @Override 1198 public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing) 1199 throws RemoteException { 1200 if (DEBUG) { 1201 Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames); 1202 } 1203 synchronized (LauncherApps.this) { 1204 for (CallbackMessageHandler callback : mCallbacks) { 1205 callback.postOnPackagesUnavailable(packageNames, user, replacing); 1206 } 1207 } 1208 } 1209 1210 @Override 1211 public void onPackagesSuspended(UserHandle user, String[] packageNames, 1212 Bundle launcherExtras) 1213 throws RemoteException { 1214 if (DEBUG) { 1215 Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames); 1216 } 1217 synchronized (LauncherApps.this) { 1218 for (CallbackMessageHandler callback : mCallbacks) { 1219 callback.postOnPackagesSuspended(packageNames, launcherExtras, user); 1220 } 1221 } 1222 } 1223 1224 @Override 1225 public void onPackagesUnsuspended(UserHandle user, String[] packageNames) 1226 throws RemoteException { 1227 if (DEBUG) { 1228 Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames); 1229 } 1230 synchronized (LauncherApps.this) { 1231 for (CallbackMessageHandler callback : mCallbacks) { 1232 callback.postOnPackagesUnsuspended(packageNames, user); 1233 } 1234 } 1235 } 1236 1237 @Override 1238 public void onShortcutChanged(UserHandle user, String packageName, 1239 ParceledListSlice shortcuts) { 1240 if (DEBUG) { 1241 Log.d(TAG, "onShortcutChanged " + user.getIdentifier() + "," + packageName); 1242 } 1243 final List<ShortcutInfo> list = shortcuts.getList(); 1244 synchronized (LauncherApps.this) { 1245 for (CallbackMessageHandler callback : mCallbacks) { 1246 callback.postOnShortcutChanged(packageName, user, list); 1247 } 1248 } 1249 } 1250 }; 1251 1252 private static class CallbackMessageHandler extends Handler { 1253 private static final int MSG_ADDED = 1; 1254 private static final int MSG_REMOVED = 2; 1255 private static final int MSG_CHANGED = 3; 1256 private static final int MSG_AVAILABLE = 4; 1257 private static final int MSG_UNAVAILABLE = 5; 1258 private static final int MSG_SUSPENDED = 6; 1259 private static final int MSG_UNSUSPENDED = 7; 1260 private static final int MSG_SHORTCUT_CHANGED = 8; 1261 1262 private LauncherApps.Callback mCallback; 1263 1264 private static class CallbackInfo { 1265 String[] packageNames; 1266 String packageName; 1267 Bundle launcherExtras; 1268 boolean replacing; 1269 UserHandle user; 1270 List<ShortcutInfo> shortcuts; 1271 } 1272 1273 public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) { 1274 super(looper, null, true); 1275 mCallback = callback; 1276 } 1277 1278 @Override 1279 public void handleMessage(Message msg) { 1280 if (mCallback == null || !(msg.obj instanceof CallbackInfo)) { 1281 return; 1282 } 1283 CallbackInfo info = (CallbackInfo) msg.obj; 1284 switch (msg.what) { 1285 case MSG_ADDED: 1286 mCallback.onPackageAdded(info.packageName, info.user); 1287 break; 1288 case MSG_REMOVED: 1289 mCallback.onPackageRemoved(info.packageName, info.user); 1290 break; 1291 case MSG_CHANGED: 1292 mCallback.onPackageChanged(info.packageName, info.user); 1293 break; 1294 case MSG_AVAILABLE: 1295 mCallback.onPackagesAvailable(info.packageNames, info.user, info.replacing); 1296 break; 1297 case MSG_UNAVAILABLE: 1298 mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing); 1299 break; 1300 case MSG_SUSPENDED: 1301 mCallback.onPackagesSuspended(info.packageNames, info.launcherExtras, 1302 info.user); 1303 break; 1304 case MSG_UNSUSPENDED: 1305 mCallback.onPackagesUnsuspended(info.packageNames, info.user); 1306 break; 1307 case MSG_SHORTCUT_CHANGED: 1308 mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user); 1309 break; 1310 } 1311 } 1312 1313 public void postOnPackageAdded(String packageName, UserHandle user) { 1314 CallbackInfo info = new CallbackInfo(); 1315 info.packageName = packageName; 1316 info.user = user; 1317 obtainMessage(MSG_ADDED, info).sendToTarget(); 1318 } 1319 1320 public void postOnPackageRemoved(String packageName, UserHandle user) { 1321 CallbackInfo info = new CallbackInfo(); 1322 info.packageName = packageName; 1323 info.user = user; 1324 obtainMessage(MSG_REMOVED, info).sendToTarget(); 1325 } 1326 1327 public void postOnPackageChanged(String packageName, UserHandle user) { 1328 CallbackInfo info = new CallbackInfo(); 1329 info.packageName = packageName; 1330 info.user = user; 1331 obtainMessage(MSG_CHANGED, info).sendToTarget(); 1332 } 1333 1334 public void postOnPackagesAvailable(String[] packageNames, UserHandle user, 1335 boolean replacing) { 1336 CallbackInfo info = new CallbackInfo(); 1337 info.packageNames = packageNames; 1338 info.replacing = replacing; 1339 info.user = user; 1340 obtainMessage(MSG_AVAILABLE, info).sendToTarget(); 1341 } 1342 1343 public void postOnPackagesUnavailable(String[] packageNames, UserHandle user, 1344 boolean replacing) { 1345 CallbackInfo info = new CallbackInfo(); 1346 info.packageNames = packageNames; 1347 info.replacing = replacing; 1348 info.user = user; 1349 obtainMessage(MSG_UNAVAILABLE, info).sendToTarget(); 1350 } 1351 1352 public void postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras, 1353 UserHandle user) { 1354 CallbackInfo info = new CallbackInfo(); 1355 info.packageNames = packageNames; 1356 info.user = user; 1357 info.launcherExtras = launcherExtras; 1358 obtainMessage(MSG_SUSPENDED, info).sendToTarget(); 1359 } 1360 1361 public void postOnPackagesUnsuspended(String[] packageNames, UserHandle user) { 1362 CallbackInfo info = new CallbackInfo(); 1363 info.packageNames = packageNames; 1364 info.user = user; 1365 obtainMessage(MSG_UNSUSPENDED, info).sendToTarget(); 1366 } 1367 1368 public void postOnShortcutChanged(String packageName, UserHandle user, 1369 List<ShortcutInfo> shortcuts) { 1370 CallbackInfo info = new CallbackInfo(); 1371 info.packageName = packageName; 1372 info.user = user; 1373 info.shortcuts = shortcuts; 1374 obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget(); 1375 } 1376 } 1377 1378 /** 1379 * A helper method to extract a {@link PinItemRequest} set to 1380 * the {@link #EXTRA_PIN_ITEM_REQUEST} extra. 1381 */ 1382 public PinItemRequest getPinItemRequest(Intent intent) { 1383 return intent.getParcelableExtra(EXTRA_PIN_ITEM_REQUEST); 1384 } 1385 1386 /** 1387 * Represents a "pin shortcut" or a "pin appwidget" request made by an app, which is sent with 1388 * an {@link #ACTION_CONFIRM_PIN_SHORTCUT} or {@link #ACTION_CONFIRM_PIN_APPWIDGET} intent 1389 * respectively to the default launcher app. 1390 * 1391 * <h3>Request of the {@link #REQUEST_TYPE_SHORTCUT} type. 1392 * 1393 * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a 1394 * {@link ShortcutInfo}. If the launcher accepts a request, call {@link #accept()}, 1395 * or {@link #accept(Bundle)} with a null or empty Bundle. No options are defined for 1396 * pin-shortcuts requests. 1397 * 1398 * <p>{@link #getShortcutInfo()} always returns a non-null {@link ShortcutInfo} for this type. 1399 * 1400 * <p>The launcher may receive a request with a {@link ShortcutInfo} that is already pinned, in 1401 * which case {@link ShortcutInfo#isPinned()} returns true. This means the user wants to create 1402 * another pinned shortcut for a shortcut that's already pinned. If the launcher accepts it, 1403 * {@link #accept()} must still be called even though the shortcut is already pinned, and 1404 * create a new pinned shortcut icon for it. 1405 * 1406 * <p>See also {@link ShortcutManager} for more details. 1407 * 1408 * <h3>Request of the {@link #REQUEST_TYPE_APPWIDGET} type. 1409 * 1410 * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a 1411 * an AppWidget. If the launcher accepts a request, call {@link #accept(Bundle)} with 1412 * the appwidget integer ID set to the 1413 * {@link android.appwidget.AppWidgetManager#EXTRA_APPWIDGET_ID} extra. 1414 * 1415 * <p>{@link #getAppWidgetProviderInfo(Context)} always returns a non-null 1416 * {@link AppWidgetProviderInfo} for this type. 1417 * 1418 * <p>See also {@link AppWidgetManager} for more details. 1419 * 1420 * @see #EXTRA_PIN_ITEM_REQUEST 1421 * @see #getPinItemRequest(Intent) 1422 */ 1423 public static final class PinItemRequest implements Parcelable { 1424 1425 /** This is a request to pin shortcut. */ 1426 public static final int REQUEST_TYPE_SHORTCUT = 1; 1427 1428 /** This is a request to pin app widget. */ 1429 public static final int REQUEST_TYPE_APPWIDGET = 2; 1430 1431 /** @hide */ 1432 @IntDef(prefix = { "REQUEST_TYPE_" }, value = { 1433 REQUEST_TYPE_SHORTCUT, 1434 REQUEST_TYPE_APPWIDGET 1435 }) 1436 @Retention(RetentionPolicy.SOURCE) 1437 public @interface RequestType {} 1438 1439 private final int mRequestType; 1440 private final IPinItemRequest mInner; 1441 1442 /** 1443 * @hide 1444 */ 1445 public PinItemRequest(IPinItemRequest inner, int type) { 1446 mInner = inner; 1447 mRequestType = type; 1448 } 1449 1450 /** 1451 * Represents the type of a request, which is one of the {@code REQUEST_TYPE_} constants. 1452 * 1453 * @return one of the {@code REQUEST_TYPE_} constants. 1454 */ 1455 @RequestType 1456 public int getRequestType() { 1457 return mRequestType; 1458 } 1459 1460 /** 1461 * {@link ShortcutInfo} sent by the requesting app. 1462 * Always non-null for a {@link #REQUEST_TYPE_SHORTCUT} request, and always null for a 1463 * different request type. 1464 * 1465 * @return requested {@link ShortcutInfo} when a request is of the 1466 * {@link #REQUEST_TYPE_SHORTCUT} type. Null otherwise. 1467 */ 1468 @Nullable 1469 public ShortcutInfo getShortcutInfo() { 1470 try { 1471 return mInner.getShortcutInfo(); 1472 } catch (RemoteException e) { 1473 throw e.rethrowAsRuntimeException(); 1474 } 1475 } 1476 1477 /** 1478 * {@link AppWidgetProviderInfo} sent by the requesting app. 1479 * Always non-null for a {@link #REQUEST_TYPE_APPWIDGET} request, and always null for a 1480 * different request type. 1481 * 1482 * <p>Launcher should not show any configuration activity associated with the provider, and 1483 * assume that the widget is already fully configured. Upon accepting the widget, it should 1484 * pass the widgetId in {@link #accept(Bundle)}. 1485 * 1486 * @return requested {@link AppWidgetProviderInfo} when a request is of the 1487 * {@link #REQUEST_TYPE_APPWIDGET} type. Null otherwise. 1488 */ 1489 @Nullable 1490 public AppWidgetProviderInfo getAppWidgetProviderInfo(Context context) { 1491 try { 1492 final AppWidgetProviderInfo info = mInner.getAppWidgetProviderInfo(); 1493 if (info == null) { 1494 return null; 1495 } 1496 info.updateDimensions(context.getResources().getDisplayMetrics()); 1497 return info; 1498 } catch (RemoteException e) { 1499 throw e.rethrowAsRuntimeException(); 1500 } 1501 } 1502 1503 /** 1504 * Any extras sent by the requesting app. 1505 * 1506 * @return For a shortcut request, this method always return null. For an AppWidget 1507 * request, this method returns the extras passed to the 1508 * {@link android.appwidget.AppWidgetManager#requestPinAppWidget( 1509 * ComponentName, Bundle, PendingIntent)} API. See {@link AppWidgetManager} for details. 1510 */ 1511 @Nullable 1512 public Bundle getExtras() { 1513 try { 1514 return mInner.getExtras(); 1515 } catch (RemoteException e) { 1516 throw e.rethrowAsRuntimeException(); 1517 } 1518 } 1519 1520 /** 1521 * Return whether a request is still valid. 1522 * 1523 * @return {@code TRUE} if a request is valid and {@link #accept(Bundle)} may be called. 1524 */ 1525 public boolean isValid() { 1526 try { 1527 return mInner.isValid(); 1528 } catch (RemoteException e) { 1529 return false; 1530 } 1531 } 1532 1533 /** 1534 * Called by the receiving launcher app when the user accepts the request. 1535 * 1536 * @param options must be set for a {@link #REQUEST_TYPE_APPWIDGET} request. 1537 * 1538 * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned. 1539 * {@code FALSE} if the item hasn't been pinned, for example, because the request had 1540 * already been canceled, in which case the launcher must not pin the requested item. 1541 */ 1542 public boolean accept(@Nullable Bundle options) { 1543 try { 1544 return mInner.accept(options); 1545 } catch (RemoteException e) { 1546 throw e.rethrowFromSystemServer(); 1547 } 1548 } 1549 1550 /** 1551 * Called by the receiving launcher app when the user accepts the request, with no options. 1552 * 1553 * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned. 1554 * {@code FALSE} if the item hasn't been pinned, for example, because the request had 1555 * already been canceled, in which case the launcher must not pin the requested item. 1556 */ 1557 public boolean accept() { 1558 return accept(/* options= */ null); 1559 } 1560 1561 private PinItemRequest(Parcel source) { 1562 final ClassLoader cl = getClass().getClassLoader(); 1563 1564 mRequestType = source.readInt(); 1565 mInner = IPinItemRequest.Stub.asInterface(source.readStrongBinder()); 1566 } 1567 1568 @Override 1569 public void writeToParcel(Parcel dest, int flags) { 1570 dest.writeInt(mRequestType); 1571 dest.writeStrongBinder(mInner.asBinder()); 1572 } 1573 1574 public static final Creator<PinItemRequest> CREATOR = 1575 new Creator<PinItemRequest>() { 1576 public PinItemRequest createFromParcel(Parcel source) { 1577 return new PinItemRequest(source); 1578 } 1579 public PinItemRequest[] newArray(int size) { 1580 return new PinItemRequest[size]; 1581 } 1582 }; 1583 1584 @Override 1585 public int describeContents() { 1586 return 0; 1587 } 1588 } 1589} 1590