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