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