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&mdash;activities that handle the {@code MAIN} action and the
71 * {@code LAUNCHER} category&mdash;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 *&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
165 *             package="com.example.myapplication"&gt;
166 *  &lt;application ... &gt;
167 *    &lt;activity android:name="Main"&gt;
168 *      &lt;intent-filter&gt;
169 *        &lt;action android:name="android.intent.action.MAIN" /&gt;
170 *        &lt;category android:name="android.intent.category.LAUNCHER" /&gt;
171 *      &lt;/intent-filter&gt;
172 *      <strong>&lt;meta-data android:name="android.app.shortcuts"
173 *                 android:resource="@xml/shortcuts" /&gt;</strong>
174 *    &lt;/activity&gt;
175 *  &lt;/application&gt;
176 *&lt;/manifest&gt;
177 * </pre>
178 *
179 * Then, define your app's static shortcuts in the <code>res/xml/shortcuts.xml</code>
180 * file:
181 * <pre>
182 *&lt;shortcuts xmlns:android="http://schemas.android.com/apk/res/android"&gt;
183 *  &lt;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"&gt;
190 *    &lt;intent
191 *      android:action="android.intent.action.VIEW"
192 *      android:targetPackage="com.example.myapplication"
193 *      android:targetClass="com.example.myapplication.ComposeActivity" /&gt;
194 *    &lt;!-- 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. --&gt;
197 *    &lt;categories android:name="android.shortcut.conversation" /&gt;
198 *  &lt;/shortcut&gt;
199 *  &lt;!-- Specify more shortcuts here. --&gt;
200 *&lt;/shortcuts&gt;
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>&lt;intent&gt;</code> elements within a single <code>&lt;shortcut&gt;</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