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