1 /* 2 * Copyright (C) 2016 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 package android.content.pm; 17 18 import android.Manifest; 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.annotation.SystemService; 25 import android.annotation.TestApi; 26 import android.annotation.UserIdInt; 27 import android.annotation.WorkerThread; 28 import android.app.Notification; 29 import android.app.usage.UsageStatsManager; 30 import android.compat.annotation.UnsupportedAppUsage; 31 import android.content.ComponentName; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.content.IntentSender; 36 import android.graphics.drawable.AdaptiveIconDrawable; 37 import android.os.Build; 38 import android.os.Build.VERSION_CODES; 39 import android.os.Parcel; 40 import android.os.Parcelable; 41 import android.os.RemoteException; 42 import android.os.ServiceManager; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.infra.AndroidFuture; 46 47 import java.lang.annotation.Retention; 48 import java.lang.annotation.RetentionPolicy; 49 import java.util.List; 50 import java.util.concurrent.ExecutionException; 51 52 /** 53 * <p><code>ShortcutManager</code> executes operations on an app's set of <i>shortcuts</i>, which 54 * represent specific tasks and actions that users can perform within your app. This page lists 55 * components of the <code>ShortcutManager</code> class that you can use to create and manage 56 * sets of shortcuts. 57 * 58 * <p>To learn about methods that retrieve information about a single shortcut—including 59 * identifiers, type, and status—read the <code> 60 * <a href="/reference/android/content/pm/ShortcutInfo.html">ShortcutInfo</a></code> reference. 61 * 62 * <p>For guidance about using shortcuts, see 63 * <a href="/guide/topics/ui/shortcuts/index.html">App shortcuts</a>. 64 * 65 * <h3>Retrieving class instances</h3> 66 * <!-- Provides a heading for the content filled in by the @SystemService annotation below --> 67 */ 68 @SystemService(Context.SHORTCUT_SERVICE) 69 public class ShortcutManager { 70 private static final String TAG = "ShortcutManager"; 71 72 /** 73 * Include manifest shortcuts in the result. 74 * 75 * @see #getShortcuts(int) 76 */ 77 public static final int FLAG_MATCH_MANIFEST = 1 << 0; 78 79 /** 80 * Include dynamic shortcuts in the result. 81 * 82 * @see #getShortcuts(int) 83 */ 84 public static final int FLAG_MATCH_DYNAMIC = 1 << 1; 85 86 /** 87 * Include pinned shortcuts in the result. 88 * 89 * @see #getShortcuts(int) 90 */ 91 public static final int FLAG_MATCH_PINNED = 1 << 2; 92 93 /** 94 * Include cached shortcuts in the result. 95 * 96 * @see #getShortcuts(int) 97 */ 98 public static final int FLAG_MATCH_CACHED = 1 << 3; 99 100 /** @hide */ 101 @IntDef(flag = true, prefix = { "FLAG_MATCH_" }, value = { 102 FLAG_MATCH_MANIFEST, 103 FLAG_MATCH_DYNAMIC, 104 FLAG_MATCH_PINNED, 105 FLAG_MATCH_CACHED, 106 }) 107 @Retention(RetentionPolicy.SOURCE) 108 public @interface ShortcutMatchFlags {} 109 110 private final Context mContext; 111 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 112 private final IShortcutService mService; 113 114 /** 115 * @hide 116 */ ShortcutManager(Context context, IShortcutService service)117 public ShortcutManager(Context context, IShortcutService service) { 118 mContext = context; 119 mService = service; 120 } 121 122 /** 123 * @hide 124 */ 125 @TestApi ShortcutManager(Context context)126 public ShortcutManager(Context context) { 127 this(context, IShortcutService.Stub.asInterface( 128 ServiceManager.getService(Context.SHORTCUT_SERVICE))); 129 } 130 131 /** 132 * Publish the list of shortcuts. All existing dynamic shortcuts from the caller app 133 * will be replaced. If there are already pinned shortcuts with the same IDs, 134 * the mutable pinned shortcuts are updated. 135 * 136 * <p>This API will be rate-limited. 137 * 138 * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. 139 * 140 * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded, 141 * or when trying to update immutable shortcuts. 142 * 143 * @throws IllegalStateException when the user is locked. 144 */ 145 @WorkerThread setDynamicShortcuts(@onNull List<ShortcutInfo> shortcutInfoList)146 public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { 147 try { 148 return mService.setDynamicShortcuts(mContext.getPackageName(), new ParceledListSlice( 149 shortcutInfoList), injectMyUserId()); 150 } catch (RemoteException e) { 151 throw e.rethrowFromSystemServer(); 152 } 153 } 154 155 /** 156 * Return all dynamic shortcuts from the caller app. 157 * 158 * <p>This API is intended to be used for examining what shortcuts are currently published. 159 * Re-publishing returned {@link ShortcutInfo}s via APIs such as 160 * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons. 161 * 162 * @throws IllegalStateException when the user is locked. 163 */ 164 @WorkerThread 165 @NonNull getDynamicShortcuts()166 public List<ShortcutInfo> getDynamicShortcuts() { 167 try { 168 return mService.getShortcuts(mContext.getPackageName(), 169 FLAG_MATCH_DYNAMIC, injectMyUserId()).getList(); 170 } catch (RemoteException e) { 171 throw e.rethrowFromSystemServer(); 172 } 173 } 174 175 /** 176 * Return all static (manifest) shortcuts from the caller app. 177 * 178 * <p>This API is intended to be used for examining what shortcuts are currently published. 179 * Re-publishing returned {@link ShortcutInfo}s via APIs such as 180 * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons. 181 * 182 * @throws IllegalStateException when the user is locked. 183 */ 184 @WorkerThread 185 @NonNull getManifestShortcuts()186 public List<ShortcutInfo> getManifestShortcuts() { 187 try { 188 return mService.getShortcuts(mContext.getPackageName(), 189 FLAG_MATCH_MANIFEST, injectMyUserId()).getList(); 190 } catch (RemoteException e) { 191 throw e.rethrowFromSystemServer(); 192 } 193 } 194 195 /** 196 * Returns {@link ShortcutInfo}s that match {@code matchFlags}. 197 * 198 * @param matchFlags result includes shortcuts matching this flags. Any combination of: 199 * <ul> 200 * <li>{@link #FLAG_MATCH_MANIFEST} 201 * <li>{@link #FLAG_MATCH_DYNAMIC} 202 * <li>{@link #FLAG_MATCH_PINNED} 203 * <li>{@link #FLAG_MATCH_CACHED} 204 * </ul> 205 206 * @return list of {@link ShortcutInfo}s that match the flag. 207 * 208 * <p>At least one of the {@code MATCH} flags should be set. Otherwise no shortcuts will be 209 * returned. 210 * 211 * @throws IllegalStateException when the user is locked. 212 */ 213 @WorkerThread 214 @NonNull getShortcuts(@hortcutMatchFlags int matchFlags)215 public List<ShortcutInfo> getShortcuts(@ShortcutMatchFlags int matchFlags) { 216 try { 217 return mService.getShortcuts(mContext.getPackageName(), matchFlags, 218 injectMyUserId()).getList(); 219 } catch (RemoteException e) { 220 throw e.rethrowFromSystemServer(); 221 } 222 } 223 224 /** 225 * Publish the list of dynamic shortcuts. If there are already dynamic or pinned shortcuts with 226 * the same IDs, each mutable shortcut is updated. 227 * 228 * <p>This API will be rate-limited. 229 * 230 * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. 231 * 232 * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded, 233 * or when trying to update immutable shortcuts. 234 * 235 * @throws IllegalStateException when the user is locked. 236 */ 237 @WorkerThread addDynamicShortcuts(@onNull List<ShortcutInfo> shortcutInfoList)238 public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { 239 try { 240 return mService.addDynamicShortcuts(mContext.getPackageName(), 241 new ParceledListSlice(shortcutInfoList), injectMyUserId()); 242 } catch (RemoteException e) { 243 throw e.rethrowFromSystemServer(); 244 } 245 } 246 247 /** 248 * Delete dynamic shortcuts by ID. 249 * 250 * @throws IllegalStateException when the user is locked. 251 */ removeDynamicShortcuts(@onNull List<String> shortcutIds)252 public void removeDynamicShortcuts(@NonNull List<String> shortcutIds) { 253 try { 254 mService.removeDynamicShortcuts(mContext.getPackageName(), shortcutIds, 255 injectMyUserId()); 256 } catch (RemoteException e) { 257 throw e.rethrowFromSystemServer(); 258 } 259 } 260 261 /** 262 * Delete all dynamic shortcuts from the caller app. 263 * 264 * @throws IllegalStateException when the user is locked. 265 */ removeAllDynamicShortcuts()266 public void removeAllDynamicShortcuts() { 267 try { 268 mService.removeAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId()); 269 } catch (RemoteException e) { 270 throw e.rethrowFromSystemServer(); 271 } 272 } 273 274 /** 275 * Delete long lived shortcuts by ID. 276 * 277 * @throws IllegalStateException when the user is locked. 278 */ removeLongLivedShortcuts(@onNull List<String> shortcutIds)279 public void removeLongLivedShortcuts(@NonNull List<String> shortcutIds) { 280 try { 281 mService.removeLongLivedShortcuts(mContext.getPackageName(), shortcutIds, 282 injectMyUserId()); 283 } catch (RemoteException e) { 284 throw e.rethrowFromSystemServer(); 285 } 286 } 287 288 /** 289 * Return all pinned shortcuts from the caller app. 290 * 291 * <p>This API is intended to be used for examining what shortcuts are currently published. 292 * Re-publishing returned {@link ShortcutInfo}s via APIs such as 293 * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons. 294 * 295 * @throws IllegalStateException when the user is locked. 296 */ 297 @WorkerThread 298 @NonNull getPinnedShortcuts()299 public List<ShortcutInfo> getPinnedShortcuts() { 300 try { 301 return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_PINNED, 302 injectMyUserId()).getList(); 303 } catch (RemoteException e) { 304 throw e.rethrowFromSystemServer(); 305 } 306 } 307 308 /** 309 * Update all existing shortcuts with the same IDs. Target shortcuts may be pinned and/or 310 * dynamic, but they must not be immutable. 311 * 312 * <p>This API will be rate-limited. 313 * 314 * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. 315 * 316 * @throws IllegalArgumentException If trying to update immutable shortcuts. 317 * 318 * @throws IllegalStateException when the user is locked. 319 */ 320 @WorkerThread updateShortcuts(@onNull List<ShortcutInfo> shortcutInfoList)321 public boolean updateShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { 322 try { 323 return mService.updateShortcuts(mContext.getPackageName(), 324 new ParceledListSlice(shortcutInfoList), injectMyUserId()); 325 } catch (RemoteException e) { 326 throw e.rethrowFromSystemServer(); 327 } 328 } 329 330 /** 331 * Disable pinned shortcuts. For more details, read 332 * <a href="/guide/topics/ui/shortcuts/managing-shortcuts.html#disable-shortcuts"> 333 * Disable shortcuts</a>. 334 * 335 * @throws IllegalArgumentException If trying to disable immutable shortcuts. 336 * 337 * @throws IllegalStateException when the user is locked. 338 */ disableShortcuts(@onNull List<String> shortcutIds)339 public void disableShortcuts(@NonNull List<String> shortcutIds) { 340 try { 341 mService.disableShortcuts(mContext.getPackageName(), shortcutIds, 342 /* disabledMessage =*/ null, /* disabledMessageResId =*/ 0, 343 injectMyUserId()); 344 } catch (RemoteException e) { 345 throw e.rethrowFromSystemServer(); 346 } 347 } 348 349 /** 350 * @hide old signature, kept for unit testing. 351 */ disableShortcuts(@onNull List<String> shortcutIds, int disabledMessageResId)352 public void disableShortcuts(@NonNull List<String> shortcutIds, int disabledMessageResId) { 353 try { 354 mService.disableShortcuts(mContext.getPackageName(), shortcutIds, 355 /* disabledMessage =*/ null, disabledMessageResId, 356 injectMyUserId()); 357 } catch (RemoteException e) { 358 throw e.rethrowFromSystemServer(); 359 } 360 } 361 362 /** 363 * @hide old signature, kept for unit testing. 364 */ disableShortcuts(@onNull List<String> shortcutIds, String disabledMessage)365 public void disableShortcuts(@NonNull List<String> shortcutIds, String disabledMessage) { 366 disableShortcuts(shortcutIds, (CharSequence) disabledMessage); 367 } 368 369 /** 370 * Disable pinned shortcuts, showing the user a custom error message when they try to select 371 * the disabled shortcuts. 372 * For more details, read 373 * <a href="/guide/topics/ui/shortcuts/managing-shortcuts.html#disable-shortcuts"> 374 * Disable shortcuts</a>. 375 * 376 * @throws IllegalArgumentException If trying to disable immutable shortcuts. 377 * 378 * @throws IllegalStateException when the user is locked. 379 */ disableShortcuts(@onNull List<String> shortcutIds, CharSequence disabledMessage)380 public void disableShortcuts(@NonNull List<String> shortcutIds, CharSequence disabledMessage) { 381 try { 382 mService.disableShortcuts(mContext.getPackageName(), shortcutIds, 383 disabledMessage, /* disabledMessageResId =*/ 0, 384 injectMyUserId()); 385 } catch (RemoteException e) { 386 throw e.rethrowFromSystemServer(); 387 } 388 } 389 390 /** 391 * Re-enable pinned shortcuts that were previously disabled. If the target shortcuts 392 * are already enabled, this method does nothing. 393 * 394 * @throws IllegalArgumentException If trying to enable immutable shortcuts. 395 * 396 * @throws IllegalStateException when the user is locked. 397 */ enableShortcuts(@onNull List<String> shortcutIds)398 public void enableShortcuts(@NonNull List<String> shortcutIds) { 399 try { 400 mService.enableShortcuts(mContext.getPackageName(), shortcutIds, injectMyUserId()); 401 } catch (RemoteException e) { 402 throw e.rethrowFromSystemServer(); 403 } 404 } 405 406 407 /** 408 * @hide old signature, kept for unit testing. 409 */ getMaxShortcutCountForActivity()410 public int getMaxShortcutCountForActivity() { 411 return getMaxShortcutCountPerActivity(); 412 } 413 414 /** 415 * Return the maximum number of static and dynamic shortcuts that each launcher icon 416 * can have at a time. 417 */ getMaxShortcutCountPerActivity()418 public int getMaxShortcutCountPerActivity() { 419 try { 420 return mService.getMaxShortcutCountPerActivity( 421 mContext.getPackageName(), injectMyUserId()); 422 } catch (RemoteException e) { 423 throw e.rethrowFromSystemServer(); 424 } 425 } 426 427 /** 428 * Return the number of times the caller app can call the rate-limited APIs 429 * before the rate limit counter is reset. 430 * 431 * @see #getRateLimitResetTime() 432 * 433 * @hide 434 */ getRemainingCallCount()435 public int getRemainingCallCount() { 436 try { 437 return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId()); 438 } catch (RemoteException e) { 439 throw e.rethrowFromSystemServer(); 440 } 441 } 442 443 /** 444 * Return when the rate limit count will be reset next time, in milliseconds since the epoch. 445 * 446 * @see #getRemainingCallCount() 447 * @see System#currentTimeMillis() 448 * 449 * @hide 450 */ getRateLimitResetTime()451 public long getRateLimitResetTime() { 452 try { 453 return mService.getRateLimitResetTime(mContext.getPackageName(), injectMyUserId()); 454 } catch (RemoteException e) { 455 throw e.rethrowFromSystemServer(); 456 } 457 } 458 459 /** 460 * Return {@code true} when rate-limiting is active for the caller app. 461 * 462 * <p>For details, see <a href="/guide/topics/ui/shortcuts/managing-shortcuts#rate-limiting"> 463 * Rate limiting</a>. 464 * 465 * @throws IllegalStateException when the user is locked. 466 */ isRateLimitingActive()467 public boolean isRateLimitingActive() { 468 try { 469 return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId()) 470 == 0; 471 } catch (RemoteException e) { 472 throw e.rethrowFromSystemServer(); 473 } 474 } 475 476 /** 477 * Return the max width for icons, in pixels. 478 * 479 * <p> Note that this method returns max width of icon's visible part. Hence, it does not take 480 * into account the inset introduced by {@link AdaptiveIconDrawable}. To calculate bitmap image 481 * to function as {@link AdaptiveIconDrawable}, multiply 482 * 1 + 2 * {@link AdaptiveIconDrawable#getExtraInsetFraction()} to the returned size. 483 */ getIconMaxWidth()484 public int getIconMaxWidth() { 485 try { 486 // TODO Implement it properly using xdpi. 487 return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId()); 488 } catch (RemoteException e) { 489 throw e.rethrowFromSystemServer(); 490 } 491 } 492 493 /** 494 * Return the max height for icons, in pixels. 495 */ getIconMaxHeight()496 public int getIconMaxHeight() { 497 try { 498 // TODO Implement it properly using ydpi. 499 return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId()); 500 } catch (RemoteException e) { 501 throw e.rethrowFromSystemServer(); 502 } 503 } 504 505 /** 506 * Apps that publish shortcuts should call this method whenever the user 507 * selects the shortcut containing the given ID or when the user completes 508 * an action in the app that is equivalent to selecting the shortcut. 509 * For more details, read about 510 * <a href="/guide/topics/ui/shortcuts/managing-shortcuts.html#track-usage"> 511 * tracking shortcut usage</a>. 512 * 513 * <p>The information is accessible via {@link UsageStatsManager#queryEvents} 514 * Typically, launcher apps use this information to build a prediction model 515 * so that they can promote the shortcuts that are likely to be used at the moment. 516 * 517 * @throws IllegalStateException when the user is locked. 518 */ reportShortcutUsed(String shortcutId)519 public void reportShortcutUsed(String shortcutId) { 520 try { 521 mService.reportShortcutUsed(mContext.getPackageName(), shortcutId, injectMyUserId()); 522 } catch (RemoteException e) { 523 throw e.rethrowFromSystemServer(); 524 } 525 } 526 527 /** 528 * Return {@code TRUE} if the app is running on a device whose default launcher supports 529 * {@link #requestPinShortcut(ShortcutInfo, IntentSender)}. 530 * 531 * <p>The return value may change in subsequent calls if the user changes the default launcher 532 * app. 533 * 534 * <p><b>Note:</b> See also the support library counterpart 535 * {@link androidx.core.content.pm.ShortcutManagerCompat#isRequestPinShortcutSupported( 536 * Context)}, which supports Android versions lower than {@link VERSION_CODES#O} using the 537 * legacy private intent {@code com.android.launcher.action.INSTALL_SHORTCUT}. 538 * 539 * @see #requestPinShortcut(ShortcutInfo, IntentSender) 540 */ isRequestPinShortcutSupported()541 public boolean isRequestPinShortcutSupported() { 542 try { 543 return mService.isRequestPinItemSupported(injectMyUserId(), 544 LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT); 545 } catch (RemoteException e) { 546 throw e.rethrowFromSystemServer(); 547 } 548 } 549 550 /** 551 * Request to create a pinned shortcut. The default launcher will receive this request and 552 * ask the user for approval. If the user approves it, the shortcut will be created, and 553 * {@code resultIntent} will be sent. If a request is denied by the user, however, no response 554 * will be sent to the caller. 555 * 556 * <p>Only apps with a foreground activity or a foreground service can call this method. 557 * Otherwise, it'll throw {@link IllegalStateException}. 558 * 559 * <p>It's up to the launcher to decide how to handle previous pending requests when the same 560 * package calls this API multiple times in a row. One possible strategy is to ignore any 561 * previous requests. 562 * 563 * <p><b>Note:</b> See also the support library counterpart 564 * {@link androidx.core.content.pm.ShortcutManagerCompat#requestPinShortcut( 565 * Context, ShortcutInfoCompat, IntentSender)}, 566 * which supports Android versions lower than {@link VERSION_CODES#O} using the 567 * legacy private intent {@code com.android.launcher.action.INSTALL_SHORTCUT}. 568 * 569 * @param shortcut Shortcut to pin. If an app wants to pin an existing (either static 570 * or dynamic) shortcut, then it only needs to have an ID. Although other fields don't have 571 * to be set, the target shortcut must be enabled. 572 * 573 * <p>If it's a new shortcut, all the mandatory fields, such as a short label, must be 574 * set. 575 * @param resultIntent If not null, this intent will be sent when the shortcut is pinned. 576 * Use {@link android.app.PendingIntent#getIntentSender()} to create an {@link IntentSender}. 577 * To avoid background execution limits, use an unexported, manifest-declared receiver. 578 * For more details, see 579 * <a href="/guide/topics/ui/shortcuts/creating-shortcuts.html#pinned"> 580 * Creating pinned shortcuts</a>. 581 * 582 * @return {@code TRUE} if the launcher supports this feature. Note the API will return without 583 * waiting for the user to respond, so getting {@code TRUE} from this API does *not* mean 584 * the shortcut was pinned successfully. {@code FALSE} if the launcher doesn't support this 585 * feature. 586 * 587 * @see #isRequestPinShortcutSupported() 588 * @see IntentSender 589 * @see android.app.PendingIntent#getIntentSender() 590 * 591 * @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled. 592 * @throws IllegalStateException The caller doesn't have a foreground activity or a foreground 593 * service, or the device is locked. 594 */ 595 @WorkerThread requestPinShortcut(@onNull ShortcutInfo shortcut, @Nullable IntentSender resultIntent)596 public boolean requestPinShortcut(@NonNull ShortcutInfo shortcut, 597 @Nullable IntentSender resultIntent) { 598 try { 599 AndroidFuture<String> ret = new AndroidFuture<>(); 600 mService.requestPinShortcut(mContext.getPackageName(), shortcut, resultIntent, 601 injectMyUserId(), ret); 602 return Boolean.parseBoolean(getFutureOrThrow(ret)); 603 } catch (RemoteException e) { 604 throw e.rethrowFromSystemServer(); 605 } 606 } 607 608 /** 609 * Returns an Intent which can be used by the default launcher to pin a shortcut containing the 610 * given {@link ShortcutInfo}. This method should be used by an Activity to set a result in 611 * response to {@link Intent#ACTION_CREATE_SHORTCUT}. 612 * 613 * @param shortcut New shortcut to pin. If an app wants to pin an existing (either dynamic 614 * or manifest) shortcut, then it only needs to have an ID, and other fields don't have to 615 * be set, in which case, the target shortcut must be enabled. 616 * If it's a new shortcut, all the mandatory fields, such as a short label, must be 617 * set. 618 * @return The intent that should be set as the result for the calling activity, or 619 * <code>null</code> if the current launcher doesn't support shortcuts. 620 * 621 * @see Intent#ACTION_CREATE_SHORTCUT 622 * 623 * @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled. 624 */ 625 @WorkerThread createShortcutResultIntent(@onNull ShortcutInfo shortcut)626 public Intent createShortcutResultIntent(@NonNull ShortcutInfo shortcut) { 627 final AndroidFuture<Intent> ret = new AndroidFuture<>(); 628 try { 629 mService.createShortcutResultIntent(mContext.getPackageName(), 630 shortcut, injectMyUserId(), ret); 631 return getFutureOrThrow(ret); 632 } catch (RemoteException e) { 633 throw e.rethrowFromSystemServer(); 634 } 635 } 636 637 /** 638 * Called internally when an app is considered to have come to the foreground 639 * even when technically it's not. This method resets the throttling for this package. 640 * For example, when the user sends an "inline reply" on a notification, the system UI will 641 * call it. 642 * 643 * @hide 644 */ onApplicationActive(@onNull String packageName, @UserIdInt int userId)645 public void onApplicationActive(@NonNull String packageName, @UserIdInt int userId) { 646 try { 647 mService.onApplicationActive(packageName, userId); 648 } catch (RemoteException e) { 649 throw e.rethrowFromSystemServer(); 650 } 651 } 652 653 /** @hide injection point */ 654 @VisibleForTesting injectMyUserId()655 protected int injectMyUserId() { 656 return mContext.getUserId(); 657 } 658 659 /** 660 * Used by framework's ShareSheet (ChooserActivity.java) to retrieve all of the direct share 661 * targets that match the given IntentFilter. 662 * 663 * @param filter IntentFilter that will be used to retrieve the matching {@link ShortcutInfo}s. 664 * @return List of {@link ShareShortcutInfo}s that match the given IntentFilter. 665 * @hide 666 */ 667 @WorkerThread 668 @NonNull 669 @SystemApi 670 @RequiresPermission(Manifest.permission.MANAGE_APP_PREDICTIONS) getShareTargets(@onNull IntentFilter filter)671 public List<ShareShortcutInfo> getShareTargets(@NonNull IntentFilter filter) { 672 try { 673 return mService.getShareTargets( 674 mContext.getPackageName(), filter, injectMyUserId()).getList(); 675 } catch (RemoteException e) { 676 throw e.rethrowFromSystemServer(); 677 } 678 } 679 680 /** 681 * Represents the result of a query return by {@link #getShareTargets(IntentFilter)}. 682 * 683 * @hide 684 */ 685 @SystemApi 686 public static final class ShareShortcutInfo implements Parcelable { 687 private final ShortcutInfo mShortcutInfo; 688 private final ComponentName mTargetComponent; 689 690 /** 691 * @hide 692 */ ShareShortcutInfo(@onNull ShortcutInfo shortcutInfo, @NonNull ComponentName targetComponent)693 public ShareShortcutInfo(@NonNull ShortcutInfo shortcutInfo, 694 @NonNull ComponentName targetComponent) { 695 if (shortcutInfo == null) { 696 throw new NullPointerException("shortcut info is null"); 697 } 698 if (targetComponent == null) { 699 throw new NullPointerException("target component is null"); 700 } 701 702 mShortcutInfo = shortcutInfo; 703 mTargetComponent = targetComponent; 704 } 705 ShareShortcutInfo(@onNull Parcel in)706 private ShareShortcutInfo(@NonNull Parcel in) { 707 mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader(), android.content.pm.ShortcutInfo.class); 708 mTargetComponent = in.readParcelable(ComponentName.class.getClassLoader(), android.content.ComponentName.class); 709 } 710 711 @NonNull getShortcutInfo()712 public ShortcutInfo getShortcutInfo() { 713 return mShortcutInfo; 714 } 715 716 @NonNull getTargetComponent()717 public ComponentName getTargetComponent() { 718 return mTargetComponent; 719 } 720 721 @Override describeContents()722 public int describeContents() { 723 return 0; 724 } 725 726 @Override writeToParcel(@onNull Parcel dest, int flags)727 public void writeToParcel(@NonNull Parcel dest, int flags) { 728 dest.writeParcelable(mShortcutInfo, flags); 729 dest.writeParcelable(mTargetComponent, flags); 730 } 731 732 public static final @NonNull Parcelable.Creator<ShareShortcutInfo> CREATOR = 733 new Parcelable.Creator<ShareShortcutInfo>() { 734 public ShareShortcutInfo createFromParcel(Parcel in) { 735 return new ShareShortcutInfo(in); 736 } 737 738 public ShareShortcutInfo[] newArray(int size) { 739 return new ShareShortcutInfo[size]; 740 } 741 }; 742 } 743 744 /** 745 * Used by framework's ShareSheet (ChooserActivity.java) to check if a given package has share 746 * target definitions in it's resources. 747 * 748 * @param packageName Package to check for share targets. 749 * @return True if the package has any share target definitions, False otherwise. 750 * @hide 751 */ 752 @SystemApi hasShareTargets(@onNull String packageName)753 public boolean hasShareTargets(@NonNull String packageName) { 754 try { 755 return mService.hasShareTargets(mContext.getPackageName(), packageName, 756 injectMyUserId()); 757 } catch (RemoteException e) { 758 throw e.rethrowFromSystemServer(); 759 } 760 } 761 762 /** 763 * Publish a single dynamic shortcut. If there are already dynamic or pinned shortcuts with the 764 * same ID, each mutable shortcut is updated. 765 * 766 * <p>This method is useful when posting notifications which are tagged with shortcut IDs; In 767 * order to make sure shortcuts exist and are up-to-date, without the need to explicitly handle 768 * the shortcut count limit. 769 * @see android.app.NotificationManager#notify(int, Notification) 770 * @see android.app.Notification.Builder#setShortcutId(String) 771 * 772 * <p>If {@link #getMaxShortcutCountPerActivity()} is already reached, an existing shortcut with 773 * the lowest rank will be removed to add space for the new shortcut. 774 * 775 * <p>If the rank of the shortcut is not explicitly set, it will be set to zero, and shortcut 776 * will be added to the top of the list. 777 * 778 * @throws IllegalArgumentException if trying to update an immutable shortcut. 779 * 780 * @throws IllegalStateException when the user is locked. 781 */ pushDynamicShortcut(@onNull ShortcutInfo shortcut)782 public void pushDynamicShortcut(@NonNull ShortcutInfo shortcut) { 783 try { 784 mService.pushDynamicShortcut(mContext.getPackageName(), shortcut, injectMyUserId()); 785 } catch (RemoteException e) { 786 throw e.rethrowFromSystemServer(); 787 } 788 } 789 getFutureOrThrow(@onNull AndroidFuture<T> future)790 private static <T> T getFutureOrThrow(@NonNull AndroidFuture<T> future) { 791 try { 792 return future.get(); 793 } catch (Throwable e) { 794 if (e instanceof ExecutionException) { 795 e = e.getCause(); 796 } 797 if (e instanceof RuntimeException) { 798 throw (RuntimeException) e; 799 } 800 if (e instanceof Error) { 801 throw (Error) e; 802 } 803 throw new RuntimeException(e); 804 } 805 } 806 } 807