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