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 */ 16package android.content.pm; 17 18import android.annotation.NonNull; 19import android.annotation.TestApi; 20import android.annotation.UserIdInt; 21import android.app.Activity; 22import android.app.usage.UsageStatsManager; 23import android.content.Context; 24import android.content.Intent; 25import android.os.RemoteException; 26import android.os.ServiceManager; 27import android.os.UserHandle; 28 29import com.android.internal.annotations.VisibleForTesting; 30 31import java.util.List; 32 33/** 34 * The ShortcutManager manages an app's <em>shortcuts</em>. Shortcuts provide users 35 * with quick access to activities other than an app's main activity in the currently-active 36 * launcher. For example, 37 * an email app may publish the "compose new email" action, which will directly open the 38 * compose activity. The {@link ShortcutInfo} class contains information about each of the 39 * shortcuts themselves. 40 * 41 * <h3>Static Shortcuts and Dynamic Shortcuts</h3> 42 * 43 * <p> 44 * There are two ways to publish shortcuts: static shortcuts and dynamic shortcuts. 45 * 46 * <ul> 47 * <li>Static shortcuts are declared in a resource 48 * XML file, which is referenced in the publisher app's <code>AndroidManifest.xml</code> file. 49 * Static shortcuts are published when an app is installed, 50 * and the details of these shortcuts change when an app is upgraded with an updated XML 51 * file. 52 * Static shortcuts are immutable, and their 53 * definitions, such as icons and labels, cannot be changed dynamically without upgrading the 54 * publisher app. 55 * 56 * <li>Dynamic shortcuts are published at runtime using this class's APIs. 57 * Apps can publish, update, and remove dynamic shortcuts at runtime. 58 * </ul> 59 * 60 * <p>Only main activities—activities that handle the {@code MAIN} action and the 61 * {@code LAUNCHER} category—can have shortcuts. 62 * If an app has multiple main activities, these activities have different sets 63 * of shortcuts. 64 * 65 * <p>Static shortcuts and dynamic shortcuts are shown in the currently active launcher when 66 * the user long-presses on an app's launcher icon. 67 * 68 * <p class="note"><strong>Note: </strong>The actual gesture may be different 69 * depending on the launcher app. 70 * 71 * <p>Each launcher icon can have at most {@link #getMaxShortcutCountPerActivity()} number of 72 * static and dynamic shortcuts combined. 73 * 74 * 75 * <h3>Pinning Shortcuts</h3> 76 * 77 * <p> 78 * Launcher apps allow users to <em>pin</em> shortcuts so they're easier to access. Both static 79 * and dynamic shortcuts can be pinned. 80 * Pinned shortcuts <b>cannot</b> be removed by publisher 81 * apps; they're removed only when the user removes them, 82 * when the publisher app is uninstalled, or when the 83 * user performs the <strong>clear data</strong> action on the publisher app from the device's Settings 84 * app. 85 * 86 * <p>However, the publisher app can <em>disable</em> pinned shortcuts so they cannot be 87 * started. See the following sections for details. 88 * 89 * 90 * <h3>Updating and Disabling Shortcuts</h3> 91 * 92 * <p>When a dynamic shortcut is pinned, even when the publisher removes it as a dynamic shortcut, 93 * the pinned shortcut will still be visible and launchable. This allows an app to have 94 * more than {@link #getMaxShortcutCountPerActivity()} number of shortcuts. 95 * 96 * <p>For example, suppose {@link #getMaxShortcutCountPerActivity()} is 5: 97 * <ol> 98 * <li>A chat app publishes 5 dynamic shortcuts for the 5 most recent 99 * conversations (c1, c2, ..., c5). 100 * 101 * <li>The user pins all 5 of the shortcuts. 102 * 103 * <li>Later, the user has started 3 additional conversations (c6, c7, and c8), 104 * so the publisher app 105 * re-publishes its dynamic shortcuts. The new dynamic shortcut list is: 106 * c4, c5, ..., c8. 107 * The publisher app has to remove c1, c2, and c3 because it can't have more than 108 * 5 dynamic shortcuts. 109 * 110 * <li>However, even though c1, c2, and c3 are no longer dynamic shortcuts, the pinned 111 * shortcuts for these conversations are still available and launchable. 112 * 113 * <li>At this point, the user can access a total of 8 shortcuts that link to activities in 114 * the publisher app, including the 3 pinned 115 * shortcuts, even though an app can have at most 5 dynamic shortcuts. 116 * 117 * <li>The app can use {@link #updateShortcuts(List)} to update <em>any</em> of the existing 118 * 8 shortcuts, when, for example, the chat peers' icons have changed. 119 * </ul> 120 * The {@link #addDynamicShortcuts(List)} and {@link #setDynamicShortcuts(List)} methods 121 * can also be used 122 * to update existing shortcuts with the same IDs, but they <b>cannot</b> be used 123 * for updating non-dynamic, pinned shortcuts because these two methods try to convert the given 124 * lists of shortcuts to dynamic shortcuts. 125 * 126 * 127 * <h4>Disabling Static Shortcuts</h4> 128 * When an app is upgraded and the new version 129 * no longer uses a static shortcut that appeared in the previous version, this deprecated 130 * shortcut will no longer be published as a static shortcut. 131 * 132 * <p>If the deprecated shortcut is pinned, then the pinned shortcut will remain on the launcher, 133 * but it will be disabled automatically. 134 * Note that, in this case, the pinned shortcut is no longer a static shortcut, but it's 135 * still <b>immutable</b>. Therefore, it cannot be updated using this class's APIs. 136 * 137 * 138 * <h4>Disabling Dynamic Shortcuts</h4> 139 * Sometimes pinned shortcuts become obsolete and may not be usable. For example, a pinned shortcut 140 * to a group chat becomes unusable when the associated group chat is deleted. In cases like this, 141 * apps should use {@link #disableShortcuts(List)}, which removes the specified dynamic 142 * shortcuts and also makes any specified pinned shortcuts un-launchable. 143 * The {@link #disableShortcuts(List, CharSequence)} method can also be used to disabled shortcuts 144 * and show users a custom error message when they attempt to launch the disabled shortcuts. 145 * 146 * 147 * <h3>Publishing Static Shortcuts</h3> 148 * 149 * <p> 150 * In order to add static shortcuts to your app, first add 151 * {@code <meta-data android:name="android.app.shortcuts" />} to your main activity in 152 * AndroidManifest.xml: 153 * <pre> 154 *<manifest xmlns:android="http://schemas.android.com/apk/res/android" 155 * package="com.example.myapplication"> 156 * <application ... > 157 * <activity android:name="Main"> 158 * <intent-filter> 159 * <action android:name="android.intent.action.MAIN" /> 160 * <category android:name="android.intent.category.LAUNCHER" /> 161 * </intent-filter> 162 * <strong><meta-data android:name="android.app.shortcuts" 163 * android:resource="@xml/shortcuts" /></strong> 164 * </activity> 165 * </application> 166 *</manifest> 167 * </pre> 168 * 169 * Then, define your app's static shortcuts in the <code>res/xml/shortcuts.xml</code> 170 * file: 171 * <pre> 172 *<shortcuts xmlns:android="http://schemas.android.com/apk/res/android"> 173 * <shortcut 174 * android:shortcutId="compose" 175 * android:enabled="true" 176 * android:icon="@drawable/compose_icon" 177 * android:shortcutShortLabel="@string/compose_shortcut_short_label1" 178 * android:shortcutLongLabel="@string/compose_shortcut_long_label1" 179 * android:shortcutDisabledMessage="@string/compose_disabled_message1"> 180 * <intent 181 * android:action="android.intent.action.VIEW" 182 * android:targetPackage="com.example.myapplication" 183 * android:targetClass="com.example.myapplication.ComposeActivity" /> 184 * <!-- If your shortcut is associated with multiple intents, include them 185 * here. The last intent in the list is what the user sees when they 186 * launch this shortcut. --> 187 * <categories android:name="android.shortcut.conversation" /> 188 * </shortcut> 189 * <!-- Specify more shortcuts here. --> 190 *</shortcuts> 191 * </pre> 192 * 193 * The following list includes descriptions for the different attributes within a static shortcut: 194 * <dl> 195 * <dt>{@code android:shortcutId}</dt> 196 * <dd>Mandatory shortcut ID</dd> 197 * 198 * <dt>{@code android:enabled}</dt> 199 * <dd>Default is {@code true}. Can be set to {@code false} in order 200 * to disable a static shortcut that was published in a previous version and set a custom 201 * disabled message. If a custom disabled message is not needed, then a static shortcut can 202 * be simply removed from the XML file rather than keeping it with {@code enabled="false"}.</dd> 203 * 204 * <dt>{@code android:icon}</dt> 205 * <dd>Shortcut icon.</dd> 206 * 207 * <dt>{@code android:shortcutShortLabel}</dt> 208 * <dd>Mandatory shortcut short label. 209 * See {@link ShortcutInfo.Builder#setShortLabel(CharSequence)}.</dd> 210 * 211 * <dt>{@code android:shortcutLongLabel}</dt> 212 * <dd>Shortcut long label. 213 * See {@link ShortcutInfo.Builder#setLongLabel(CharSequence)}.</dd> 214 * 215 * <dt>{@code android:shortcutDisabledMessage}</dt> 216 * <dd>When {@code android:enabled} is set to 217 * {@code false}, this attribute is used to display a custom disabled message.</dd> 218 * 219 * <dt>{@code intent}</dt> 220 * <dd>Intent to launch when the user selects the shortcut. 221 * {@code android:action} is mandatory. 222 * See <a href="{@docRoot}guide/topics/ui/settings.html#Intents">Using intents</a> for the 223 * other supported tags. 224 * You can provide multiple intents for a single shortcut so that the last defined activity is launched 225 * with the other activities in the <a href="/guide/components/tasks-and-back-stack.html">back stack</a>. 226 * See {@link android.app.TaskStackBuilder} for details. 227 * </dd> 228 * <dt>{@code categories}</dt> 229 * <dd>Specify shortcut categories. Currently only 230 * {@link ShortcutInfo#SHORTCUT_CATEGORY_CONVERSATION} is defined in the framework. 231 * </dd> 232 * </dl> 233 * 234 * <h3>Publishing Dynamic Shortcuts</h3> 235 * 236 * <p> 237 * Apps can publish dynamic shortcuts with {@link #setDynamicShortcuts(List)} 238 * or {@link #addDynamicShortcuts(List)}. The {@link #updateShortcuts(List)} method can also be 239 * used to update existing, mutable shortcuts. 240 * Use {@link #removeDynamicShortcuts(List)} or {@link #removeAllDynamicShortcuts()} to remove 241 * dynamic shortcuts. 242 * 243 * <p>The following code snippet shows how to create a single dynamic shortcut: 244 * <pre> 245 *ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); 246 * 247 *ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1") 248 * .setShortLabel("Web site") 249 * .setLongLabel("Open the web site") 250 * .setIcon(Icon.createWithResource(context, R.drawable.icon_website)) 251 * .setIntent(new Intent(Intent.ACTION_VIEW, 252 * Uri.parse("https://www.mysite.example.com/"))) 253 * .build(); 254 * 255 *shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut)); 256 * </pre> 257 * 258 * 259 * <h3>Shortcut Intents</h3> 260 * <p> 261 * Dynamic shortcuts can be published with any set of {@link Intent#addFlags Intent} flags. 262 * Typically, {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} is specified, possibly along with other 263 * flags; otherwise, if the app is already running, the app is simply brought to 264 * the foreground, and the target activity may not appear. 265 * 266 * <p>The {@link ShortcutInfo.Builder#setIntents(Intent[])} method can be used instead of 267 * {@link ShortcutInfo.Builder#setIntent(Intent)} with {@link android.app.TaskStackBuilder} 268 * in order to launch an activity with other activities in the back stack. 269 * When the user selects a shortcut to load an activity with a back stack, 270 * then presses the back key, a parent activity from the same app will be shown 271 * instead of the user being navigated back to the launcher. 272 * 273 * <p>Static shortcuts can also have multiple intents to achieve the same effect. 274 * In order to associate multiple {@link Intent} objects with a shortcut, simply list multiple 275 * <code><intent></code> elements within a single <code><shortcut></code> element. 276 * The last intent specifies what the user sees when they launch a shortcut. 277 * 278 * <p>Static shortcuts <b>cannot</b> have custom intent flags. 279 * The first intent of a static shortcut will always have {@link Intent#FLAG_ACTIVITY_NEW_TASK} 280 * and {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} set. 281 * This means, when the app is already running, all the existing activities will be 282 * destroyed when a static shortcut is launched. 283 * If this behavior is not desirable, you can use a <em>trampoline activity</em>, 284 * or an invisible activity that starts another activity in {@link Activity#onCreate}, 285 * then calls {@link Activity#finish()}. 286 * The first activity should include an attribute setting 287 * of {@code android:taskAffinity=""} in the app's <code>AndroidManifest.xml</code> 288 * file, and the intent within the static shortcut should point at this first activity. 289 * 290 * 291 * <h3>Showing New Information in a Shortcut</h3> 292 * <p> 293 * In order to avoid confusion, you should not use {@link #updateShortcuts(List)} to update 294 * a shortcut so that it contains conceptually different information. 295 * 296 * <p>For example, a phone app may publish the most frequently called contact as a dynamic 297 * shortcut. Over time, this contact may change. When it does, the app should 298 * represent the changed contact with a new shortcut that contains a different ID, using either 299 * {@link #setDynamicShortcuts(List)} or {@link #addDynamicShortcuts(List)}, rather than updating 300 * the existing shortcut with {@link #updateShortcuts(List)}. 301 * This is because when the shortcut is pinned, changing 302 * it to reference a different contact will likely confuse the user. 303 * 304 * <p>On the other hand, when the 305 * contact's information has changed, such as the name or picture, the app should 306 * use {@link #updateShortcuts(List)} so that the pinned shortcut is updated too. 307 * 308 * 309 * <h3>Shortcut Display Order</h3> 310 * When the launcher displays the shortcuts that are associated with a particular launcher icon, 311 * the shortcuts should appear in the following order: 312 * <ul> 313 * <li>First show static shortcuts 314 * (if {@link ShortcutInfo#isDeclaredInManifest()} is {@code true}), 315 * and then show dynamic shortcuts (if {@link ShortcutInfo#isDynamic()} is {@code true}). 316 * <li>Within each category of shortcuts (static and dynamic), sort the shortcuts in order 317 * of increasing rank according to {@link ShortcutInfo#getRank()}. 318 * </ul> 319 * <p>Shortcut ranks are non-negative, sequential integers 320 * that determine the order in which shortcuts appear, assuming that the shortcuts are all in 321 * the same category. 322 * Ranks of existing shortcuts can be updated with 323 * {@link #updateShortcuts(List)}. You can also use {@link #addDynamicShortcuts(List)} and 324 * {@link #setDynamicShortcuts(List)}. 325 * 326 * <p>Ranks are auto-adjusted so that they're unique for each target activity in each category 327 * (static or dynamic). For example, if there are 3 dynamic shortcuts with ranks 0, 1 and 2, 328 * adding another dynamic shortcut with a rank of 1 represents a request to place this shortcut at 329 * the second position. 330 * In response, the third and fourth shortcuts move closer to the bottom of the shortcut list, 331 * with their ranks changing to 2 and 3, respectively. 332 * 333 * <h3>Rate Limiting</h3> 334 * 335 * <p> 336 * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcuts(List)}, and 337 * {@link #updateShortcuts(List)} may be rate-limited when called by <em>background apps</em>, or 338 * apps with no foreground activity or service. When you attempt to call these methods 339 * from a background app after exceeding the rate limit, these APIs return {@code false}. 340 * 341 * <p>Apps with a foreground activity or service are not rate-limited. 342 * 343 * <p>Rate-limiting is reset upon certain events, so that even background apps 344 * can call these APIs until the rate limit is reached again. 345 * These events include the following: 346 * <ul> 347 * <li>An app comes to the foreground. 348 * <li>The system locale changes. 349 * <li>The user performs the <strong>inline reply</strong> action on a notification. 350 * </ul> 351 * 352 * <p>When rate-limiting is active, {@link #isRateLimitingActive()} returns {@code true}. 353 * 354 * <h4>Resetting rate-limiting for testing</h4> 355 * 356 * <p> 357 * If your app is rate-limited during development or testing, you can use the 358 * <strong>Reset ShortcutManager rate-limiting</strong> development option or 359 * the following {@code adb} command to reset it: 360 * <pre class="no-pretty-print"> 361 *$ adb shell cmd shortcut reset-throttling [ --user USER-ID ] 362 * </pre> 363 * 364 * <h3>Handling System Locale Changes</h3> 365 * 366 * <p> 367 * Apps should update dynamic and pinned shortcuts when the system locale changes 368 * using the {@link Intent#ACTION_LOCALE_CHANGED} broadcast. 369 * 370 * <p>When the system locale changes, rate-limiting is reset, so even background apps 371 * can add and update dynamic shortcuts until the rate limit is reached again. 372 * 373 * 374 * <h3>Backup and Restore</h3> 375 * 376 * <p> 377 * When an app has the {@code android:allowBackup="true"} attribute assignment included 378 * in its <code>AndroidManifest.xml</code> file, pinned shortcuts are 379 * backed up automatically and are restored when the user sets up a new device. 380 * 381 * <h4>Categories of shortcuts that are backed up</h4> 382 * 383 * <ul> 384 * <li>Pinned shortcuts are backed up. Bitmap icons are not backed up by the system, 385 * so launcher apps should back them up and restore them so that the user still sees icons 386 * for pinned shortcuts on the launcher. Apps can always use 387 * {@link #updateShortcuts(List)} to re-publish icons. 388 * 389 * <li>Static shortcuts aren't backed up, but when an app is re-installed on a new 390 * device, they are re-published from the <code>AndroidManifest.xml</code> file. 391 * 392 * <li>Dynamic shortcuts <b>aren't</b> backed up. 393 * </ul> 394 * 395 * <p>Because dynamic shortcuts are not restored, it is recommended that apps check 396 * currently-published dynamic shortcuts using {@link #getDynamicShortcuts()} 397 * each time they are launched, and they should re-publish 398 * dynamic shortcuts when necessary. 399 * 400 * <pre> 401 *public class MainActivity extends Activity { 402 * public void onCreate(Bundle savedInstanceState) { 403 * super.onCreate(savedInstanceState); 404 * ShortcutManager shortcutManager = 405 * getSystemService(ShortcutManager.class); 406 * 407 * if (shortcutManager.getDynamicShortcuts().size() == 0) { 408 * // Application restored. Need to re-publish dynamic shortcuts. 409 * if (shortcutManager.getPinnedShortcuts().size() > 0) { 410 * // Pinned shortcuts have been restored. Use 411 * // updateShortcuts() to make sure they contain 412 * // up-to-date information. 413 * } 414 * } 415 * } 416 * // ... 417 *} 418 * </pre> 419 * 420 * 421 * <h4>Backup/restore and shortcut IDs</h4> 422 * <p> 423 * Because pinned shortcuts are backed up and restored on new devices, shortcut IDs 424 * should contain either stable, constant strings or server-side identifiers, 425 * rather than identifiers generated locally that might not make sense on other devices. 426 * 427 * 428 * <h3>Report Shortcut Usage and Prediction</h3> 429 * <p> 430 * Launcher apps may be capable of predicting which shortcuts will most likely be 431 * used at a given time by examining the shortcut usage history data. 432 * 433 * <p>In order to provide launchers with such data, publisher apps should 434 * report the shortcuts that are used with {@link #reportShortcutUsed(String)} 435 * when a shortcut is selected, 436 * <b>or when an action equivalent to a shortcut is taken by the user even if it wasn't started 437 * with the shortcut</b>. 438 * 439 * <p>For example, suppose a navigation app supports "navigate to work" as a shortcut. 440 * It should then report when the user selects this shortcut <b>and</b> when the user chooses 441 * to navigate to work within the app itself. 442 * This helps the launcher app 443 * learn that the user wants to navigate to work at a certain time every 444 * weekday, and it can then show this shortcut in a suggestion list at the right time. 445 * 446 * <h3>Launcher API</h3> 447 * 448 * The {@link LauncherApps} class provides APIs for launcher apps to access shortcuts. 449 * 450 * 451 * <h3>Direct Boot and Shortcuts</h3> 452 * 453 * All shortcut information is stored in credential encrypted storage, so no shortcuts can be 454 * accessed when the user is locked. 455 */ 456public class ShortcutManager { 457 private static final String TAG = "ShortcutManager"; 458 459 private final Context mContext; 460 private final IShortcutService mService; 461 462 /** 463 * @hide 464 */ 465 public ShortcutManager(Context context, IShortcutService service) { 466 mContext = context; 467 mService = service; 468 } 469 470 /** 471 * @hide 472 */ 473 @TestApi 474 public ShortcutManager(Context context) { 475 this(context, IShortcutService.Stub.asInterface( 476 ServiceManager.getService(Context.SHORTCUT_SERVICE))); 477 } 478 479 /** 480 * Publish the list of shortcuts. All existing dynamic shortcuts from the caller app 481 * will be replaced. If there are already pinned shortcuts with the same IDs, 482 * the mutable pinned shortcuts are updated. 483 * 484 * <p>This API will be rate-limited. 485 * 486 * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. 487 * 488 * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded, 489 * or when trying to update immutable shortcuts. 490 * 491 * @throws IllegalStateException when the user is locked. 492 */ 493 public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { 494 try { 495 return mService.setDynamicShortcuts(mContext.getPackageName(), 496 new ParceledListSlice(shortcutInfoList), injectMyUserId()); 497 } catch (RemoteException e) { 498 throw e.rethrowFromSystemServer(); 499 } 500 } 501 502 /** 503 * Return all dynamic shortcuts from the caller app. 504 * 505 * @throws IllegalStateException when the user is locked. 506 */ 507 @NonNull 508 public List<ShortcutInfo> getDynamicShortcuts() { 509 try { 510 return mService.getDynamicShortcuts(mContext.getPackageName(), injectMyUserId()) 511 .getList(); 512 } catch (RemoteException e) { 513 throw e.rethrowFromSystemServer(); 514 } 515 } 516 517 /** 518 * Return all static (manifest) shortcuts from the caller app. 519 * 520 * @throws IllegalStateException when the user is locked. 521 */ 522 @NonNull 523 public List<ShortcutInfo> getManifestShortcuts() { 524 try { 525 return mService.getManifestShortcuts(mContext.getPackageName(), injectMyUserId()) 526 .getList(); 527 } catch (RemoteException e) { 528 throw e.rethrowFromSystemServer(); 529 } 530 } 531 532 /** 533 * Publish the list of dynamic shortcuts. If there are already dynamic or pinned shortcuts with 534 * the same IDs, each mutable shortcut is updated. 535 * 536 * <p>This API will be rate-limited. 537 * 538 * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. 539 * 540 * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded, 541 * or when trying to update immutable shortcuts. 542 * 543 * @throws IllegalStateException when the user is locked. 544 */ 545 public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { 546 try { 547 return mService.addDynamicShortcuts(mContext.getPackageName(), 548 new ParceledListSlice(shortcutInfoList), injectMyUserId()); 549 } catch (RemoteException e) { 550 throw e.rethrowFromSystemServer(); 551 } 552 } 553 554 /** 555 * Delete dynamic shortcuts by ID. 556 * 557 * @throws IllegalStateException when the user is locked. 558 */ 559 public void removeDynamicShortcuts(@NonNull List<String> shortcutIds) { 560 try { 561 mService.removeDynamicShortcuts(mContext.getPackageName(), shortcutIds, 562 injectMyUserId()); 563 } catch (RemoteException e) { 564 throw e.rethrowFromSystemServer(); 565 } 566 } 567 568 /** 569 * Delete all dynamic shortcuts from the caller app. 570 * 571 * @throws IllegalStateException when the user is locked. 572 */ 573 public void removeAllDynamicShortcuts() { 574 try { 575 mService.removeAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId()); 576 } catch (RemoteException e) { 577 throw e.rethrowFromSystemServer(); 578 } 579 } 580 581 /** 582 * Return all pinned shortcuts from the caller app. 583 * 584 * @throws IllegalStateException when the user is locked. 585 */ 586 @NonNull 587 public List<ShortcutInfo> getPinnedShortcuts() { 588 try { 589 return mService.getPinnedShortcuts(mContext.getPackageName(), injectMyUserId()) 590 .getList(); 591 } catch (RemoteException e) { 592 throw e.rethrowFromSystemServer(); 593 } 594 } 595 596 /** 597 * Update all existing shortcuts with the same IDs. Target shortcuts may be pinned and/or 598 * dynamic, but they must not be immutable. 599 * 600 * <p>This API will be rate-limited. 601 * 602 * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. 603 * 604 * @throws IllegalArgumentException If trying to update immutable shortcuts. 605 * 606 * @throws IllegalStateException when the user is locked. 607 */ 608 public boolean updateShortcuts(List<ShortcutInfo> shortcutInfoList) { 609 try { 610 return mService.updateShortcuts(mContext.getPackageName(), 611 new ParceledListSlice(shortcutInfoList), injectMyUserId()); 612 } catch (RemoteException e) { 613 throw e.rethrowFromSystemServer(); 614 } 615 } 616 617 /** 618 * Disable pinned shortcuts. For more details, see the Javadoc for the {@link ShortcutManager} 619 * class. 620 * 621 * @throws IllegalArgumentException If trying to disable immutable shortcuts. 622 * 623 * @throws IllegalStateException when the user is locked. 624 */ 625 public void disableShortcuts(@NonNull List<String> shortcutIds) { 626 try { 627 mService.disableShortcuts(mContext.getPackageName(), shortcutIds, 628 /* disabledMessage =*/ null, /* disabledMessageResId =*/ 0, 629 injectMyUserId()); 630 } catch (RemoteException e) { 631 throw e.rethrowFromSystemServer(); 632 } 633 } 634 635 /** 636 * @hide old signature, kept for unit testing. 637 */ 638 public void disableShortcuts(@NonNull List<String> shortcutIds, int disabledMessageResId) { 639 try { 640 mService.disableShortcuts(mContext.getPackageName(), shortcutIds, 641 /* disabledMessage =*/ null, disabledMessageResId, 642 injectMyUserId()); 643 } catch (RemoteException e) { 644 throw e.rethrowFromSystemServer(); 645 } 646 } 647 648 /** 649 * @hide old signature, kept for unit testing. 650 */ 651 public void disableShortcuts(@NonNull List<String> shortcutIds, String disabledMessage) { 652 disableShortcuts(shortcutIds, (CharSequence) disabledMessage); 653 } 654 655 /** 656 * Disable pinned shortcuts, showing the user a custom error message when they try to select 657 * the disabled shortcuts. 658 * For more details, see the Javadoc for the {@link ShortcutManager} class. 659 * 660 * @throws IllegalArgumentException If trying to disable immutable shortcuts. 661 * 662 * @throws IllegalStateException when the user is locked. 663 */ 664 public void disableShortcuts(@NonNull List<String> shortcutIds, CharSequence disabledMessage) { 665 try { 666 mService.disableShortcuts(mContext.getPackageName(), shortcutIds, 667 disabledMessage, /* disabledMessageResId =*/ 0, 668 injectMyUserId()); 669 } catch (RemoteException e) { 670 throw e.rethrowFromSystemServer(); 671 } 672 } 673 674 /** 675 * Re-enable pinned shortcuts that were previously disabled. If the target shortcuts 676 * are already enabled, this method does nothing. 677 * 678 * @throws IllegalArgumentException If trying to enable immutable shortcuts. 679 * 680 * @throws IllegalStateException when the user is locked. 681 */ 682 public void enableShortcuts(@NonNull List<String> shortcutIds) { 683 try { 684 mService.enableShortcuts(mContext.getPackageName(), shortcutIds, injectMyUserId()); 685 } catch (RemoteException e) { 686 throw e.rethrowFromSystemServer(); 687 } 688 } 689 690 691 /** 692 * @hide old signature, kept for unit testing. 693 */ 694 public int getMaxShortcutCountForActivity() { 695 return getMaxShortcutCountPerActivity(); 696 } 697 698 /** 699 * Return the maximum number of static and dynamic shortcuts that each launcher icon 700 * can have at a time. 701 */ 702 public int getMaxShortcutCountPerActivity() { 703 try { 704 return mService.getMaxShortcutCountPerActivity( 705 mContext.getPackageName(), injectMyUserId()); 706 } catch (RemoteException e) { 707 throw e.rethrowFromSystemServer(); 708 } 709 } 710 711 /** 712 * Return the number of times the caller app can call the rate-limited APIs 713 * before the rate limit counter is reset. 714 * 715 * @see #getRateLimitResetTime() 716 * 717 * @hide 718 */ 719 public int getRemainingCallCount() { 720 try { 721 return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId()); 722 } catch (RemoteException e) { 723 throw e.rethrowFromSystemServer(); 724 } 725 } 726 727 /** 728 * Return when the rate limit count will be reset next time, in milliseconds since the epoch. 729 * 730 * @see #getRemainingCallCount() 731 * @see System#currentTimeMillis() 732 * 733 * @hide 734 */ 735 public long getRateLimitResetTime() { 736 try { 737 return mService.getRateLimitResetTime(mContext.getPackageName(), injectMyUserId()); 738 } catch (RemoteException e) { 739 throw e.rethrowFromSystemServer(); 740 } 741 } 742 743 /** 744 * Return {@code true} when rate-limiting is active for the caller app. 745 * 746 * <p>See the class level javadoc for details. 747 * 748 * @throws IllegalStateException when the user is locked. 749 */ 750 public boolean isRateLimitingActive() { 751 try { 752 return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId()) 753 == 0; 754 } catch (RemoteException e) { 755 throw e.rethrowFromSystemServer(); 756 } 757 } 758 759 /** 760 * Return the max width for icons, in pixels. 761 */ 762 public int getIconMaxWidth() { 763 try { 764 // TODO Implement it properly using xdpi. 765 return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId()); 766 } catch (RemoteException e) { 767 throw e.rethrowFromSystemServer(); 768 } 769 } 770 771 /** 772 * Return the max height for icons, in pixels. 773 */ 774 public int getIconMaxHeight() { 775 try { 776 // TODO Implement it properly using ydpi. 777 return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId()); 778 } catch (RemoteException e) { 779 throw e.rethrowFromSystemServer(); 780 } 781 } 782 783 /** 784 * Apps that publish shortcuts should call this method whenever the user 785 * selects the shortcut containing the given ID or when the user completes 786 * an action in the app that is equivalent to selecting the shortcut. 787 * For more details, see the Javadoc for the {@link ShortcutManager} class 788 * 789 * <p>The information is accessible via {@link UsageStatsManager#queryEvents} 790 * Typically, launcher apps use this information to build a prediction model 791 * so that they can promote the shortcuts that are likely to be used at the moment. 792 * 793 * @throws IllegalStateException when the user is locked. 794 */ 795 public void reportShortcutUsed(String shortcutId) { 796 try { 797 mService.reportShortcutUsed(mContext.getPackageName(), shortcutId, 798 injectMyUserId()); 799 } catch (RemoteException e) { 800 throw e.rethrowFromSystemServer(); 801 } 802 } 803 804 /** 805 * Called internally when an app is considered to have come to the foreground 806 * even when technically it's not. This method resets the throttling for this package. 807 * For example, when the user sends an "inline reply" on a notification, the system UI will 808 * call it. 809 * 810 * @hide 811 */ 812 public void onApplicationActive(@NonNull String packageName, @UserIdInt int userId) { 813 try { 814 mService.onApplicationActive(packageName, userId); 815 } catch (RemoteException e) { 816 throw e.rethrowFromSystemServer(); 817 } 818 } 819 820 /** @hide injection point */ 821 @VisibleForTesting 822 protected int injectMyUserId() { 823 return UserHandle.myUserId(); 824 } 825} 826