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