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.SystemService;
21import android.annotation.TestApi;
22import android.annotation.UserIdInt;
23import android.app.Activity;
24import android.app.usage.UsageStatsManager;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentSender;
28import android.graphics.drawable.AdaptiveIconDrawable;
29import android.os.Build.VERSION_CODES;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.os.UserHandle;
33
34import com.android.internal.annotations.VisibleForTesting;
35
36import java.util.List;
37
38/**
39 * The ShortcutManager manages an app's <em>shortcuts</em>. Shortcuts provide users with quick
40 * access to activities other than an app's main activity in the currently-active launcher, provided
41 * that the launcher supports app shortcuts.  For example, an email app may publish the "compose new
42 * email" action, which will directly open the compose activity.  The {@link ShortcutInfo} class
43 * contains information about each of the shortcuts themselves.
44 *
45 * <p>This page discusses the implementation details of the <code>ShortcutManager</code> class. For
46 * guidance on performing operations on app shortcuts within your app, see the
47 * <a href="/guide/topics/ui/shortcuts.html">App Shortcuts</a> feature guide.
48 *
49 * <h3>Shortcut characteristics</h3>
50 *
51 * This section describes in-depth details about each shortcut type's usage and availability.
52 *
53 * <p class="note"><b>Important security note:</b> All shortcut information is stored in
54 * <a href="/training/articles/direct-boot.html">credential encrypted storage</a>, so your app
55 * cannot access a user's shortcuts until after they've unlocked the device.
56 *
57 * <h4>Static and dynamic shortcuts</h4>
58 *
59 * <p>Static shortcuts and dynamic shortcuts are shown in a supported launcher when the user
60 * performs a specific gesture. On currently-supported launchers, the gesture is a long-press on the
61 * app's launcher icon, but the actual gesture may be different on other launcher apps.
62 *
63 * <p>The {@link LauncherApps} class provides APIs for launcher apps to access shortcuts.
64 *
65 * <h4>Pinned shortcuts</h4>
66 *
67 * <p>Because pinned shortcuts appear in the launcher itself, they're always visible. A pinned
68 * shortcut is removed from the launcher only in the following situations:
69 * <ul>
70 *     <li>The user removes it.
71 *     <li>The publisher app associated with the shortcut is uninstalled.
72 *     <li>The user performs the clear data action on the publisher app from the device's
73 *     <b>Settings</b> app.
74 * </ul>
75 *
76 * <p>Because the system performs
77 * <a href="/guide/topics/ui/shortcuts.html#backup-and-restore">backup and restore</a> on pinned
78 * shortcuts automatically, these shortcuts' IDs should contain either stable, constant strings or
79 * server-side identifiers, rather than identifiers generated locally that might not make sense on
80 * other devices.
81 *
82 * <h3>Shortcut display order</h3>
83 *
84 * <p>When the launcher displays an app's shortcuts, they should appear in the following order:
85 *
86 * <ul>
87 *   <li>Static shortcuts (if {@link ShortcutInfo#isDeclaredInManifest()} is {@code true}),
88 *   and then show dynamic shortcuts (if {@link ShortcutInfo#isDynamic()} is {@code true}).
89 *   <li>Within each shortcut type (static and dynamic), sort the shortcuts in order of increasing
90 *   rank according to {@link ShortcutInfo#getRank()}.
91 * </ul>
92 *
93 * <p>Shortcut ranks are non-negative, sequential integers that determine the order in which
94 * shortcuts appear, assuming that the shortcuts are all in the same category. You can update ranks
95 * of existing shortcuts when you call {@link #updateShortcuts(List)},
96 * {@link #addDynamicShortcuts(List)}, or {@link #setDynamicShortcuts(List)}.
97 *
98 * <p class="note"><b>Note:</b> Ranks are auto-adjusted so that they're unique for each type of
99 * shortcut (static or dynamic). For example, if there are 3 dynamic shortcuts with ranks 0, 1 and
100 * 2, adding another dynamic shortcut with a rank of 1 represents a request to place this shortcut
101 * at the second position. In response, the third and fourth shortcuts move closer to the bottom of
102 * the shortcut list, with their ranks changing to 2 and 3, respectively.
103 *
104 * <h3>Options for static shortcuts</h3>
105 *
106 * The following list includes descriptions for the different attributes within a static shortcut:
107 * <dl>
108 *   <dt>{@code android:shortcutId}</dt>
109 *   <dd>Mandatory shortcut ID.
110 *   <p>
111 *   This must be a string literal.
112 *   A resource string, such as <code>@string/foo</code>, cannot be used.
113 *   </dd>
114 *
115 *   <dt>{@code android:enabled}</dt>
116 *   <dd>Default is {@code true}.  Can be set to {@code false} in order
117 *   to disable a static shortcut that was published in a previous version and set a custom
118 *   disabled message.  If a custom disabled message is not needed, then a static shortcut can
119 *   be simply removed from the XML file rather than keeping it with {@code enabled="false"}.</dd>
120 *
121 *   <dt>{@code android:icon}</dt>
122 *   <dd>Shortcut icon.</dd>
123 *
124 *   <dt>{@code android:shortcutShortLabel}</dt>
125 *   <dd>Mandatory shortcut short label.
126 *   See {@link ShortcutInfo.Builder#setShortLabel(CharSequence)}.
127 *   <p>
128 *   This must be a resource string, such as <code>@string/shortcut_label</code>.
129 *   </dd>
130 *
131 *   <dt>{@code android:shortcutLongLabel}</dt>
132 *   <dd>Shortcut long label.
133 *   See {@link ShortcutInfo.Builder#setLongLabel(CharSequence)}.
134 *   <p>
135 *   This must be a resource string, such as <code>@string/shortcut_long_label</code>.
136 *   </dd>
137 *
138 *   <dt>{@code android:shortcutDisabledMessage}</dt>
139 *   <dd>When {@code android:enabled} is set to
140 *   {@code false}, this attribute is used to display a custom disabled message.
141 *   <p>
142 *   This must be a resource string, such as <code>@string/shortcut_disabled_message</code>.
143 *   </dd>
144 *
145 *   <dt>{@code intent}</dt>
146 *   <dd>Intent to launch when the user selects the shortcut.
147 *   {@code android:action} is mandatory.
148 *   See <a href="{@docRoot}guide/topics/ui/settings.html#Intents">Using intents</a> for the
149 *   other supported tags.
150 *   <p>You can provide multiple intents for a single shortcut so that the last defined activity is
151 *   launched with the other activities in the
152 *   <a href="/guide/components/tasks-and-back-stack.html">back stack</a>. See
153 *   {@link android.app.TaskStackBuilder} for details.
154 *   <p><b>Note:</b> String resources may not be used within an {@code <intent>} element.
155 *   </dd>
156 *   <dt>{@code categories}</dt>
157 *   <dd>Specify shortcut categories.  Currently only
158 *   {@link ShortcutInfo#SHORTCUT_CATEGORY_CONVERSATION} is defined in the framework.
159 *   </dd>
160 * </dl>
161 *
162 * <h3>Updating shortcuts</h3>
163 *
164 * <p>As an example, suppose {@link #getMaxShortcutCountPerActivity()} is 5:
165 * <ol>
166 *     <li>A chat app publishes 5 dynamic shortcuts for the 5 most recent
167 *     conversations (c1, c2, ..., c5).
168 *
169 *     <li>The user pins all 5 of the shortcuts.
170 *
171 *     <li>Later, the user has started 3 additional conversations (c6, c7, and c8),
172 *     so the publisher app
173 *     re-publishes its dynamic shortcuts.  The new dynamic shortcut list is:
174 *     c4, c5, ..., c8.
175 *     The publisher app has to remove c1, c2, and c3 because it can't have more than
176 *     5 dynamic shortcuts.
177 *
178 *     <li>However, even though c1, c2, and c3 are no longer dynamic shortcuts, the pinned
179 *     shortcuts for these conversations are still available and launchable.
180 *
181 *     <li>At this point, the user can access a total of 8 shortcuts that link to activities in
182 *     the publisher app, including the 3 pinned shortcuts, even though an app can have at most 5
183 *     dynamic shortcuts.
184 *
185 *     <li>The app can use {@link #updateShortcuts(List)} to update <em>any</em> of the existing
186 *     8 shortcuts, when, for example, the chat peers' icons have changed.
187 *     <p>The {@link #addDynamicShortcuts(List)} and {@link #setDynamicShortcuts(List)} methods
188 *     can also be used to update existing shortcuts with the same IDs, but they <b>cannot</b> be
189 *     used for updating non-dynamic, pinned shortcuts because these 2 methods try to convert the
190 *     given lists of shortcuts to dynamic shortcuts.
191 * </ol>
192 *
193 * <h3>Shortcut intents</h3>
194 *
195 * <p>
196 * Dynamic shortcuts can be published with any set of {@link Intent#addFlags Intent} flags.
197 * Typically, {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} is specified, possibly along with other
198 * flags; otherwise, if the app is already running, the app is simply brought to
199 * the foreground, and the target activity may not appear.
200 *
201 * <p>Static shortcuts <b>cannot</b> have custom intent flags.
202 * The first intent of a static shortcut will always have {@link Intent#FLAG_ACTIVITY_NEW_TASK}
203 * and {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} set. This means, when the app is already running, all
204 * the existing activities in your app will be destroyed when a static shortcut is launched.
205 * If this behavior is not desirable, you can use a <em>trampoline activity</em>, or an invisible
206 * activity that starts another activity in {@link Activity#onCreate}, then calls
207 * {@link Activity#finish()}:
208 * <ol>
209 *     <li>In the <code>AndroidManifest.xml</code> file, the trampoline activity should include the
210 *     attribute assignment {@code android:taskAffinity=""}.
211 *     <li>In the shortcuts resource file, the intent within the static shortcut should point at
212 *     the trampoline activity.
213 * </ol>
214 *
215 * <h3>Handling system locale changes</h3>
216 *
217 * <p>Apps should update dynamic and pinned shortcuts when the system locale changes using the
218 * {@link Intent#ACTION_LOCALE_CHANGED} broadcast. When the system locale changes,
219 * <a href="/guide/topics/ui/shortcuts.html#rate-limit">rate limiting</a> is reset, so even
220 * background apps can add and update dynamic shortcuts until the rate limit is reached again.
221 *
222 * <h3>Shortcut limits</h3>
223 *
224 * <p>Only main activities&mdash;activities that handle the {@code MAIN} action and the
225 * {@code LAUNCHER} category&mdash;can have shortcuts. If an app has multiple main activities, you
226 * need to define the set of shortcuts for <em>each</em> activity.
227 *
228 * <p>Each launcher icon can have at most {@link #getMaxShortcutCountPerActivity()} number of
229 * static and dynamic shortcuts combined. There is no limit to the number of pinned shortcuts that
230 * an app can create.
231 *
232 * <p>When a dynamic shortcut is pinned, even when the publisher removes it as a dynamic shortcut,
233 * the pinned shortcut is still visible and launchable.  This allows an app to have more than
234 * {@link #getMaxShortcutCountPerActivity()} number of shortcuts.
235 *
236 * <h4>Rate limiting</h4>
237 *
238 * <p>When <a href="/guide/topics/ui/shortcuts.html#rate-limit">rate limiting</a> is active,
239 * {@link #isRateLimitingActive()} returns {@code true}.
240 *
241 * <p>Rate limiting is reset upon certain events, so even background apps can call these APIs until
242 * the rate limit is reached again. These events include the following:
243 * <ul>
244 *   <li>An app comes to the foreground.
245 *   <li>The system locale changes.
246 *   <li>The user performs the <strong>inline reply</strong> action on a notification.
247 * </ul>
248 */
249@SystemService(Context.SHORTCUT_SERVICE)
250public class ShortcutManager {
251    private static final String TAG = "ShortcutManager";
252
253    private final Context mContext;
254    private final IShortcutService mService;
255
256    /**
257     * @hide
258     */
259    public ShortcutManager(Context context, IShortcutService service) {
260        mContext = context;
261        mService = service;
262    }
263
264    /**
265     * @hide
266     */
267    @TestApi
268    public ShortcutManager(Context context) {
269        this(context, IShortcutService.Stub.asInterface(
270                ServiceManager.getService(Context.SHORTCUT_SERVICE)));
271    }
272
273    /**
274     * Publish the list of shortcuts.  All existing dynamic shortcuts from the caller app
275     * will be replaced.  If there are already pinned shortcuts with the same IDs,
276     * the mutable pinned shortcuts are updated.
277     *
278     * <p>This API will be rate-limited.
279     *
280     * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
281     *
282     * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded,
283     * or when trying to update immutable shortcuts.
284     *
285     * @throws IllegalStateException when the user is locked.
286     */
287    public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
288        try {
289            return mService.setDynamicShortcuts(mContext.getPackageName(),
290                    new ParceledListSlice(shortcutInfoList), injectMyUserId());
291        } catch (RemoteException e) {
292            throw e.rethrowFromSystemServer();
293        }
294    }
295
296    /**
297     * Return all dynamic shortcuts from the caller app.
298     *
299     * <p>This API is intended to be used for examining what shortcuts are currently published.
300     * Re-publishing returned {@link ShortcutInfo}s via APIs such as
301     * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons.
302     *
303     * @throws IllegalStateException when the user is locked.
304     */
305    @NonNull
306    public List<ShortcutInfo> getDynamicShortcuts() {
307        try {
308            return mService.getDynamicShortcuts(mContext.getPackageName(), injectMyUserId())
309                    .getList();
310        } catch (RemoteException e) {
311            throw e.rethrowFromSystemServer();
312        }
313    }
314
315    /**
316     * Return all static (manifest) shortcuts from the caller app.
317     *
318     * <p>This API is intended to be used for examining what shortcuts are currently published.
319     * Re-publishing returned {@link ShortcutInfo}s via APIs such as
320     * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons.
321     *
322     * @throws IllegalStateException when the user is locked.
323     */
324    @NonNull
325    public List<ShortcutInfo> getManifestShortcuts() {
326        try {
327            return mService.getManifestShortcuts(mContext.getPackageName(), injectMyUserId())
328                    .getList();
329        } catch (RemoteException e) {
330            throw e.rethrowFromSystemServer();
331        }
332    }
333
334    /**
335     * Publish the list of dynamic shortcuts.  If there are already dynamic or pinned shortcuts with
336     * the same IDs, each mutable shortcut is updated.
337     *
338     * <p>This API will be rate-limited.
339     *
340     * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
341     *
342     * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded,
343     * or when trying to update immutable shortcuts.
344     *
345     * @throws IllegalStateException when the user is locked.
346     */
347    public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
348        try {
349            return mService.addDynamicShortcuts(mContext.getPackageName(),
350                    new ParceledListSlice(shortcutInfoList), injectMyUserId());
351        } catch (RemoteException e) {
352            throw e.rethrowFromSystemServer();
353        }
354    }
355
356    /**
357     * Delete dynamic shortcuts by ID.
358     *
359     * @throws IllegalStateException when the user is locked.
360     */
361    public void removeDynamicShortcuts(@NonNull List<String> shortcutIds) {
362        try {
363            mService.removeDynamicShortcuts(mContext.getPackageName(), shortcutIds,
364                    injectMyUserId());
365        } catch (RemoteException e) {
366            throw e.rethrowFromSystemServer();
367        }
368    }
369
370    /**
371     * Delete all dynamic shortcuts from the caller app.
372     *
373     * @throws IllegalStateException when the user is locked.
374     */
375    public void removeAllDynamicShortcuts() {
376        try {
377            mService.removeAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId());
378        } catch (RemoteException e) {
379            throw e.rethrowFromSystemServer();
380        }
381    }
382
383    /**
384     * Return all pinned shortcuts from the caller app.
385     *
386     * <p>This API is intended to be used for examining what shortcuts are currently published.
387     * Re-publishing returned {@link ShortcutInfo}s via APIs such as
388     * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons.
389     *
390     * @throws IllegalStateException when the user is locked.
391     */
392    @NonNull
393    public List<ShortcutInfo> getPinnedShortcuts() {
394        try {
395            return mService.getPinnedShortcuts(mContext.getPackageName(), injectMyUserId())
396                    .getList();
397        } catch (RemoteException e) {
398            throw e.rethrowFromSystemServer();
399        }
400    }
401
402    /**
403     * Update all existing shortcuts with the same IDs.  Target shortcuts may be pinned and/or
404     * dynamic, but they must not be immutable.
405     *
406     * <p>This API will be rate-limited.
407     *
408     * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
409     *
410     * @throws IllegalArgumentException If trying to update immutable shortcuts.
411     *
412     * @throws IllegalStateException when the user is locked.
413     */
414    public boolean updateShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
415        try {
416            return mService.updateShortcuts(mContext.getPackageName(),
417                    new ParceledListSlice(shortcutInfoList), injectMyUserId());
418        } catch (RemoteException e) {
419            throw e.rethrowFromSystemServer();
420        }
421    }
422
423    /**
424     * Disable pinned shortcuts.  For more details, see the Javadoc for the {@link ShortcutManager}
425     * class.
426     *
427     * @throws IllegalArgumentException If trying to disable immutable shortcuts.
428     *
429     * @throws IllegalStateException when the user is locked.
430     */
431    public void disableShortcuts(@NonNull List<String> shortcutIds) {
432        try {
433            mService.disableShortcuts(mContext.getPackageName(), shortcutIds,
434                    /* disabledMessage =*/ null, /* disabledMessageResId =*/ 0,
435                    injectMyUserId());
436        } catch (RemoteException e) {
437            throw e.rethrowFromSystemServer();
438        }
439    }
440
441    /**
442     * @hide old signature, kept for unit testing.
443     */
444    public void disableShortcuts(@NonNull List<String> shortcutIds, int disabledMessageResId) {
445        try {
446            mService.disableShortcuts(mContext.getPackageName(), shortcutIds,
447                    /* disabledMessage =*/ null, disabledMessageResId,
448                    injectMyUserId());
449        } catch (RemoteException e) {
450            throw e.rethrowFromSystemServer();
451        }
452    }
453
454    /**
455     * @hide old signature, kept for unit testing.
456     */
457    public void disableShortcuts(@NonNull List<String> shortcutIds, String disabledMessage) {
458        disableShortcuts(shortcutIds, (CharSequence) disabledMessage);
459    }
460
461    /**
462     * Disable pinned shortcuts, showing the user a custom error message when they try to select
463     * the disabled shortcuts.
464     * For more details, see the Javadoc for the {@link ShortcutManager} class.
465     *
466     * @throws IllegalArgumentException If trying to disable immutable shortcuts.
467     *
468     * @throws IllegalStateException when the user is locked.
469     */
470    public void disableShortcuts(@NonNull List<String> shortcutIds, CharSequence disabledMessage) {
471        try {
472            mService.disableShortcuts(mContext.getPackageName(), shortcutIds,
473                    disabledMessage, /* disabledMessageResId =*/ 0,
474                    injectMyUserId());
475        } catch (RemoteException e) {
476            throw e.rethrowFromSystemServer();
477        }
478    }
479
480    /**
481     * Re-enable pinned shortcuts that were previously disabled.  If the target shortcuts
482     * are already enabled, this method does nothing.
483     *
484     * @throws IllegalArgumentException If trying to enable immutable shortcuts.
485     *
486     * @throws IllegalStateException when the user is locked.
487     */
488    public void enableShortcuts(@NonNull List<String> shortcutIds) {
489        try {
490            mService.enableShortcuts(mContext.getPackageName(), shortcutIds, injectMyUserId());
491        } catch (RemoteException e) {
492            throw e.rethrowFromSystemServer();
493        }
494    }
495
496
497    /**
498     * @hide old signature, kept for unit testing.
499     */
500    public int getMaxShortcutCountForActivity() {
501        return getMaxShortcutCountPerActivity();
502    }
503
504    /**
505     * Return the maximum number of static and dynamic shortcuts that each launcher icon
506     * can have at a time.
507     */
508    public int getMaxShortcutCountPerActivity() {
509        try {
510            return mService.getMaxShortcutCountPerActivity(
511                    mContext.getPackageName(), injectMyUserId());
512        } catch (RemoteException e) {
513            throw e.rethrowFromSystemServer();
514        }
515    }
516
517    /**
518     * Return the number of times the caller app can call the rate-limited APIs
519     * before the rate limit counter is reset.
520     *
521     * @see #getRateLimitResetTime()
522     *
523     * @hide
524     */
525    public int getRemainingCallCount() {
526        try {
527            return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId());
528        } catch (RemoteException e) {
529            throw e.rethrowFromSystemServer();
530        }
531    }
532
533    /**
534     * Return when the rate limit count will be reset next time, in milliseconds since the epoch.
535     *
536     * @see #getRemainingCallCount()
537     * @see System#currentTimeMillis()
538     *
539     * @hide
540     */
541    public long getRateLimitResetTime() {
542        try {
543            return mService.getRateLimitResetTime(mContext.getPackageName(), injectMyUserId());
544        } catch (RemoteException e) {
545            throw e.rethrowFromSystemServer();
546        }
547    }
548
549    /**
550     * Return {@code true} when rate-limiting is active for the caller app.
551     *
552     * <p>See the class level javadoc for details.
553     *
554     * @throws IllegalStateException when the user is locked.
555     */
556    public boolean isRateLimitingActive() {
557        try {
558            return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId())
559                    == 0;
560        } catch (RemoteException e) {
561            throw e.rethrowFromSystemServer();
562        }
563    }
564
565    /**
566     * Return the max width for icons, in pixels.
567     *
568     * <p> Note that this method returns max width of icon's visible part. Hence, it does not take
569     * into account the inset introduced by {@link AdaptiveIconDrawable}. To calculate bitmap image
570     * to function as {@link AdaptiveIconDrawable}, multiply
571     * 1 + 2 * {@link AdaptiveIconDrawable#getExtraInsetFraction()} to the returned size.
572     */
573    public int getIconMaxWidth() {
574        try {
575            // TODO Implement it properly using xdpi.
576            return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId());
577        } catch (RemoteException e) {
578            throw e.rethrowFromSystemServer();
579        }
580    }
581
582    /**
583     * Return the max height for icons, in pixels.
584     */
585    public int getIconMaxHeight() {
586        try {
587            // TODO Implement it properly using ydpi.
588            return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId());
589        } catch (RemoteException e) {
590            throw e.rethrowFromSystemServer();
591        }
592    }
593
594    /**
595     * Apps that publish shortcuts should call this method whenever the user
596     * selects the shortcut containing the given ID or when the user completes
597     * an action in the app that is equivalent to selecting the shortcut.
598     * For more details, see the Javadoc for the {@link ShortcutManager} class
599     *
600     * <p>The information is accessible via {@link UsageStatsManager#queryEvents}
601     * Typically, launcher apps use this information to build a prediction model
602     * so that they can promote the shortcuts that are likely to be used at the moment.
603     *
604     * @throws IllegalStateException when the user is locked.
605     */
606    public void reportShortcutUsed(String shortcutId) {
607        try {
608            mService.reportShortcutUsed(mContext.getPackageName(), shortcutId,
609                    injectMyUserId());
610        } catch (RemoteException e) {
611            throw e.rethrowFromSystemServer();
612        }
613    }
614
615    /**
616     * Return {@code TRUE} if the app is running on a device whose default launcher supports
617     * {@link #requestPinShortcut(ShortcutInfo, IntentSender)}.
618     *
619     * <p>The return value may change in subsequent calls if the user changes the default launcher
620     * app.
621     *
622     * <p><b>Note:</b> See also the support library counterpart
623     * {@link android.support.v4.content.pm.ShortcutManagerCompat#isRequestPinShortcutSupported(
624     * Context)}, which supports Android versions lower than {@link VERSION_CODES#O} using the
625     * legacy private intent {@code com.android.launcher.action.INSTALL_SHORTCUT}.
626     *
627     * @see #requestPinShortcut(ShortcutInfo, IntentSender)
628     */
629    public boolean isRequestPinShortcutSupported() {
630        try {
631            return mService.isRequestPinItemSupported(injectMyUserId(),
632                    LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT);
633        } catch (RemoteException e) {
634            throw e.rethrowFromSystemServer();
635        }
636    }
637
638    /**
639     * Request to create a pinned shortcut.  The default launcher will receive this request and
640     * ask the user for approval.  If the user approves it, the shortcut will be created, and
641     * {@code resultIntent} will be sent. If a request is denied by the user, however, no response
642     * will be sent to the caller.
643     *
644     * <p>Only apps with a foreground activity or a foreground service can call this method.
645     * Otherwise, it'll throw {@link IllegalStateException}.
646     *
647     * <p>It's up to the launcher to decide how to handle previous pending requests when the same
648     * package calls this API multiple times in a row. One possible strategy is to ignore any
649     * previous requests.
650     *
651     * <p><b>Note:</b> See also the support library counterpart
652     * {@link android.support.v4.content.pm.ShortcutManagerCompat#requestPinShortcut(
653     * Context, ShortcutInfoCompat, IntentSender)},
654     * which supports Android versions lower than {@link VERSION_CODES#O} using the
655     * legacy private intent {@code com.android.launcher.action.INSTALL_SHORTCUT}.
656     *
657     * @param shortcut Shortcut to pin.  If an app wants to pin an existing (either static
658     *     or dynamic) shortcut, then it only needs to have an ID. Although other fields don't have
659     *     to be set, the target shortcut must be enabled.
660     *
661     *     <p>If it's a new shortcut, all the mandatory fields, such as a short label, must be
662     *     set.
663     * @param resultIntent If not null, this intent will be sent when the shortcut is pinned.
664     *    Use {@link android.app.PendingIntent#getIntentSender()} to create an {@link IntentSender}.
665     *    To avoid background execution limits, use an unexported, manifest-declared receiver.
666     *    For more details, see the overview documentation for the {@link ShortcutManager} class.
667     *
668     * @return {@code TRUE} if the launcher supports this feature.  Note the API will return without
669     *    waiting for the user to respond, so getting {@code TRUE} from this API does *not* mean
670     *    the shortcut was pinned successfully.  {@code FALSE} if the launcher doesn't support this
671     *    feature.
672     *
673     * @see #isRequestPinShortcutSupported()
674     * @see IntentSender
675     * @see android.app.PendingIntent#getIntentSender()
676     *
677     * @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled.
678     * @throws IllegalStateException The caller doesn't have a foreground activity or a foreground
679     * service, or the device is locked.
680     */
681    public boolean requestPinShortcut(@NonNull ShortcutInfo shortcut,
682            @Nullable IntentSender resultIntent) {
683        try {
684            return mService.requestPinShortcut(mContext.getPackageName(), shortcut,
685                    resultIntent, injectMyUserId());
686        } catch (RemoteException e) {
687            throw e.rethrowFromSystemServer();
688        }
689    }
690
691    /**
692     * Returns an Intent which can be used by the default launcher to pin a shortcut containing the
693     * given {@link ShortcutInfo}. This method should be used by an Activity to set a result in
694     * response to {@link Intent#ACTION_CREATE_SHORTCUT}.
695     *
696     * @param shortcut New shortcut to pin.  If an app wants to pin an existing (either dynamic
697     *     or manifest) shortcut, then it only needs to have an ID, and other fields don't have to
698     *     be set, in which case, the target shortcut must be enabled.
699     *     If it's a new shortcut, all the mandatory fields, such as a short label, must be
700     *     set.
701     * @return The intent that should be set as the result for the calling activity, or
702     *     <code>null</code> if the current launcher doesn't support shortcuts.
703     *
704     * @see Intent#ACTION_CREATE_SHORTCUT
705     *
706     * @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled.
707     */
708    public Intent createShortcutResultIntent(@NonNull ShortcutInfo shortcut) {
709        try {
710            return mService.createShortcutResultIntent(mContext.getPackageName(), shortcut,
711                    injectMyUserId());
712        } catch (RemoteException e) {
713            throw e.rethrowFromSystemServer();
714        }
715    }
716
717    /**
718     * Called internally when an app is considered to have come to the foreground
719     * even when technically it's not.  This method resets the throttling for this package.
720     * For example, when the user sends an "inline reply" on a notification, the system UI will
721     * call it.
722     *
723     * @hide
724     */
725    public void onApplicationActive(@NonNull String packageName, @UserIdInt int userId) {
726        try {
727            mService.onApplicationActive(packageName, userId);
728        } catch (RemoteException e) {
729            throw e.rethrowFromSystemServer();
730        }
731    }
732
733    /** @hide injection point */
734    @VisibleForTesting
735    protected int injectMyUserId() {
736        return UserHandle.myUserId();
737    }
738}
739