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