• 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.annotation.IntDef;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SystemApi;
22 import android.annotation.TestApi;
23 import android.annotation.UserIdInt;
24 import android.app.Notification;
25 import android.app.Person;
26 import android.app.TaskStackBuilder;
27 import android.app.appsearch.GenericDocument;
28 import android.compat.annotation.UnsupportedAppUsage;
29 import android.content.ComponentName;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.LocusId;
33 import android.content.pm.LauncherApps.ShortcutQuery;
34 import android.content.res.Resources;
35 import android.content.res.Resources.NotFoundException;
36 import android.graphics.Bitmap;
37 import android.graphics.drawable.Icon;
38 import android.os.Build;
39 import android.os.Bundle;
40 import android.os.Parcel;
41 import android.os.Parcelable;
42 import android.os.PersistableBundle;
43 import android.os.UserHandle;
44 import android.text.TextUtils;
45 import android.util.ArrayMap;
46 import android.util.ArraySet;
47 import android.util.Log;
48 import android.view.contentcapture.ContentCaptureContext;
49 
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.internal.util.Preconditions;
52 
53 import java.lang.annotation.Retention;
54 import java.lang.annotation.RetentionPolicy;
55 import java.util.ArrayList;
56 import java.util.Collections;
57 import java.util.HashMap;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Objects;
61 import java.util.Set;
62 import java.util.stream.Collectors;
63 
64 /**
65  * Represents a shortcut that can be published via {@link ShortcutManager}.
66  *
67  * @see ShortcutManager
68  */
69 public final class ShortcutInfo implements Parcelable {
70     static final String TAG = "Shortcut";
71 
72     private static final String RES_TYPE_STRING = "string";
73 
74     private static final String ANDROID_PACKAGE_NAME = "android";
75 
76     private static final int IMPLICIT_RANK_MASK = 0x7fffffff;
77 
78     /** @hide */
79     public static final int RANK_CHANGED_BIT = ~IMPLICIT_RANK_MASK;
80 
81     /** @hide */
82     public static final int RANK_NOT_SET = Integer.MAX_VALUE;
83 
84     /** @hide */
85     public static final int FLAG_DYNAMIC = 1 << 0;
86 
87     /** @hide */
88     public static final int FLAG_PINNED = 1 << 1;
89 
90     /** @hide */
91     public static final int FLAG_HAS_ICON_RES = 1 << 2;
92 
93     /** @hide */
94     public static final int FLAG_HAS_ICON_FILE = 1 << 3;
95 
96     /** @hide */
97     public static final int FLAG_KEY_FIELDS_ONLY = 1 << 4;
98 
99     /** @hide */
100     public static final int FLAG_MANIFEST = 1 << 5;
101 
102     /** @hide */
103     public static final int FLAG_DISABLED = 1 << 6;
104 
105     /** @hide */
106     public static final int FLAG_STRINGS_RESOLVED = 1 << 7;
107 
108     /** @hide */
109     public static final int FLAG_IMMUTABLE = 1 << 8;
110 
111     /** @hide */
112     public static final int FLAG_ADAPTIVE_BITMAP = 1 << 9;
113 
114     /** @hide */
115     public static final int FLAG_RETURNED_BY_SERVICE = 1 << 10;
116 
117     /** @hide When this is set, the bitmap icon is waiting to be saved. */
118     public static final int FLAG_ICON_FILE_PENDING_SAVE = 1 << 11;
119 
120     /**
121      * "Shadow" shortcuts are the ones that are restored, but the owner package hasn't been
122      * installed yet.
123      * @hide
124      */
125     public static final int FLAG_SHADOW = 1 << 12;
126 
127     /** @hide */
128     public static final int FLAG_LONG_LIVED = 1 << 13;
129 
130     /**
131      * TODO(b/155135057): This is a quick and temporary fix for b/155135890. ShortcutService doesn't
132      *  need to be aware of the outside world. Replace this with a more extensible solution.
133      * @hide
134      */
135     public static final int FLAG_CACHED_NOTIFICATIONS = 1 << 14;
136 
137     /** @hide */
138     public static final int FLAG_HAS_ICON_URI = 1 << 15;
139 
140     /**
141      * TODO(b/155135057): This is a quick and temporary fix for b/155135890. ShortcutService doesn't
142      *  need to be aware of the outside world. Replace this with a more extensible solution.
143      * @hide
144      */
145     public static final int FLAG_CACHED_PEOPLE_TILE = 1 << 29;
146 
147     /**
148      * TODO(b/155135057): This is a quick and temporary fix for b/155135890. ShortcutService doesn't
149      *  need to be aware of the outside world. Replace this with a more extensible solution.
150      * @hide
151      */
152     public static final int FLAG_CACHED_BUBBLES = 1 << 30;
153 
154     /** @hide */
155     public static final int FLAG_CACHED_ALL =
156             FLAG_CACHED_NOTIFICATIONS | FLAG_CACHED_BUBBLES | FLAG_CACHED_PEOPLE_TILE;
157 
158     /**
159      * Bitmask-based flags indicating different states associated with the shortcut. Note that if
160      * new value is added here, consider adding also the corresponding string representation and
161      * queries in {@link AppSearchShortcutInfo}.
162      *
163      * @hide
164      */
165     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
166             FLAG_DYNAMIC,
167             FLAG_PINNED,
168             FLAG_HAS_ICON_RES,
169             FLAG_HAS_ICON_FILE,
170             FLAG_KEY_FIELDS_ONLY,
171             FLAG_MANIFEST,
172             FLAG_DISABLED,
173             FLAG_STRINGS_RESOLVED,
174             FLAG_IMMUTABLE,
175             FLAG_ADAPTIVE_BITMAP,
176             FLAG_RETURNED_BY_SERVICE,
177             FLAG_ICON_FILE_PENDING_SAVE,
178             FLAG_SHADOW,
179             FLAG_LONG_LIVED,
180             FLAG_HAS_ICON_URI,
181             FLAG_CACHED_NOTIFICATIONS,
182             FLAG_CACHED_BUBBLES,
183             FLAG_CACHED_PEOPLE_TILE
184     })
185     @Retention(RetentionPolicy.SOURCE)
186     public @interface ShortcutFlags {}
187 
188     // Cloning options.
189 
190     /** @hide */
191     private static final int CLONE_REMOVE_ICON = 1 << 0;
192 
193     /** @hide */
194     private static final int CLONE_REMOVE_INTENT = 1 << 1;
195 
196     /** @hide */
197     public static final int CLONE_REMOVE_NON_KEY_INFO = 1 << 2;
198 
199     /** @hide */
200     public static final int CLONE_REMOVE_RES_NAMES = 1 << 3;
201 
202     /** @hide */
203     public static final int CLONE_REMOVE_PERSON = 1 << 4;
204 
205     /** @hide */
206     public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON | CLONE_REMOVE_RES_NAMES;
207 
208     /** @hide */
209     public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT
210             | CLONE_REMOVE_RES_NAMES | CLONE_REMOVE_PERSON;
211 
212     /** @hide */
213     public static final int CLONE_REMOVE_FOR_LAUNCHER_APPROVAL = CLONE_REMOVE_INTENT
214             | CLONE_REMOVE_RES_NAMES | CLONE_REMOVE_PERSON;
215 
216     /** @hide */
217     public static final int CLONE_REMOVE_FOR_APP_PREDICTION = CLONE_REMOVE_ICON
218             | CLONE_REMOVE_RES_NAMES;
219 
220     /** @hide */
221     @IntDef(flag = true, prefix = { "CLONE_" }, value = {
222             CLONE_REMOVE_ICON,
223             CLONE_REMOVE_INTENT,
224             CLONE_REMOVE_NON_KEY_INFO,
225             CLONE_REMOVE_RES_NAMES,
226             CLONE_REMOVE_PERSON,
227             CLONE_REMOVE_FOR_CREATOR,
228             CLONE_REMOVE_FOR_LAUNCHER,
229             CLONE_REMOVE_FOR_LAUNCHER_APPROVAL,
230             CLONE_REMOVE_FOR_APP_PREDICTION
231     })
232     @Retention(RetentionPolicy.SOURCE)
233     public @interface CloneFlags {}
234 
235     /**
236      * Shortcut is not disabled.
237      */
238     public static final int DISABLED_REASON_NOT_DISABLED = 0;
239 
240     /**
241      * Shortcut has been disabled by the publisher app with the
242      * {@link ShortcutManager#disableShortcuts(List)} API.
243      */
244     public static final int DISABLED_REASON_BY_APP = 1;
245 
246     /**
247      * Shortcut has been disabled due to changes to the publisher app. (e.g. a manifest shortcut
248      * no longer exists.)
249      */
250     public static final int DISABLED_REASON_APP_CHANGED = 2;
251 
252     /**
253      * Shortcut is disabled for an unknown reason.
254      */
255     public static final int DISABLED_REASON_UNKNOWN = 3;
256 
257     /**
258      * A disabled reason that's equal to or bigger than this is due to backup and restore issue.
259      * A shortcut with such a reason wil be visible to the launcher, but not to the publisher.
260      * ({@link #isVisibleToPublisher()} will be false.)
261      */
262     private static final int DISABLED_REASON_RESTORE_ISSUE_START = 100;
263 
264     /**
265      * Shortcut has been restored from the previous device, but the publisher app on the current
266      * device is of a lower version. The shortcut will not be usable until the app is upgraded to
267      * the same version or higher.
268      */
269     public static final int DISABLED_REASON_VERSION_LOWER = 100;
270 
271     /**
272      * Shortcut has not been restored because the publisher app does not support backup and restore.
273      */
274     public static final int DISABLED_REASON_BACKUP_NOT_SUPPORTED = 101;
275 
276     /**
277      * Shortcut has not been restored because the publisher app's signature has changed.
278      */
279     public static final int DISABLED_REASON_SIGNATURE_MISMATCH = 102;
280 
281     /**
282      * Shortcut has not been restored for unknown reason.
283      */
284     public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103;
285 
286     /**
287      * The maximum length of Shortcut ID. IDs will be truncated at this limit.
288      * @hide
289      */
290     public static final int MAX_ID_LENGTH = 1000;
291 
292     /** @hide */
293     @IntDef(prefix = { "DISABLED_REASON_" }, value = {
294             DISABLED_REASON_NOT_DISABLED,
295             DISABLED_REASON_BY_APP,
296             DISABLED_REASON_APP_CHANGED,
297             DISABLED_REASON_UNKNOWN,
298             DISABLED_REASON_VERSION_LOWER,
299             DISABLED_REASON_BACKUP_NOT_SUPPORTED,
300             DISABLED_REASON_SIGNATURE_MISMATCH,
301             DISABLED_REASON_OTHER_RESTORE_ISSUE,
302     })
303     @Retention(RetentionPolicy.SOURCE)
304     public @interface DisabledReason{}
305 
306     /**
307      * Return a label for disabled reasons, which are *not* supposed to be shown to the user.
308      * @hide
309      */
getDisabledReasonDebugString(@isabledReason int disabledReason)310     public static String getDisabledReasonDebugString(@DisabledReason int disabledReason) {
311         switch (disabledReason) {
312             case DISABLED_REASON_NOT_DISABLED:
313                 return "[Not disabled]";
314             case DISABLED_REASON_BY_APP:
315                 return "[Disabled: by app]";
316             case DISABLED_REASON_APP_CHANGED:
317                 return "[Disabled: app changed]";
318             case DISABLED_REASON_VERSION_LOWER:
319                 return "[Disabled: lower version]";
320             case DISABLED_REASON_BACKUP_NOT_SUPPORTED:
321                 return "[Disabled: backup not supported]";
322             case DISABLED_REASON_SIGNATURE_MISMATCH:
323                 return "[Disabled: signature mismatch]";
324             case DISABLED_REASON_OTHER_RESTORE_ISSUE:
325                 return "[Disabled: unknown restore issue]";
326         }
327         return "[Disabled: unknown reason:" + disabledReason + "]";
328     }
329 
330     /**
331      * Return a label for a disabled reason for shortcuts that are disabled due to a backup and
332      * restore issue. If the reason is not due to backup & restore, then it'll return null.
333      *
334      * This method returns localized, user-facing strings, which will be returned by
335      * {@link #getDisabledMessage()}.
336      *
337      * @hide
338      */
getDisabledReasonForRestoreIssue(Context context, @DisabledReason int disabledReason)339     public static String getDisabledReasonForRestoreIssue(Context context,
340             @DisabledReason int disabledReason) {
341         final Resources res = context.getResources();
342 
343         switch (disabledReason) {
344             case DISABLED_REASON_VERSION_LOWER:
345                 return res.getString(
346                         com.android.internal.R.string.shortcut_restored_on_lower_version);
347             case DISABLED_REASON_BACKUP_NOT_SUPPORTED:
348                 return res.getString(
349                         com.android.internal.R.string.shortcut_restore_not_supported);
350             case DISABLED_REASON_SIGNATURE_MISMATCH:
351                 return res.getString(
352                         com.android.internal.R.string.shortcut_restore_signature_mismatch);
353             case DISABLED_REASON_OTHER_RESTORE_ISSUE:
354                 return res.getString(
355                         com.android.internal.R.string.shortcut_restore_unknown_issue);
356             case DISABLED_REASON_UNKNOWN:
357                 return res.getString(
358                         com.android.internal.R.string.shortcut_disabled_reason_unknown);
359         }
360         return null;
361     }
362 
363     /** @hide */
isDisabledForRestoreIssue(@isabledReason int disabledReason)364     public static boolean isDisabledForRestoreIssue(@DisabledReason int disabledReason) {
365         return disabledReason >= DISABLED_REASON_RESTORE_ISSUE_START;
366     }
367 
368     /** @hide */
369     @IntDef(flag = true, value = {SURFACE_LAUNCHER})
370     @Retention(RetentionPolicy.SOURCE)
371     public @interface Surface {}
372 
373     /**
374      * Indicates system surfaces managed by a launcher app. e.g. Long-Press Menu.
375      */
376     public static final int SURFACE_LAUNCHER = 1 << 0;
377 
378     /**
379      * Shortcut category for messaging related actions, such as chat.
380      */
381     public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
382 
383     private final String mId;
384 
385     @NonNull
386     private final String mPackageName;
387 
388     @Nullable
389     private ComponentName mActivity;
390 
391     @Nullable
392     private Icon mIcon;
393 
394     private int mTitleResId;
395 
396     private String mTitleResName;
397 
398     @Nullable
399     private CharSequence mTitle;
400 
401     private int mTextResId;
402 
403     private String mTextResName;
404 
405     @Nullable
406     private CharSequence mText;
407 
408     private int mDisabledMessageResId;
409 
410     private String mDisabledMessageResName;
411 
412     @Nullable
413     private CharSequence mDisabledMessage;
414 
415     @Nullable
416     private ArraySet<String> mCategories;
417 
418     /**
419      * Intents *with extras removed*.
420      */
421     @Nullable
422     private Intent[] mIntents;
423 
424     /**
425      * Extras for the intents.
426      */
427     @Nullable
428     private PersistableBundle[] mIntentPersistableExtrases;
429 
430     @Nullable
431     private Person[] mPersons;
432 
433     @Nullable
434     private LocusId mLocusId;
435 
436     private int mRank;
437 
438     /**
439      * Internally used for auto-rank-adjustment.
440      *
441      * RANK_CHANGED_BIT is used to denote that the rank of a shortcut is changing.
442      * The rest of the bits are used to denote the order in which shortcuts are passed to
443      * APIs, which is used to preserve the argument order when ranks are tie.
444      */
445     private int mImplicitRank;
446 
447     @Nullable
448     private PersistableBundle mExtras;
449 
450     private long mLastChangedTimestamp;
451 
452     // Internal use only.
453     @ShortcutFlags
454     private int mFlags;
455 
456     // Internal use only.
457     private int mIconResId;
458 
459     private String mIconResName;
460 
461     // Internal use only.
462     private String mIconUri;
463 
464     // Internal use only.
465     @Nullable
466     private String mBitmapPath;
467 
468     private final int mUserId;
469 
470     /** @hide */
471     public static final int VERSION_CODE_UNKNOWN = -1;
472 
473     private int mDisabledReason;
474 
475     @Nullable private String mStartingThemeResName;
476 
477     private int mExcludedSurfaces;
478 
479     @Nullable
480     private Map<String, Map<String, List<String>>> mCapabilityBindings;
481 
ShortcutInfo(Builder b)482     private ShortcutInfo(Builder b) {
483         mUserId = b.mContext.getUserId();
484         mId = getSafeId(Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided"));
485 
486         // Note we can't do other null checks here because SM.updateShortcuts() takes partial
487         // information.
488         mPackageName = b.mContext.getPackageName();
489         mActivity = b.mActivity;
490         mIcon = b.mIcon;
491         mTitle = b.mTitle;
492         mTitleResId = b.mTitleResId;
493         mText = b.mText;
494         mTextResId = b.mTextResId;
495         mDisabledMessage = b.mDisabledMessage;
496         mDisabledMessageResId = b.mDisabledMessageResId;
497         mCategories = cloneCategories(b.mCategories);
498         mIntents = cloneIntents(b.mIntents);
499         fixUpIntentExtras();
500         mPersons = clonePersons(b.mPersons);
501         if (b.mIsLongLived) {
502             setLongLived();
503         }
504         mExcludedSurfaces = b.mExcludedSurfaces;
505         mRank = b.mRank;
506         mExtras = b.mExtras;
507         mLocusId = b.mLocusId;
508         mCapabilityBindings =
509                 cloneCapabilityBindings(b.mCapabilityBindings);
510         mStartingThemeResName = b.mStartingThemeResId != 0
511                 ? b.mContext.getResources().getResourceName(b.mStartingThemeResId) : null;
512         updateTimestamp();
513     }
514 
515     /**
516      * Extract extras from {@link #mIntents} and set them to {@link #mIntentPersistableExtrases}
517      * as {@link PersistableBundle}, and remove extras from the original intents.
518      */
fixUpIntentExtras()519     private void fixUpIntentExtras() {
520         if (mIntents == null) {
521             mIntentPersistableExtrases = null;
522             return;
523         }
524         mIntentPersistableExtrases = new PersistableBundle[mIntents.length];
525         for (int i = 0; i < mIntents.length; i++) {
526             final Intent intent = mIntents[i];
527             final Bundle extras = intent.getExtras();
528             if (extras == null) {
529                 mIntentPersistableExtrases[i] = null;
530             } else {
531                 mIntentPersistableExtrases[i] = new PersistableBundle(extras);
532                 intent.replaceExtras((Bundle) null);
533             }
534         }
535     }
536 
cloneCategories(Set<String> source)537     private static ArraySet<String> cloneCategories(Set<String> source) {
538         if (source == null) {
539             return null;
540         }
541         final ArraySet<String> ret = new ArraySet<>(source.size());
542         for (CharSequence s : source) {
543             if (!TextUtils.isEmpty(s)) {
544                 ret.add(s.toString().intern());
545             }
546         }
547         return ret;
548     }
549 
cloneIntents(Intent[] intents)550     private static Intent[] cloneIntents(Intent[] intents) {
551         if (intents == null) {
552             return null;
553         }
554         final Intent[] ret = new Intent[intents.length];
555         for (int i = 0; i < ret.length; i++) {
556             if (intents[i] != null) {
557                 ret[i] = new Intent(intents[i]);
558             }
559         }
560         return ret;
561     }
562 
clonePersistableBundle(PersistableBundle[] bundle)563     private static PersistableBundle[] clonePersistableBundle(PersistableBundle[] bundle) {
564         if (bundle == null) {
565             return null;
566         }
567         final PersistableBundle[] ret = new PersistableBundle[bundle.length];
568         for (int i = 0; i < ret.length; i++) {
569             if (bundle[i] != null) {
570                 ret[i] = new PersistableBundle(bundle[i]);
571             }
572         }
573         return ret;
574     }
575 
clonePersons(Person[] persons)576     private static Person[] clonePersons(Person[] persons) {
577         if (persons == null) {
578             return null;
579         }
580         final Person[] ret = new Person[persons.length];
581         for (int i = 0; i < ret.length; i++) {
582             if (persons[i] != null) {
583                 // Don't need to keep the icon, remove it to save space
584                 ret[i] = persons[i].toBuilder().setIcon(null).build();
585             }
586         }
587         return ret;
588     }
589 
590     @NonNull
getSafeId(@onNull String id)591     private static String getSafeId(@NonNull String id) {
592         if (id.length() > MAX_ID_LENGTH) {
593             return id.substring(0, MAX_ID_LENGTH);
594         }
595         return id;
596     }
597 
598     /**
599      * Throws if any of the mandatory fields is not set.
600      *
601      * @hide
602      */
enforceMandatoryFields(boolean forPinned)603     public void enforceMandatoryFields(boolean forPinned) {
604         Preconditions.checkStringNotEmpty(mId, "Shortcut ID must be provided");
605         if (!forPinned) {
606             Objects.requireNonNull(mActivity, "Activity must be provided");
607         }
608         if (mTitle == null && mTitleResId == 0) {
609             throw new IllegalArgumentException("Short label must be provided");
610         }
611         Objects.requireNonNull(mIntents, "Shortcut Intent must be provided");
612         Preconditions.checkArgument(mIntents.length > 0, "Shortcut Intent must be provided");
613     }
614 
615     /**
616      * Copy constructor.
617      */
ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags)618     private ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags) {
619         mUserId = source.mUserId;
620         mId = source.mId;
621         mPackageName = source.mPackageName;
622         mActivity = source.mActivity;
623         mFlags = source.mFlags;
624         mLastChangedTimestamp = source.mLastChangedTimestamp;
625         mDisabledReason = source.mDisabledReason;
626         mLocusId = source.mLocusId;
627         mExcludedSurfaces = source.mExcludedSurfaces;
628 
629         // Just always keep it since it's cheap.
630         mIconResId = source.mIconResId;
631 
632         if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
633 
634             if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
635                 mIcon = source.mIcon;
636                 mBitmapPath = source.mBitmapPath;
637                 mIconUri = source.mIconUri;
638             }
639 
640             mTitle = source.mTitle;
641             mTitleResId = source.mTitleResId;
642             mText = source.mText;
643             mTextResId = source.mTextResId;
644             mDisabledMessage = source.mDisabledMessage;
645             mDisabledMessageResId = source.mDisabledMessageResId;
646             mCategories = cloneCategories(source.mCategories);
647             if ((cloneFlags & CLONE_REMOVE_PERSON) == 0) {
648                 mPersons = clonePersons(source.mPersons);
649             }
650             if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
651                 mIntents = cloneIntents(source.mIntents);
652                 mIntentPersistableExtrases =
653                         clonePersistableBundle(source.mIntentPersistableExtrases);
654             }
655             mRank = source.mRank;
656             mExtras = source.mExtras;
657 
658             if ((cloneFlags & CLONE_REMOVE_RES_NAMES) == 0) {
659                 mTitleResName = source.mTitleResName;
660                 mTextResName = source.mTextResName;
661                 mDisabledMessageResName = source.mDisabledMessageResName;
662                 mIconResName = source.mIconResName;
663             }
664         } else {
665             // Set this bit.
666             mFlags |= FLAG_KEY_FIELDS_ONLY;
667         }
668         mCapabilityBindings = cloneCapabilityBindings(
669                 source.mCapabilityBindings);
670         mStartingThemeResName = source.mStartingThemeResName;
671     }
672 
673     /**
674      * Convert a {@link GenericDocument} into a ShortcutInfo.
675      *
676      * @param context Client context
677      * @param document An instance of {@link GenericDocument} that represents the shortcut.
678      */
679     @NonNull
createFromGenericDocument(@onNull final Context context, @NonNull final GenericDocument document)680     public static ShortcutInfo createFromGenericDocument(@NonNull final Context context,
681             @NonNull final GenericDocument document) {
682         Objects.requireNonNull(context);
683         Objects.requireNonNull(document);
684         return createFromGenericDocument(context.getUserId(), document);
685     }
686 
687     /**
688      * @hide
689      */
createFromGenericDocument( final int userId, @NonNull final GenericDocument document)690     public static ShortcutInfo createFromGenericDocument(
691             final int userId, @NonNull final GenericDocument document) {
692         return new AppSearchShortcutInfo(document).toShortcutInfo(userId);
693     }
694 
695     /**
696      * Load a string resource from the publisher app.
697      *
698      * @param resId resource ID
699      * @param defValue default value to be returned when the specified resource isn't found.
700      */
getResourceString(Resources res, int resId, CharSequence defValue)701     private CharSequence getResourceString(Resources res, int resId, CharSequence defValue) {
702         try {
703             return res.getString(resId);
704         } catch (NotFoundException e) {
705             Log.e(TAG, "Resource for ID=" + resId + " not found in package " + mPackageName);
706             return defValue;
707         }
708     }
709 
710     /**
711      * Load the string resources for the text fields and set them to the actual value fields.
712      * This will set {@link #FLAG_STRINGS_RESOLVED}.
713      *
714      * @param res {@link Resources} for the publisher.  Must have been loaded with
715      * {@link PackageManager#getResourcesForApplication(String)}.
716      *
717      * @hide
718      */
resolveResourceStrings(@onNull Resources res)719     public void resolveResourceStrings(@NonNull Resources res) {
720         mFlags |= FLAG_STRINGS_RESOLVED;
721 
722         if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)) {
723             return; // Bail early.
724         }
725 
726         if (mTitleResId != 0) {
727             mTitle = getResourceString(res, mTitleResId, mTitle);
728         }
729         if (mTextResId != 0) {
730             mText = getResourceString(res, mTextResId, mText);
731         }
732         if (mDisabledMessageResId != 0) {
733             mDisabledMessage = getResourceString(res, mDisabledMessageResId, mDisabledMessage);
734         }
735     }
736 
737     /**
738      * Look up resource name for a given resource ID.
739      *
740      * @return a simple resource name (e.g. "text_1") when {@code withType} is false, or with the
741      * type (e.g. "string/text_1").
742      *
743      * @hide
744      */
745     @VisibleForTesting
lookUpResourceName(@onNull Resources res, int resId, boolean withType, @NonNull String packageName)746     public static String lookUpResourceName(@NonNull Resources res, int resId, boolean withType,
747             @NonNull String packageName) {
748         if (resId == 0) {
749             return null;
750         }
751         try {
752             final String fullName = res.getResourceName(resId);
753 
754             if (ANDROID_PACKAGE_NAME.equals(getResourcePackageName(fullName))) {
755                 // If it's a framework resource, the value won't change, so just return the ID
756                 // value as a string.
757                 return String.valueOf(resId);
758             }
759             return withType ? getResourceTypeAndEntryName(fullName)
760                     : getResourceEntryName(fullName);
761         } catch (NotFoundException e) {
762             Log.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName
763                     + ". Resource IDs may change when the application is upgraded, and the system"
764                     + " may not be able to find the correct resource.");
765             return null;
766         }
767     }
768 
769     /**
770      * Extract the package name from a fully-donated resource name.
771      * e.g. "com.android.app1:drawable/icon1" -> "com.android.app1"
772      * @hide
773      */
774     @VisibleForTesting
getResourcePackageName(@onNull String fullResourceName)775     public static String getResourcePackageName(@NonNull String fullResourceName) {
776         final int p1 = fullResourceName.indexOf(':');
777         if (p1 < 0) {
778             return null;
779         }
780         return fullResourceName.substring(0, p1);
781     }
782 
783     /**
784      * Extract the type name from a fully-donated resource name.
785      * e.g. "com.android.app1:drawable/icon1" -> "drawable"
786      * @hide
787      */
788     @VisibleForTesting
getResourceTypeName(@onNull String fullResourceName)789     public static String getResourceTypeName(@NonNull String fullResourceName) {
790         final int p1 = fullResourceName.indexOf(':');
791         if (p1 < 0) {
792             return null;
793         }
794         final int p2 = fullResourceName.indexOf('/', p1 + 1);
795         if (p2 < 0) {
796             return null;
797         }
798         return fullResourceName.substring(p1 + 1, p2);
799     }
800 
801     /**
802      * Extract the type name + the entry name from a fully-donated resource name.
803      * e.g. "com.android.app1:drawable/icon1" -> "drawable/icon1"
804      * @hide
805      */
806     @VisibleForTesting
getResourceTypeAndEntryName(@onNull String fullResourceName)807     public static String getResourceTypeAndEntryName(@NonNull String fullResourceName) {
808         final int p1 = fullResourceName.indexOf(':');
809         if (p1 < 0) {
810             return null;
811         }
812         return fullResourceName.substring(p1 + 1);
813     }
814 
815     /**
816      * Extract the entry name from a fully-donated resource name.
817      * e.g. "com.android.app1:drawable/icon1" -> "icon1"
818      * @hide
819      */
820     @VisibleForTesting
getResourceEntryName(@onNull String fullResourceName)821     public static String getResourceEntryName(@NonNull String fullResourceName) {
822         final int p1 = fullResourceName.indexOf('/');
823         if (p1 < 0) {
824             return null;
825         }
826         return fullResourceName.substring(p1 + 1);
827     }
828 
829     /**
830      * Return the resource ID for a given resource ID.
831      *
832      * Basically its' a wrapper over {@link Resources#getIdentifier(String, String, String)}, except
833      * if {@code resourceName} is an integer then it'll just return its value.  (Which also the
834      * aforementioned method would do internally, but not documented, so doing here explicitly.)
835      *
836      * @param res {@link Resources} for the publisher.  Must have been loaded with
837      * {@link PackageManager#getResourcesForApplication(String)}.
838      *
839      * @hide
840      */
841     @VisibleForTesting
lookUpResourceId(@onNull Resources res, @Nullable String resourceName, @Nullable String resourceType, String packageName)842     public static int lookUpResourceId(@NonNull Resources res, @Nullable String resourceName,
843             @Nullable String resourceType, String packageName) {
844         if (resourceName == null) {
845             return 0;
846         }
847         try {
848             try {
849                 // It the name can be parsed as an integer, just use it.
850                 return Integer.parseInt(resourceName);
851             } catch (NumberFormatException ignore) {
852             }
853 
854             return res.getIdentifier(resourceName, resourceType, packageName);
855         } catch (NotFoundException e) {
856             Log.e(TAG, "Resource ID for name=" + resourceName + " not found in package "
857                     + packageName);
858             return 0;
859         }
860     }
861 
862     /**
863      * Look up resource names from the resource IDs for the icon res and the text fields, and fill
864      * in the resource name fields.
865      *
866      * @param res {@link Resources} for the publisher.  Must have been loaded with
867      * {@link PackageManager#getResourcesForApplication(String)}.
868      *
869      * @hide
870      */
lookupAndFillInResourceNames(@onNull Resources res)871     public void lookupAndFillInResourceNames(@NonNull Resources res) {
872         if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)
873                 && (mIconResId == 0)) {
874             return; // Bail early.
875         }
876 
877         // We don't need types for strings because their types are always "string".
878         mTitleResName = lookUpResourceName(res, mTitleResId, /*withType=*/ false, mPackageName);
879         mTextResName = lookUpResourceName(res, mTextResId, /*withType=*/ false, mPackageName);
880         mDisabledMessageResName = lookUpResourceName(res, mDisabledMessageResId,
881                 /*withType=*/ false, mPackageName);
882 
883         // But icons have multiple possible types, so include the type.
884         mIconResName = lookUpResourceName(res, mIconResId, /*withType=*/ true, mPackageName);
885     }
886 
887     /**
888      * Look up resource IDs from the resource names for the icon res and the text fields, and fill
889      * in the resource ID fields.
890      *
891      * This is called when an app is updated.
892      *
893      * @hide
894      */
lookupAndFillInResourceIds(@onNull Resources res)895     public void lookupAndFillInResourceIds(@NonNull Resources res) {
896         if ((mTitleResName == null) && (mTextResName == null) && (mDisabledMessageResName == null)
897                 && (mIconResName == null)) {
898             return; // Bail early.
899         }
900 
901         mTitleResId = lookUpResourceId(res, mTitleResName, RES_TYPE_STRING, mPackageName);
902         mTextResId = lookUpResourceId(res, mTextResName, RES_TYPE_STRING, mPackageName);
903         mDisabledMessageResId = lookUpResourceId(res, mDisabledMessageResName, RES_TYPE_STRING,
904                 mPackageName);
905 
906         // mIconResName already contains the type, so the third argument is not needed.
907         mIconResId = lookUpResourceId(res, mIconResName, null, mPackageName);
908     }
909 
910     /**
911      * Copy a {@link ShortcutInfo}, optionally removing fields.
912      * @hide
913      */
clone(@loneFlags int cloneFlags)914     public ShortcutInfo clone(@CloneFlags int cloneFlags) {
915         return new ShortcutInfo(this, cloneFlags);
916     }
917 
918     /**
919      * @hide
920      *
921      * @isUpdating set true if it's "update", as opposed to "replace".
922      */
ensureUpdatableWith(ShortcutInfo source, boolean isUpdating)923     public void ensureUpdatableWith(ShortcutInfo source, boolean isUpdating) {
924         if (isUpdating) {
925             Preconditions.checkState(isVisibleToPublisher(),
926                     "[Framework BUG] Invisible shortcuts can't be updated");
927         }
928         Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
929         Preconditions.checkState(mId.equals(source.mId), "ID must match");
930         Preconditions.checkState(mPackageName.equals(source.mPackageName),
931                 "Package name must match");
932 
933         if (isVisibleToPublisher()) {
934             // Don't do this check for restore-blocked shortcuts.
935             Preconditions.checkState(!isImmutable(), "Target ShortcutInfo is immutable");
936         }
937     }
938 
939     /**
940      * Copy non-null/zero fields from another {@link ShortcutInfo}.  Only "public" information
941      * will be overwritten.  The timestamp will *not* be updated to be consistent with other
942      * setters (and also the clock is not injectable in this file).
943      *
944      * - Flags will not change
945      * - mBitmapPath will not change
946      * - Current time will be set to timestamp
947      *
948      * @throws IllegalStateException if source is not compatible.
949      *
950      * @hide
951      */
copyNonNullFieldsFrom(ShortcutInfo source)952     public void copyNonNullFieldsFrom(ShortcutInfo source) {
953         ensureUpdatableWith(source, /*isUpdating=*/ true);
954 
955         if (source.mActivity != null) {
956             mActivity = source.mActivity;
957         }
958 
959         if (source.mIcon != null) {
960             mIcon = source.mIcon;
961 
962             mIconResId = 0;
963             mIconResName = null;
964             mBitmapPath = null;
965             mIconUri = null;
966         }
967         if (source.mTitle != null) {
968             mTitle = source.mTitle;
969             mTitleResId = 0;
970             mTitleResName = null;
971         } else if (source.mTitleResId != 0) {
972             mTitle = null;
973             mTitleResId = source.mTitleResId;
974             mTitleResName = null;
975         }
976 
977         if (source.mText != null) {
978             mText = source.mText;
979             mTextResId = 0;
980             mTextResName = null;
981         } else if (source.mTextResId != 0) {
982             mText = null;
983             mTextResId = source.mTextResId;
984             mTextResName = null;
985         }
986         if (source.mDisabledMessage != null) {
987             mDisabledMessage = source.mDisabledMessage;
988             mDisabledMessageResId = 0;
989             mDisabledMessageResName = null;
990         } else if (source.mDisabledMessageResId != 0) {
991             mDisabledMessage = null;
992             mDisabledMessageResId = source.mDisabledMessageResId;
993             mDisabledMessageResName = null;
994         }
995         if (source.mCategories != null) {
996             mCategories = cloneCategories(source.mCategories);
997         }
998         if (source.mPersons != null) {
999             mPersons = clonePersons(source.mPersons);
1000         }
1001         if (source.mIntents != null) {
1002             mIntents = cloneIntents(source.mIntents);
1003             mIntentPersistableExtrases =
1004                     clonePersistableBundle(source.mIntentPersistableExtrases);
1005         }
1006         if (source.mRank != RANK_NOT_SET) {
1007             mRank = source.mRank;
1008         }
1009         if (source.mExtras != null) {
1010             mExtras = source.mExtras;
1011         }
1012 
1013         if (source.mLocusId != null) {
1014             mLocusId = source.mLocusId;
1015         }
1016         if (source.mStartingThemeResName != null && !source.mStartingThemeResName.isEmpty()) {
1017             mStartingThemeResName = source.mStartingThemeResName;
1018         }
1019         if (source.mCapabilityBindings != null) {
1020             mCapabilityBindings =
1021                     cloneCapabilityBindings(source.mCapabilityBindings);
1022         }
1023     }
1024 
1025     /**
1026      * @hide
1027      */
validateIcon(Icon icon)1028     public static Icon validateIcon(Icon icon) {
1029         switch (icon.getType()) {
1030             case Icon.TYPE_RESOURCE:
1031             case Icon.TYPE_BITMAP:
1032             case Icon.TYPE_ADAPTIVE_BITMAP:
1033             case Icon.TYPE_URI:
1034             case Icon.TYPE_URI_ADAPTIVE_BITMAP:
1035                 break; // OK
1036             default:
1037                 throw getInvalidIconException();
1038         }
1039         if (icon.hasTint()) {
1040             throw new IllegalArgumentException("Icons with tints are not supported");
1041         }
1042 
1043         return icon;
1044     }
1045 
1046     /** @hide */
getInvalidIconException()1047     public static IllegalArgumentException getInvalidIconException() {
1048         return new IllegalArgumentException("Unsupported icon type:"
1049                 +" only the bitmap and resource types are supported");
1050     }
1051 
1052     /**
1053      * Builder class for {@link ShortcutInfo} objects.
1054      *
1055      * @see ShortcutManager
1056      */
1057     public static class Builder {
1058         private final Context mContext;
1059 
1060         private String mId;
1061 
1062         private ComponentName mActivity;
1063 
1064         private Icon mIcon;
1065 
1066         private int mTitleResId;
1067 
1068         private CharSequence mTitle;
1069 
1070         private int mTextResId;
1071 
1072         private CharSequence mText;
1073 
1074         private int mDisabledMessageResId;
1075 
1076         private CharSequence mDisabledMessage;
1077 
1078         private Set<String> mCategories;
1079 
1080         private Intent[] mIntents;
1081 
1082         private Person[] mPersons;
1083 
1084         private boolean mIsLongLived;
1085 
1086         private int mRank = RANK_NOT_SET;
1087 
1088         private PersistableBundle mExtras;
1089 
1090         private LocusId mLocusId;
1091 
1092         private int mStartingThemeResId;
1093 
1094         @Nullable
1095         private Map<String, Map<String, List<String>>> mCapabilityBindings;
1096 
1097         private int mExcludedSurfaces;
1098 
1099         /**
1100          * Old style constructor.
1101          * @hide
1102          */
1103         @Deprecated
Builder(Context context)1104         public Builder(Context context) {
1105             mContext = context;
1106         }
1107 
1108         /**
1109          * Used with the old style constructor, kept for unit tests.
1110          * @hide
1111          */
1112         @NonNull
1113         @Deprecated
setId(@onNull String id)1114         public Builder setId(@NonNull String id) {
1115             mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty");
1116             return this;
1117         }
1118 
1119         /**
1120          * Constructor.
1121          *
1122          * @param context Client context.
1123          * @param id ID of the shortcut.
1124          */
Builder(Context context, String id)1125         public Builder(Context context, String id) {
1126             mContext = context;
1127             mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty");
1128         }
1129 
1130         /**
1131          * Sets the {@link LocusId} associated with this shortcut.
1132          *
1133          * <p>This method should be called when the {@link LocusId} is used in other places (such
1134          * as {@link Notification} and {@link ContentCaptureContext}) so the device's intelligence
1135          * services can correlate them.
1136          */
1137         @NonNull
setLocusId(@onNull LocusId locusId)1138         public Builder setLocusId(@NonNull LocusId locusId) {
1139             mLocusId = Objects.requireNonNull(locusId, "locusId cannot be null");
1140             return this;
1141         }
1142 
1143         /**
1144          * Sets the target activity.  A shortcut will be shown along with this activity's icon
1145          * on the launcher.
1146          *
1147          * When selecting a target activity, keep the following in mind:
1148          * <ul>
1149          * <li>All dynamic shortcuts must have a target activity.  When a shortcut with no target
1150          * activity is published using
1151          * {@link ShortcutManager#addDynamicShortcuts(List)} or
1152          * {@link ShortcutManager#setDynamicShortcuts(List)},
1153          * the first main activity defined in the app's <code>AndroidManifest.xml</code>
1154          * file is used.
1155          *
1156          * <li>Only "main" activities&mdash;ones that define the {@link Intent#ACTION_MAIN}
1157          * and {@link Intent#CATEGORY_LAUNCHER} intent filters&mdash;can be target
1158          * activities.
1159          *
1160          * <li>By default, the first main activity defined in the app's manifest is
1161          * the target activity.
1162          *
1163          * <li>A target activity must belong to the publisher app.
1164          * </ul>
1165          *
1166          * @see ShortcutInfo#getActivity()
1167          */
1168         @NonNull
setActivity(@onNull ComponentName activity)1169         public Builder setActivity(@NonNull ComponentName activity) {
1170             mActivity = Objects.requireNonNull(activity, "activity cannot be null");
1171             return this;
1172         }
1173 
1174         /**
1175          * Sets an icon of a shortcut.
1176          *
1177          * <p>Icons are not available on {@link ShortcutInfo} instances
1178          * returned by {@link ShortcutManager} or {@link LauncherApps}.  The default launcher
1179          * app can use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}
1180          * or {@link LauncherApps#getShortcutBadgedIconDrawable(ShortcutInfo, int)} to fetch
1181          * shortcut icons.
1182          *
1183          * <p>Tints set with {@link Icon#setTint} or {@link Icon#setTintList} are not supported
1184          * and will be ignored.
1185          *
1186          * <p>Only icons created with {@link Icon#createWithBitmap(Bitmap)},
1187          * {@link Icon#createWithAdaptiveBitmap(Bitmap)}
1188          * and {@link Icon#createWithResource} are supported.
1189          * Other types, such as URI-based icons, are not supported.
1190          *
1191          * @see LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)
1192          * @see LauncherApps#getShortcutBadgedIconDrawable(ShortcutInfo, int)
1193          */
1194         @NonNull
setIcon(Icon icon)1195         public Builder setIcon(Icon icon) {
1196             mIcon = validateIcon(icon);
1197             return this;
1198         }
1199 
1200         /**
1201          * Sets a theme resource id for the splash screen.
1202          */
1203         @NonNull
setStartingTheme(int themeResId)1204         public Builder setStartingTheme(int themeResId) {
1205             mStartingThemeResId = themeResId;
1206             return this;
1207         }
1208 
1209         /**
1210          * @hide We don't support resource strings for dynamic shortcuts for now.  (But unit tests
1211          * use it.)
1212          */
1213         @Deprecated
setShortLabelResId(int shortLabelResId)1214         public Builder setShortLabelResId(int shortLabelResId) {
1215             Preconditions.checkState(mTitle == null, "shortLabel already set");
1216             mTitleResId = shortLabelResId;
1217             return this;
1218         }
1219 
1220         /**
1221          * Sets the short title of a shortcut.
1222          *
1223          * <p>This is a mandatory field when publishing a new shortcut with
1224          * {@link ShortcutManager#addDynamicShortcuts(List)} or
1225          * {@link ShortcutManager#setDynamicShortcuts(List)}.
1226          *
1227          * <p>This field is intended to be a concise description of a shortcut.
1228          *
1229          * <p>The recommended maximum length is 10 characters.
1230          *
1231          * @see ShortcutInfo#getShortLabel()
1232          */
1233         @NonNull
setShortLabel(@onNull CharSequence shortLabel)1234         public Builder setShortLabel(@NonNull CharSequence shortLabel) {
1235             Preconditions.checkState(mTitleResId == 0, "shortLabelResId already set");
1236             mTitle = Preconditions.checkStringNotEmpty(shortLabel, "shortLabel cannot be empty");
1237             return this;
1238         }
1239 
1240         /**
1241          * @hide We don't support resource strings for dynamic shortcuts for now.  (But unit tests
1242          * use it.)
1243          */
1244         @Deprecated
setLongLabelResId(int longLabelResId)1245         public Builder setLongLabelResId(int longLabelResId) {
1246             Preconditions.checkState(mText == null, "longLabel already set");
1247             mTextResId = longLabelResId;
1248             return this;
1249         }
1250 
1251         /**
1252          * Sets the text of a shortcut.
1253          *
1254          * <p>This field is intended to be more descriptive than the shortcut title.  The launcher
1255          * shows this instead of the short title when it has enough space.
1256          *
1257          * <p>The recommend maximum length is 25 characters.
1258          *
1259          * @see ShortcutInfo#getLongLabel()
1260          */
1261         @NonNull
setLongLabel(@onNull CharSequence longLabel)1262         public Builder setLongLabel(@NonNull CharSequence longLabel) {
1263             Preconditions.checkState(mTextResId == 0, "longLabelResId already set");
1264             mText = Preconditions.checkStringNotEmpty(longLabel, "longLabel cannot be empty");
1265             return this;
1266         }
1267 
1268         /** @hide -- old signature, the internal code still uses it. */
1269         @Deprecated
setTitle(@onNull CharSequence value)1270         public Builder setTitle(@NonNull CharSequence value) {
1271             return setShortLabel(value);
1272         }
1273 
1274         /** @hide -- old signature, the internal code still uses it. */
1275         @Deprecated
setTitleResId(int value)1276         public Builder setTitleResId(int value) {
1277             return setShortLabelResId(value);
1278         }
1279 
1280         /** @hide -- old signature, the internal code still uses it. */
1281         @Deprecated
setText(@onNull CharSequence value)1282         public Builder setText(@NonNull CharSequence value) {
1283             return setLongLabel(value);
1284         }
1285 
1286         /** @hide -- old signature, the internal code still uses it. */
1287         @Deprecated
setTextResId(int value)1288         public Builder setTextResId(int value) {
1289             return setLongLabelResId(value);
1290         }
1291 
1292         /**
1293          * @hide We don't support resource strings for dynamic shortcuts for now.  (But unit tests
1294          * use it.)
1295          */
1296         @Deprecated
setDisabledMessageResId(int disabledMessageResId)1297         public Builder setDisabledMessageResId(int disabledMessageResId) {
1298             Preconditions.checkState(mDisabledMessage == null, "disabledMessage already set");
1299             mDisabledMessageResId = disabledMessageResId;
1300             return this;
1301         }
1302 
1303         /**
1304          * Sets the message that should be shown when the user attempts to start a shortcut that
1305          * is disabled.
1306          *
1307          * @see ShortcutInfo#getDisabledMessage()
1308          */
1309         @NonNull
setDisabledMessage(@onNull CharSequence disabledMessage)1310         public Builder setDisabledMessage(@NonNull CharSequence disabledMessage) {
1311             Preconditions.checkState(
1312                     mDisabledMessageResId == 0, "disabledMessageResId already set");
1313             mDisabledMessage =
1314                     Preconditions.checkStringNotEmpty(disabledMessage,
1315                             "disabledMessage cannot be empty");
1316             return this;
1317         }
1318 
1319         /**
1320          * Sets categories for a shortcut.
1321          * <ul>
1322          * <li>Launcher apps may use this information to categorize shortcuts
1323          * <li> Used by the system to associate a published Sharing Shortcut with supported
1324          * mimeTypes. Required for published Sharing Shortcuts with a matching category
1325          * declared in share targets, defined in the app's manifest linked shortcuts xml file.
1326          * </ul>
1327          *
1328          * @see #SHORTCUT_CATEGORY_CONVERSATION
1329          * @see ShortcutInfo#getCategories()
1330          */
1331         @NonNull
setCategories(Set<String> categories)1332         public Builder setCategories(Set<String> categories) {
1333             mCategories = categories;
1334             return this;
1335         }
1336 
1337         /**
1338          * Sets the intent of a shortcut.  Alternatively, {@link #setIntents(Intent[])} can be used
1339          * to launch an activity with other activities in the back stack.
1340          *
1341          * <p>This is a mandatory field when publishing a new shortcut with
1342          * {@link ShortcutManager#addDynamicShortcuts(List)} or
1343          * {@link ShortcutManager#setDynamicShortcuts(List)}.
1344          *
1345          * <p>A shortcut can launch any intent that the publisher app has permission to
1346          * launch.  For example, a shortcut can launch an unexported activity within the publisher
1347          * app.  A shortcut intent doesn't have to point at the target activity.
1348          *
1349          * <p>The given {@code intent} can contain extras, but these extras must contain values
1350          * of primitive types in order for the system to persist these values.
1351          *
1352          * @see ShortcutInfo#getIntent()
1353          * @see #setIntents(Intent[])
1354          */
1355         @NonNull
setIntent(@onNull Intent intent)1356         public Builder setIntent(@NonNull Intent intent) {
1357             return setIntents(new Intent[]{intent});
1358         }
1359 
1360         /**
1361          * Sets multiple intents instead of a single intent, in order to launch an activity with
1362          * other activities in back stack.  Use {@link TaskStackBuilder} to build intents. The
1363          * last element in the list represents the only intent that doesn't place an activity on
1364          * the back stack.
1365          * See the {@link ShortcutManager} javadoc for details.
1366          *
1367          * @see Builder#setIntent(Intent)
1368          * @see ShortcutInfo#getIntents()
1369          * @see Context#startActivities(Intent[])
1370          * @see TaskStackBuilder
1371          */
1372         @NonNull
setIntents(@onNull Intent[] intents)1373         public Builder setIntents(@NonNull Intent[] intents) {
1374             Objects.requireNonNull(intents, "intents cannot be null");
1375             Objects.requireNonNull(intents.length, "intents cannot be empty");
1376             for (Intent intent : intents) {
1377                 Objects.requireNonNull(intent, "intents cannot contain null");
1378                 Objects.requireNonNull(intent.getAction(), "intent's action must be set");
1379             }
1380             // Make sure always clone incoming intents.
1381             mIntents = cloneIntents(intents);
1382             return this;
1383         }
1384 
1385         /**
1386          * Add a person that is relevant to this shortcut. Alternatively,
1387          * {@link #setPersons(Person[])} can be used to add multiple persons to a shortcut.
1388          *
1389          * <p> This is an optional field, but the addition of person may cause this shortcut to
1390          * appear more prominently in the user interface (e.g. ShareSheet).
1391          *
1392          * <p> A person should usually contain a uri in order to benefit from the ranking boost.
1393          * However, even if no uri is provided, it's beneficial to provide people in the shortcut,
1394          * such that listeners and voice only devices can announce and handle them properly.
1395          *
1396          * @see Person
1397          * @see #setPersons(Person[])
1398          */
1399         @NonNull
setPerson(@onNull Person person)1400         public Builder setPerson(@NonNull Person person) {
1401             return setPersons(new Person[]{person});
1402         }
1403 
1404         /**
1405          * Sets multiple persons instead of a single person.
1406          *
1407          * @see Person
1408          * @see #setPerson(Person)
1409          */
1410         @NonNull
setPersons(@onNull Person[] persons)1411         public Builder setPersons(@NonNull Person[] persons) {
1412             Objects.requireNonNull(persons, "persons cannot be null");
1413             Objects.requireNonNull(persons.length, "persons cannot be empty");
1414             for (Person person : persons) {
1415                 Objects.requireNonNull(person, "persons cannot contain null");
1416             }
1417             mPersons = clonePersons(persons);
1418             return this;
1419         }
1420 
1421         /**
1422          * Sets if a shortcut would be valid even if it has been unpublished/invisible by the app
1423          * (as a dynamic or pinned shortcut). If it is long lived, it can be cached by various
1424          * system services even after it has been unpublished as a dynamic shortcut.
1425          */
1426         @NonNull
setLongLived(boolean longLived)1427         public Builder setLongLived(boolean longLived) {
1428             mIsLongLived = longLived;
1429             return this;
1430         }
1431 
1432         /**
1433          * "Rank" of a shortcut, which is a non-negative value that's used by the launcher app
1434          * to sort shortcuts.
1435          *
1436          * See {@link ShortcutInfo#getRank()} for details.
1437          */
1438         @NonNull
setRank(int rank)1439         public Builder setRank(int rank) {
1440             Preconditions.checkArgument((0 <= rank),
1441                     "Rank cannot be negative or bigger than MAX_RANK");
1442             mRank = rank;
1443             return this;
1444         }
1445 
1446         /**
1447          * Extras that the app can set for any purpose.
1448          *
1449          * <p>Apps can store arbitrary shortcut metadata in extras and retrieve the
1450          * metadata later using {@link ShortcutInfo#getExtras()}.
1451          */
1452         @NonNull
setExtras(@onNull PersistableBundle extras)1453         public Builder setExtras(@NonNull PersistableBundle extras) {
1454             mExtras = extras;
1455             return this;
1456         }
1457 
1458         /**
1459          * Associates a shortcut with a capability, and a parameter of that capability. Used when
1460          * the shortcut is an instance of a capability.
1461          *
1462          * <P>This method can be called multiple times to add multiple parameters to the same
1463          * capability.
1464          *
1465          * @param capability {@link Capability} associated with the shortcut.
1466          * @param capabilityParams Optional {@link CapabilityParams} associated with given
1467          *                        capability.
1468          */
1469         @NonNull
addCapabilityBinding(@onNull final Capability capability, @Nullable final CapabilityParams capabilityParams)1470         public Builder addCapabilityBinding(@NonNull final Capability capability,
1471                 @Nullable final CapabilityParams capabilityParams) {
1472             Objects.requireNonNull(capability);
1473             if (mCapabilityBindings == null) {
1474                 mCapabilityBindings = new ArrayMap<>(1);
1475             }
1476             if (!mCapabilityBindings.containsKey(capability.getName())) {
1477                 mCapabilityBindings.put(capability.getName(), new ArrayMap<>(0));
1478             }
1479             if (capabilityParams == null) {
1480                 return this;
1481             }
1482             final Map<String, List<String>> params = mCapabilityBindings.get(capability.getName());
1483             params.put(capabilityParams.getName(), capabilityParams.getValues());
1484             return this;
1485         }
1486 
1487         /**
1488          * Sets which surfaces a shortcut will be excluded from.
1489          *
1490          * If the shortcut is set to be excluded from {@link #SURFACE_LAUNCHER}, shortcuts will be
1491          * excluded from the search result of {@link android.content.pm.LauncherApps#getShortcuts(
1492          * android.content.pm.LauncherApps.ShortcutQuery, UserHandle)} nor
1493          * {@link android.content.pm.ShortcutManager#getShortcuts(int)}. This generally means the
1494          * shortcut would not be displayed by a launcher app (e.g. in Long-Press menu), while
1495          * remain visible in other surfaces such as assistant or on-device-intelligence.
1496          */
1497         @NonNull
setExcludedFromSurfaces(final int surfaces)1498         public Builder setExcludedFromSurfaces(final int surfaces) {
1499             mExcludedSurfaces = surfaces;
1500             return this;
1501         }
1502 
1503         /**
1504          * Creates a {@link ShortcutInfo} instance.
1505          */
1506         @NonNull
build()1507         public ShortcutInfo build() {
1508             return new ShortcutInfo(this);
1509         }
1510     }
1511 
1512     /**
1513      * Returns the ID of a shortcut.
1514      *
1515      * <p>Shortcut IDs are unique within each publisher app and must be stable across
1516      * devices so that shortcuts will still be valid when restored on a different device.
1517      * See {@link ShortcutManager} for details.
1518      */
1519     @NonNull
getId()1520     public String getId() {
1521         return mId;
1522     }
1523 
1524     /**
1525      * Gets the {@link LocusId} associated with this shortcut.
1526      *
1527      * <p>Used by the device's intelligence services to correlate objects (such as
1528      * {@link Notification} and {@link ContentCaptureContext}) that are correlated.
1529      */
1530     @Nullable
getLocusId()1531     public LocusId getLocusId() {
1532         return mLocusId;
1533     }
1534 
1535     /**
1536      * Return the package name of the publisher app.
1537      */
1538     @NonNull
getPackage()1539     public String getPackage() {
1540         return mPackageName;
1541     }
1542 
1543     /**
1544      * Return the target activity.
1545      *
1546      * <p>This has nothing to do with the activity that this shortcut will launch.
1547      * Launcher apps should show the launcher icon for the returned activity alongside
1548      * this shortcut.
1549      *
1550      * @see Builder#setActivity
1551      */
1552     @Nullable
getActivity()1553     public ComponentName getActivity() {
1554         return mActivity;
1555     }
1556 
1557     /** @hide */
setActivity(ComponentName activity)1558     public void setActivity(ComponentName activity) {
1559         mActivity = activity;
1560     }
1561 
1562     /**
1563      * Returns the shortcut icon.
1564      *
1565      * @hide
1566      */
1567     @Nullable
1568     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
getIcon()1569     public Icon getIcon() {
1570         return mIcon;
1571     }
1572 
1573     /**
1574      * Returns the theme resource name used for the splash screen.
1575      * @hide
1576      */
1577     @Nullable
getStartingThemeResName()1578     public String getStartingThemeResName() {
1579         return mStartingThemeResName;
1580     }
1581 
1582     /** @hide -- old signature, the internal code still uses it. */
1583     @Nullable
1584     @Deprecated
getTitle()1585     public CharSequence getTitle() {
1586         return mTitle;
1587     }
1588 
1589     /** @hide -- old signature, the internal code still uses it. */
1590     @Deprecated
getTitleResId()1591     public int getTitleResId() {
1592         return mTitleResId;
1593     }
1594 
1595     /** @hide -- old signature, the internal code still uses it. */
1596     @Nullable
1597     @Deprecated
getText()1598     public CharSequence getText() {
1599         return mText;
1600     }
1601 
1602     /** @hide -- old signature, the internal code still uses it. */
1603     @Deprecated
getTextResId()1604     public int getTextResId() {
1605         return mTextResId;
1606     }
1607 
1608     /**
1609      * Return the short description of a shortcut.
1610      *
1611      * @see Builder#setShortLabel(CharSequence)
1612      */
1613     @Nullable
getShortLabel()1614     public CharSequence getShortLabel() {
1615         return mTitle;
1616     }
1617 
1618     /** @hide */
getShortLabelResourceId()1619     public int getShortLabelResourceId() {
1620         return mTitleResId;
1621     }
1622 
1623     /**
1624      * Return the long description of a shortcut.
1625      *
1626      * @see Builder#setLongLabel(CharSequence)
1627      */
1628     @Nullable
getLongLabel()1629     public CharSequence getLongLabel() {
1630         return mText;
1631     }
1632 
1633     /**
1634      * Returns the {@link #getLongLabel()} if it's populated, and if not, the
1635      * {@link #getShortLabel()}.
1636      * @hide
1637      */
1638     @Nullable
getLabel()1639     public CharSequence getLabel() {
1640         CharSequence label = getLongLabel();
1641         if (TextUtils.isEmpty(label)) {
1642             label = getShortLabel();
1643         }
1644 
1645         return label;
1646     }
1647 
1648     /** @hide */
getLongLabelResourceId()1649     public int getLongLabelResourceId() {
1650         return mTextResId;
1651     }
1652 
1653     /**
1654      * Return the message that should be shown when the user attempts to start a shortcut
1655      * that is disabled.
1656      *
1657      * @see Builder#setDisabledMessage(CharSequence)
1658      */
1659     @Nullable
getDisabledMessage()1660     public CharSequence getDisabledMessage() {
1661         return mDisabledMessage;
1662     }
1663 
1664     /** @hide */
getDisabledMessageResourceId()1665     public int getDisabledMessageResourceId() {
1666         return mDisabledMessageResId;
1667     }
1668 
1669     /** @hide */
setDisabledReason(@isabledReason int reason)1670     public void setDisabledReason(@DisabledReason int reason) {
1671         mDisabledReason = reason;
1672     }
1673 
1674     /**
1675      * Returns why a shortcut has been disabled.
1676      */
1677     @DisabledReason
getDisabledReason()1678     public int getDisabledReason() {
1679         return mDisabledReason;
1680     }
1681 
1682     /**
1683      * Return the shortcut's categories.
1684      *
1685      * @see Builder#setCategories(Set)
1686      */
1687     @Nullable
getCategories()1688     public Set<String> getCategories() {
1689         return mCategories;
1690     }
1691 
1692     /**
1693      * Returns the intent that is executed when the user selects this shortcut.
1694      * If setIntents() was used, then return the last intent in the array.
1695      *
1696      * <p>Launcher apps <b>cannot</b> see the intent.  If a {@link ShortcutInfo} is
1697      * obtained via {@link LauncherApps}, then this method will always return null.
1698      * Launchers can only start a shortcut intent with {@link LauncherApps#startShortcut}.
1699      *
1700      * @see Builder#setIntent(Intent)
1701      */
1702     @Nullable
getIntent()1703     public Intent getIntent() {
1704         if (mIntents == null || mIntents.length == 0) {
1705             return null;
1706         }
1707         final int last = mIntents.length - 1;
1708         final Intent intent = new Intent(mIntents[last]);
1709         return setIntentExtras(intent, mIntentPersistableExtrases[last]);
1710     }
1711 
1712     /**
1713      * Return the intent set with {@link Builder#setIntents(Intent[])}.
1714      *
1715      * <p>Launcher apps <b>cannot</b> see the intents.  If a {@link ShortcutInfo} is
1716      * obtained via {@link LauncherApps}, then this method will always return null.
1717      * Launchers can only start a shortcut intent with {@link LauncherApps#startShortcut}.
1718      *
1719      * @see Builder#setIntents(Intent[])
1720      */
1721     @Nullable
getIntents()1722     public Intent[] getIntents() {
1723         if (mIntents == null) {
1724             return null;
1725         }
1726         final Intent[] ret = new Intent[mIntents.length];
1727 
1728         for (int i = 0; i < ret.length; i++) {
1729             ret[i] = new Intent(mIntents[i]);
1730             setIntentExtras(ret[i], mIntentPersistableExtrases[i]);
1731         }
1732 
1733         return ret;
1734     }
1735 
1736     /**
1737      * Return "raw" intents, which is the original intents without the extras.
1738      * @hide
1739      */
1740     @Nullable
getIntentsNoExtras()1741     public Intent[] getIntentsNoExtras() {
1742         return mIntents;
1743     }
1744 
1745     /**
1746      * Return the Persons set with {@link Builder#setPersons(Person[])}.
1747      *
1748      * @hide
1749      */
1750     @Nullable
1751     @SystemApi
getPersons()1752     public Person[] getPersons() {
1753         return clonePersons(mPersons);
1754     }
1755 
1756     /**
1757      * The extras in the intents.  We convert extras into {@link PersistableBundle} so we can
1758      * persist them.
1759      * @hide
1760      */
1761     @Nullable
getIntentPersistableExtrases()1762     public PersistableBundle[] getIntentPersistableExtrases() {
1763         return mIntentPersistableExtrases;
1764     }
1765 
1766     /**
1767      * "Rank" of a shortcut, which is a non-negative, sequential value that's unique for each
1768      * {@link #getActivity} for each of the two types of shortcuts (static and dynamic).
1769      *
1770      * <p><em>Floating shortcuts</em>, or shortcuts that are neither static nor dynamic, will all
1771      * have rank 0, because they aren't sorted.
1772      *
1773      * See the {@link ShortcutManager}'s class javadoc for details.
1774      *
1775      * @see Builder#setRank(int)
1776      */
getRank()1777     public int getRank() {
1778         return mRank;
1779     }
1780 
1781     /** @hide */
hasRank()1782     public boolean hasRank() {
1783         return mRank != RANK_NOT_SET;
1784     }
1785 
1786     /** @hide */
setRank(int rank)1787     public void setRank(int rank) {
1788         mRank = rank;
1789     }
1790 
1791     /** @hide */
clearImplicitRankAndRankChangedFlag()1792     public void clearImplicitRankAndRankChangedFlag() {
1793         mImplicitRank = 0;
1794     }
1795 
1796     /** @hide */
setImplicitRank(int rank)1797     public void setImplicitRank(int rank) {
1798         // Make sure to keep RANK_CHANGED_BIT.
1799         mImplicitRank = (mImplicitRank & RANK_CHANGED_BIT) | (rank & IMPLICIT_RANK_MASK);
1800     }
1801 
1802     /** @hide */
getImplicitRank()1803     public int getImplicitRank() {
1804         return mImplicitRank & IMPLICIT_RANK_MASK;
1805     }
1806 
1807     /** @hide */
setRankChanged()1808     public void setRankChanged() {
1809         mImplicitRank |= RANK_CHANGED_BIT;
1810     }
1811 
1812     /** @hide */
isRankChanged()1813     public boolean isRankChanged() {
1814         return (mImplicitRank & RANK_CHANGED_BIT) != 0;
1815     }
1816 
1817     /**
1818      * Extras that the app can set for any purpose.
1819      *
1820      * @see Builder#setExtras(PersistableBundle)
1821      */
1822     @Nullable
getExtras()1823     public PersistableBundle getExtras() {
1824         return mExtras;
1825     }
1826 
1827     /** @hide */
getUserId()1828     public int getUserId() {
1829         return mUserId;
1830     }
1831 
1832     /**
1833      * {@link UserHandle} on which the publisher created this shortcut.
1834      */
getUserHandle()1835     public UserHandle getUserHandle() {
1836         return UserHandle.of(mUserId);
1837     }
1838 
1839     /**
1840      * Last time when any of the fields was updated.
1841      */
getLastChangedTimestamp()1842     public long getLastChangedTimestamp() {
1843         return mLastChangedTimestamp;
1844     }
1845 
1846     /** @hide */
1847     @ShortcutFlags
getFlags()1848     public int getFlags() {
1849         return mFlags;
1850     }
1851 
1852     /** @hide*/
replaceFlags(@hortcutFlags int flags)1853     public void replaceFlags(@ShortcutFlags int flags) {
1854         mFlags = flags;
1855     }
1856 
1857     /** @hide*/
addFlags(@hortcutFlags int flags)1858     public void addFlags(@ShortcutFlags int flags) {
1859         mFlags |= flags;
1860     }
1861 
1862     /** @hide*/
clearFlags(@hortcutFlags int flags)1863     public void clearFlags(@ShortcutFlags int flags) {
1864         mFlags &= ~flags;
1865     }
1866 
1867     /** @hide*/
hasFlags(@hortcutFlags int flags)1868     public boolean hasFlags(@ShortcutFlags int flags) {
1869         return (mFlags & flags) == flags;
1870     }
1871 
1872     /** @hide */
isReturnedByServer()1873     public boolean isReturnedByServer() {
1874         return hasFlags(FLAG_RETURNED_BY_SERVICE);
1875     }
1876 
1877     /** @hide */
setReturnedByServer()1878     public void setReturnedByServer() {
1879         addFlags(FLAG_RETURNED_BY_SERVICE);
1880     }
1881 
1882     /** @hide */
isLongLived()1883     public boolean isLongLived() {
1884         return hasFlags(FLAG_LONG_LIVED);
1885     }
1886 
1887     /** @hide */
setLongLived()1888     public void setLongLived() {
1889         addFlags(FLAG_LONG_LIVED);
1890     }
1891 
1892     /** @hide */
setCached(@hortcutFlags int cacheFlag)1893     public void setCached(@ShortcutFlags int cacheFlag) {
1894         addFlags(cacheFlag);
1895     }
1896 
1897     /** Return whether a shortcut is cached. */
isCached()1898     public boolean isCached() {
1899         return (getFlags() & FLAG_CACHED_ALL) != 0;
1900     }
1901 
1902     /** Return whether a shortcut is dynamic. */
isDynamic()1903     public boolean isDynamic() {
1904         return hasFlags(FLAG_DYNAMIC);
1905     }
1906 
1907     /** Return whether a shortcut is pinned. */
isPinned()1908     public boolean isPinned() {
1909         return hasFlags(FLAG_PINNED);
1910     }
1911 
1912     /**
1913      * Return whether a shortcut is static; that is, whether a shortcut is
1914      * published from AndroidManifest.xml.  If {@code true}, the shortcut is
1915      * also {@link #isImmutable()}.
1916      *
1917      * <p>When an app is upgraded and a shortcut is no longer published from AndroidManifest.xml,
1918      * this will be set to {@code false}.  If the shortcut is not pinned, then it'll disappear.
1919      * However, if it's pinned, it will still be visible, {@link #isEnabled()} will be
1920      * {@code false} and {@link #isImmutable()} will be {@code true}.
1921      */
isDeclaredInManifest()1922     public boolean isDeclaredInManifest() {
1923         return hasFlags(FLAG_MANIFEST);
1924     }
1925 
1926     /** @hide kept for unit tests */
1927     @Deprecated
isManifestShortcut()1928     public boolean isManifestShortcut() {
1929         return isDeclaredInManifest();
1930     }
1931 
1932     /**
1933      * @return true if pinned or cached, but neither static nor dynamic.
1934      * @hide
1935      */
isFloating()1936     public boolean isFloating() {
1937         return (isPinned() || isCached()) && !(isDynamic() || isManifestShortcut());
1938     }
1939 
1940     /** @hide */
isOriginallyFromManifest()1941     public boolean isOriginallyFromManifest() {
1942         return hasFlags(FLAG_IMMUTABLE);
1943     }
1944 
1945     /** @hide */
isDynamicVisible()1946     public boolean isDynamicVisible() {
1947         return isDynamic() && isVisibleToPublisher();
1948     }
1949 
1950     /** @hide */
isPinnedVisible()1951     public boolean isPinnedVisible() {
1952         return isPinned() && isVisibleToPublisher();
1953     }
1954 
1955     /** @hide */
isManifestVisible()1956     public boolean isManifestVisible() {
1957         return isDeclaredInManifest() && isVisibleToPublisher();
1958     }
1959 
1960     /** @hide */
isNonManifestVisible()1961     public boolean isNonManifestVisible() {
1962         return !isDeclaredInManifest() && isVisibleToPublisher()
1963                 && (isPinned() || isCached() || isDynamic());
1964     }
1965 
1966     /**
1967      * Return if a shortcut is immutable, in which case it cannot be modified with any of
1968      * {@link ShortcutManager} APIs.
1969      *
1970      * <p>All static shortcuts are immutable.  When a static shortcut is pinned and is then
1971      * disabled because it doesn't appear in AndroidManifest.xml for a newer version of the
1972      * app, {@link #isDeclaredInManifest()} returns {@code false}, but the shortcut
1973      * is still immutable.
1974      *
1975      * <p>All shortcuts originally published via the {@link ShortcutManager} APIs
1976      * are all mutable.
1977      */
isImmutable()1978     public boolean isImmutable() {
1979         return hasFlags(FLAG_IMMUTABLE);
1980     }
1981 
1982     /**
1983      * Returns {@code false} if a shortcut is disabled with
1984      * {@link ShortcutManager#disableShortcuts}.
1985      */
isEnabled()1986     public boolean isEnabled() {
1987         return !hasFlags(FLAG_DISABLED);
1988     }
1989 
1990     /** @hide */
isAlive()1991     public boolean isAlive() {
1992         return hasFlags(FLAG_PINNED) || hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_MANIFEST)
1993                 || isCached();
1994     }
1995 
1996     /** @hide */
usesQuota()1997     public boolean usesQuota() {
1998         return hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_MANIFEST);
1999     }
2000 
2001     /**
2002      * Return whether a shortcut's icon is a resource in the owning package.
2003      *
2004      * @hide internal/unit tests only
2005      */
hasIconResource()2006     public boolean hasIconResource() {
2007         return hasFlags(FLAG_HAS_ICON_RES);
2008     }
2009 
2010     /**
2011      * Return whether a shortcut's icon is provided via a URI.
2012      *
2013      * @hide internal/unit tests only
2014      */
hasIconUri()2015     public boolean hasIconUri() {
2016         return hasFlags(FLAG_HAS_ICON_URI);
2017     }
2018 
2019     /** @hide */
hasStringResources()2020     public boolean hasStringResources() {
2021         return (mTitleResId != 0) || (mTextResId != 0) || (mDisabledMessageResId != 0);
2022     }
2023 
2024     /** @hide */
hasAnyResources()2025     public boolean hasAnyResources() {
2026         return hasIconResource() || hasStringResources();
2027     }
2028 
2029     /**
2030      * Return whether a shortcut's icon is stored as a file.
2031      *
2032      * @hide internal/unit tests only
2033      */
hasIconFile()2034     public boolean hasIconFile() {
2035         return hasFlags(FLAG_HAS_ICON_FILE);
2036     }
2037 
2038     /**
2039      * Return whether a shortcut's icon is adaptive bitmap following design guideline
2040      * defined in {@link android.graphics.drawable.AdaptiveIconDrawable}.
2041      *
2042      * @hide internal/unit tests only
2043      */
hasAdaptiveBitmap()2044     public boolean hasAdaptiveBitmap() {
2045         return hasFlags(FLAG_ADAPTIVE_BITMAP);
2046     }
2047 
2048     /** @hide */
isIconPendingSave()2049     public boolean isIconPendingSave() {
2050         return hasFlags(FLAG_ICON_FILE_PENDING_SAVE);
2051     }
2052 
2053     /** @hide */
setIconPendingSave()2054     public void setIconPendingSave() {
2055         addFlags(FLAG_ICON_FILE_PENDING_SAVE);
2056     }
2057 
2058     /** @hide */
clearIconPendingSave()2059     public void clearIconPendingSave() {
2060         clearFlags(FLAG_ICON_FILE_PENDING_SAVE);
2061     }
2062 
2063     /**
2064      * When the system wasn't able to restore a shortcut, it'll still be registered to the system
2065      * but disabled, and such shortcuts will not be visible to the publisher. They're still visible
2066      * to launchers though.
2067      *
2068      * @hide
2069      */
2070     @TestApi
isVisibleToPublisher()2071     public boolean isVisibleToPublisher() {
2072         return !isDisabledForRestoreIssue(mDisabledReason);
2073     }
2074 
2075     /**
2076      * Return whether a shortcut only contains "key" information only or not.  If true, only the
2077      * following fields are available.
2078      * <ul>
2079      *     <li>{@link #getId()}
2080      *     <li>{@link #getPackage()}
2081      *     <li>{@link #getActivity()}
2082      *     <li>{@link #getLastChangedTimestamp()}
2083      *     <li>{@link #isDynamic()}
2084      *     <li>{@link #isPinned()}
2085      *     <li>{@link #isDeclaredInManifest()}
2086      *     <li>{@link #isImmutable()}
2087      *     <li>{@link #isEnabled()}
2088      *     <li>{@link #getUserHandle()}
2089      * </ul>
2090      *
2091      * <p>For performance reasons, shortcuts passed to
2092      * {@link LauncherApps.Callback#onShortcutsChanged(String, List, UserHandle)} as well as those
2093      * returned from {@link LauncherApps#getShortcuts(ShortcutQuery, UserHandle)}
2094      * while using the {@link ShortcutQuery#FLAG_GET_KEY_FIELDS_ONLY} option contain only key
2095      * information.
2096      */
hasKeyFieldsOnly()2097     public boolean hasKeyFieldsOnly() {
2098         return hasFlags(FLAG_KEY_FIELDS_ONLY);
2099     }
2100 
2101     /** @hide */
hasStringResourcesResolved()2102     public boolean hasStringResourcesResolved() {
2103         return hasFlags(FLAG_STRINGS_RESOLVED);
2104     }
2105 
2106     /** @hide */
updateTimestamp()2107     public void updateTimestamp() {
2108         mLastChangedTimestamp = System.currentTimeMillis();
2109     }
2110 
2111     /** @hide */
2112     // VisibleForTesting
setTimestamp(long value)2113     public void setTimestamp(long value) {
2114         mLastChangedTimestamp = value;
2115     }
2116 
2117     /** @hide */
clearIcon()2118     public void clearIcon() {
2119         mIcon = null;
2120     }
2121 
2122     /** @hide */
setIconResourceId(int iconResourceId)2123     public void setIconResourceId(int iconResourceId) {
2124         if (mIconResId != iconResourceId) {
2125             mIconResName = null;
2126         }
2127         mIconResId = iconResourceId;
2128     }
2129 
2130     /**
2131      * Get the resource ID for the icon, valid only when {@link #hasIconResource()} } is true.
2132      * @hide internal / tests only.
2133      */
getIconResourceId()2134     public int getIconResourceId() {
2135         return mIconResId;
2136     }
2137 
2138     /** @hide */
setIconUri(String iconUri)2139     public void setIconUri(String iconUri) {
2140         mIconUri = iconUri;
2141     }
2142 
2143     /**
2144      * Get the Uri for the icon, valid only when {@link #hasIconUri()} } is true.
2145      * @hide internal / tests only.
2146      */
getIconUri()2147     public String getIconUri() {
2148         return mIconUri;
2149     }
2150 
2151     /**
2152      * Bitmap path.  Note this will be null even if {@link #hasIconFile()} is set when the save
2153      * is pending.  Use {@link #isIconPendingSave()} to check it.
2154      *
2155      * @hide
2156      */
getBitmapPath()2157     public String getBitmapPath() {
2158         return mBitmapPath;
2159     }
2160 
2161     /** @hide */
setBitmapPath(String bitmapPath)2162     public void setBitmapPath(String bitmapPath) {
2163         mBitmapPath = bitmapPath;
2164     }
2165 
2166     /** @hide */
setDisabledMessageResId(int disabledMessageResId)2167     public void setDisabledMessageResId(int disabledMessageResId) {
2168         if (mDisabledMessageResId != disabledMessageResId) {
2169             mDisabledMessageResName = null;
2170         }
2171         mDisabledMessageResId = disabledMessageResId;
2172         mDisabledMessage = null;
2173     }
2174 
2175     /** @hide */
setDisabledMessage(String disabledMessage)2176     public void setDisabledMessage(String disabledMessage) {
2177         mDisabledMessage = disabledMessage;
2178         mDisabledMessageResId = 0;
2179         mDisabledMessageResName = null;
2180     }
2181 
2182     /** @hide */
getTitleResName()2183     public String getTitleResName() {
2184         return mTitleResName;
2185     }
2186 
2187     /** @hide */
setTitleResName(String titleResName)2188     public void setTitleResName(String titleResName) {
2189         mTitleResName = titleResName;
2190     }
2191 
2192     /** @hide */
getTextResName()2193     public String getTextResName() {
2194         return mTextResName;
2195     }
2196 
2197     /** @hide */
setTextResName(String textResName)2198     public void setTextResName(String textResName) {
2199         mTextResName = textResName;
2200     }
2201 
2202     /** @hide */
getDisabledMessageResName()2203     public String getDisabledMessageResName() {
2204         return mDisabledMessageResName;
2205     }
2206 
2207     /** @hide */
setDisabledMessageResName(String disabledMessageResName)2208     public void setDisabledMessageResName(String disabledMessageResName) {
2209         mDisabledMessageResName = disabledMessageResName;
2210     }
2211 
2212     /** @hide */
getIconResName()2213     public String getIconResName() {
2214         return mIconResName;
2215     }
2216 
2217     /** @hide */
setIconResName(String iconResName)2218     public void setIconResName(String iconResName) {
2219         mIconResName = iconResName;
2220     }
2221 
2222     /**
2223      * Replaces the intent.
2224      *
2225      * @throws IllegalArgumentException when extra is not compatible with {@link PersistableBundle}.
2226      *
2227      * @hide
2228      */
setIntents(Intent[] intents)2229     public void setIntents(Intent[] intents) throws IllegalArgumentException {
2230         Objects.requireNonNull(intents);
2231         Preconditions.checkArgument(intents.length > 0);
2232 
2233         mIntents = cloneIntents(intents);
2234         fixUpIntentExtras();
2235     }
2236 
2237     /** @hide */
setIntentExtras(Intent intent, PersistableBundle extras)2238     public static Intent setIntentExtras(Intent intent, PersistableBundle extras) {
2239         if (extras == null) {
2240             intent.replaceExtras((Bundle) null);
2241         } else {
2242             intent.replaceExtras(new Bundle(extras));
2243         }
2244         return intent;
2245     }
2246 
2247     /**
2248      * Replaces the categories.
2249      *
2250      * @hide
2251      */
setCategories(Set<String> categories)2252     public void setCategories(Set<String> categories) {
2253         mCategories = cloneCategories(categories);
2254     }
2255 
2256     /**
2257      * Return true if the shortcut is excluded from specified surface.
2258      */
isExcludedFromSurfaces(@urface int surface)2259     public boolean isExcludedFromSurfaces(@Surface int surface) {
2260         return (mExcludedSurfaces & surface) != 0;
2261     }
2262 
2263     /**
2264      * Returns a bitmask of all surfaces this shortcut is excluded from.
2265      *
2266      * @see ShortcutInfo.Builder#setExcludedFromSurfaces(int)
2267      */
2268     @Surface
getExcludedFromSurfaces()2269     public int getExcludedFromSurfaces() {
2270         return mExcludedSurfaces;
2271     }
2272 
2273     /**
2274      * Returns an immutable copy of the capability bindings using internal data structure.
2275      * @hide
2276      */
2277     @Nullable
getCapabilityBindingsInternal()2278     public Map<String, Map<String, List<String>>> getCapabilityBindingsInternal() {
2279         return cloneCapabilityBindings(mCapabilityBindings);
2280     }
2281 
2282     @Nullable
cloneCapabilityBindings( @ullable final Map<String, Map<String, List<String>>> orig)2283     private static Map<String, Map<String, List<String>>> cloneCapabilityBindings(
2284             @Nullable final Map<String, Map<String, List<String>>> orig) {
2285         if (orig == null) {
2286             return null;
2287         }
2288         final Map<String, Map<String, List<String>>> ret = new ArrayMap<>();
2289         for (String capability : orig.keySet()) {
2290             final Map<String, List<String>> params = orig.get(capability);
2291             final Map<String, List<String>> clone;
2292             if (params == null) {
2293                 clone = null;
2294             } else {
2295                 clone = new ArrayMap<>(params.size());
2296                 for (String paramName : params.keySet()) {
2297                     final List<String> paramValues = params.get(paramName);
2298                     clone.put(paramName, Collections.unmodifiableList(paramValues));
2299                 }
2300             }
2301             ret.put(capability, Collections.unmodifiableMap(clone));
2302         }
2303         return Collections.unmodifiableMap(ret);
2304     }
2305 
2306     /**
2307      * Return a list of {@link Capability} associated with the shortcut.
2308      */
2309     @NonNull
getCapabilities()2310     public List<Capability> getCapabilities() {
2311         if (mCapabilityBindings == null) {
2312             return new ArrayList<>(0);
2313         }
2314         return mCapabilityBindings.keySet().stream().map(Capability::new)
2315                 .collect(Collectors.toList());
2316     }
2317 
2318     /**
2319      *  Returns the {@link CapabilityParams} in associated with given capability.
2320      *
2321      *  @param capability {@link Capability} associated with the shortcut.
2322      */
2323     @NonNull
getCapabilityParams(@onNull final Capability capability)2324     public List<CapabilityParams> getCapabilityParams(@NonNull final Capability capability) {
2325         Objects.requireNonNull(capability);
2326         if (mCapabilityBindings == null) {
2327             return new ArrayList<>(0);
2328         }
2329         final Map<String, List<String>> param = mCapabilityBindings.get(capability.getName());
2330         if (param == null) {
2331             return new ArrayList<>(0);
2332         }
2333         final List<CapabilityParams> ret = new ArrayList<>(param.size());
2334         for (String key : param.keySet()) {
2335             final List<String> values = param.get(key);
2336             final String primaryValue = values.get(0);
2337             final List<String> aliases = values.size() == 1
2338                     ? Collections.emptyList() : values.subList(1, values.size());
2339             CapabilityParams.Builder builder = new CapabilityParams.Builder(key, primaryValue);
2340             for (String alias : aliases) {
2341                 builder = builder.addAlias(alias);
2342             }
2343             ret.add(builder.build());
2344         }
2345         return ret;
2346     }
2347 
ShortcutInfo(Parcel source)2348     private ShortcutInfo(Parcel source) {
2349         final ClassLoader cl = getClass().getClassLoader();
2350 
2351         mUserId = source.readInt();
2352         mId = getSafeId(Preconditions.checkStringNotEmpty(source.readString8(),
2353                 "Shortcut ID must be provided"));
2354         mPackageName = source.readString8();
2355         mActivity = source.readParcelable(cl, android.content.ComponentName.class);
2356         mFlags = source.readInt();
2357         mIconResId = source.readInt();
2358         mLastChangedTimestamp = source.readLong();
2359         mDisabledReason = source.readInt();
2360 
2361         if (source.readInt() == 0) {
2362             return; // key information only.
2363         }
2364 
2365         mIcon = source.readParcelable(cl, android.graphics.drawable.Icon.class);
2366         mTitle = source.readCharSequence();
2367         mTitleResId = source.readInt();
2368         mText = source.readCharSequence();
2369         mTextResId = source.readInt();
2370         mDisabledMessage = source.readCharSequence();
2371         mDisabledMessageResId = source.readInt();
2372         mIntents = source.readParcelableArray(cl, Intent.class);
2373         mIntentPersistableExtrases = source.readParcelableArray(cl, PersistableBundle.class);
2374         mRank = source.readInt();
2375         mExtras = source.readParcelable(cl, android.os.PersistableBundle.class);
2376         mBitmapPath = source.readString8();
2377 
2378         mIconResName = source.readString8();
2379         mTitleResName = source.readString8();
2380         mTextResName = source.readString8();
2381         mDisabledMessageResName = source.readString8();
2382 
2383         int N = source.readInt();
2384         if (N == 0) {
2385             mCategories = null;
2386         } else {
2387             mCategories = new ArraySet<>(N);
2388             for (int i = 0; i < N; i++) {
2389                 mCategories.add(source.readString8().intern());
2390             }
2391         }
2392 
2393         mPersons = source.readParcelableArray(cl, Person.class);
2394         mLocusId = source.readParcelable(cl, android.content.LocusId.class);
2395         mIconUri = source.readString8();
2396         mStartingThemeResName = source.readString8();
2397         mExcludedSurfaces = source.readInt();
2398 
2399         final Map<String, Map> rawCapabilityBindings = source.readHashMap(
2400                 /*loader*/ null, /*clazzKey*/ String.class, /*clazzValue*/ HashMap.class);
2401         if (rawCapabilityBindings != null && !rawCapabilityBindings.isEmpty()) {
2402             final Map<String, Map<String, List<String>>> capabilityBindings =
2403                     new ArrayMap<>(rawCapabilityBindings.size());
2404             rawCapabilityBindings.forEach(capabilityBindings::put);
2405             mCapabilityBindings = cloneCapabilityBindings(capabilityBindings);
2406         }
2407     }
2408 
2409     @Override
writeToParcel(Parcel dest, int flags)2410     public void writeToParcel(Parcel dest, int flags) {
2411         dest.writeInt(mUserId);
2412         dest.writeString8(mId);
2413         dest.writeString8(mPackageName);
2414         dest.writeParcelable(mActivity, flags);
2415         dest.writeInt(mFlags);
2416         dest.writeInt(mIconResId);
2417         dest.writeLong(mLastChangedTimestamp);
2418         dest.writeInt(mDisabledReason);
2419 
2420         if (hasKeyFieldsOnly()) {
2421             dest.writeInt(0);
2422             return;
2423         }
2424         dest.writeInt(1);
2425 
2426         dest.writeParcelable(mIcon, flags);
2427         dest.writeCharSequence(mTitle);
2428         dest.writeInt(mTitleResId);
2429         dest.writeCharSequence(mText);
2430         dest.writeInt(mTextResId);
2431         dest.writeCharSequence(mDisabledMessage);
2432         dest.writeInt(mDisabledMessageResId);
2433 
2434         dest.writeParcelableArray(mIntents, flags);
2435         dest.writeParcelableArray(mIntentPersistableExtrases, flags);
2436         dest.writeInt(mRank);
2437         dest.writeParcelable(mExtras, flags);
2438         dest.writeString8(mBitmapPath);
2439 
2440         dest.writeString8(mIconResName);
2441         dest.writeString8(mTitleResName);
2442         dest.writeString8(mTextResName);
2443         dest.writeString8(mDisabledMessageResName);
2444 
2445         if (mCategories != null) {
2446             final int N = mCategories.size();
2447             dest.writeInt(N);
2448             for (int i = 0; i < N; i++) {
2449                 dest.writeString8(mCategories.valueAt(i));
2450             }
2451         } else {
2452             dest.writeInt(0);
2453         }
2454 
2455         dest.writeParcelableArray(mPersons, flags);
2456         dest.writeParcelable(mLocusId, flags);
2457         dest.writeString8(mIconUri);
2458         dest.writeString8(mStartingThemeResName);
2459         dest.writeInt(mExcludedSurfaces);
2460         dest.writeMap(mCapabilityBindings);
2461     }
2462 
2463     public static final @NonNull Creator<ShortcutInfo> CREATOR =
2464             new Creator<ShortcutInfo>() {
2465                 public ShortcutInfo createFromParcel(Parcel source) {
2466                     return new ShortcutInfo(source);
2467                 }
2468                 public ShortcutInfo[] newArray(int size) {
2469                     return new ShortcutInfo[size];
2470                 }
2471             };
2472 
2473     @Override
describeContents()2474     public int describeContents() {
2475         return 0;
2476     }
2477 
2478 
2479     /**
2480      * Return a string representation, intended for logging.  Some fields will be retracted.
2481      */
2482     @Override
toString()2483     public String toString() {
2484         return toStringInner(/* secure =*/ true, /* includeInternalData =*/ false,
2485                 /*indent=*/ null);
2486     }
2487 
2488     /** @hide */
toInsecureString()2489     public String toInsecureString() {
2490         return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true,
2491                 /*indent=*/ null);
2492     }
2493 
2494     /** @hide */
toDumpString(String indent)2495     public String toDumpString(String indent) {
2496         return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true, indent);
2497     }
2498 
addIndentOrComma(StringBuilder sb, String indent)2499     private void addIndentOrComma(StringBuilder sb, String indent) {
2500         if (indent != null) {
2501             sb.append("\n  ");
2502             sb.append(indent);
2503         } else {
2504             sb.append(", ");
2505         }
2506     }
2507 
toStringInner(boolean secure, boolean includeInternalData, String indent)2508     private String toStringInner(boolean secure, boolean includeInternalData, String indent) {
2509         final StringBuilder sb = new StringBuilder();
2510 
2511         if (indent != null) {
2512             sb.append(indent);
2513         }
2514 
2515         sb.append("ShortcutInfo {");
2516 
2517         sb.append("id=");
2518         sb.append(secure ? "***" : mId);
2519 
2520         sb.append(", flags=0x");
2521         sb.append(Integer.toHexString(mFlags));
2522         sb.append(" [");
2523         if ((mFlags & FLAG_SHADOW) != 0) {
2524             // Note the shadow flag isn't actually used anywhere and it's just for dumpsys, so
2525             // we don't have an isXxx for this.
2526             sb.append("Sdw");
2527         }
2528         if (!isEnabled()) {
2529             sb.append("Dis");
2530         }
2531         if (isImmutable()) {
2532             sb.append("Im");
2533         }
2534         if (isManifestShortcut()) {
2535             sb.append("Man");
2536         }
2537         if (isDynamic()) {
2538             sb.append("Dyn");
2539         }
2540         if (isPinned()) {
2541             sb.append("Pin");
2542         }
2543         if (hasIconFile()) {
2544             sb.append("Ic-f");
2545         }
2546         if (isIconPendingSave()) {
2547             sb.append("Pens");
2548         }
2549         if (hasIconResource()) {
2550             sb.append("Ic-r");
2551         }
2552         if (hasIconUri()) {
2553             sb.append("Ic-u");
2554         }
2555         if (hasAdaptiveBitmap()) {
2556             sb.append("Ic-a");
2557         }
2558         if (hasKeyFieldsOnly()) {
2559             sb.append("Key");
2560         }
2561         if (hasStringResourcesResolved()) {
2562             sb.append("Str");
2563         }
2564         if (isReturnedByServer()) {
2565             sb.append("Rets");
2566         }
2567         if (isLongLived()) {
2568             sb.append("Liv");
2569         }
2570         if (isExcludedFromSurfaces(SURFACE_LAUNCHER)) {
2571             sb.append("Hid-L");
2572         }
2573         sb.append("]");
2574 
2575         addIndentOrComma(sb, indent);
2576 
2577         sb.append("packageName=");
2578         sb.append(mPackageName);
2579 
2580         addIndentOrComma(sb, indent);
2581 
2582         sb.append("activity=");
2583         sb.append(mActivity);
2584 
2585         addIndentOrComma(sb, indent);
2586 
2587         sb.append("shortLabel=");
2588         sb.append(secure ? "***" : mTitle);
2589         sb.append(", resId=");
2590         sb.append(mTitleResId);
2591         sb.append("[");
2592         sb.append(mTitleResName);
2593         sb.append("]");
2594 
2595         addIndentOrComma(sb, indent);
2596 
2597         sb.append("longLabel=");
2598         sb.append(secure ? "***" : mText);
2599         sb.append(", resId=");
2600         sb.append(mTextResId);
2601         sb.append("[");
2602         sb.append(mTextResName);
2603         sb.append("]");
2604 
2605         addIndentOrComma(sb, indent);
2606 
2607         sb.append("disabledMessage=");
2608         sb.append(secure ? "***" : mDisabledMessage);
2609         sb.append(", resId=");
2610         sb.append(mDisabledMessageResId);
2611         sb.append("[");
2612         sb.append(mDisabledMessageResName);
2613         sb.append("]");
2614 
2615         addIndentOrComma(sb, indent);
2616 
2617         sb.append("disabledReason=");
2618         sb.append(getDisabledReasonDebugString(mDisabledReason));
2619 
2620         if (mStartingThemeResName != null && !mStartingThemeResName.isEmpty()) {
2621             addIndentOrComma(sb, indent);
2622             sb.append("SplashScreenThemeResName=");
2623             sb.append(mStartingThemeResName);
2624         }
2625 
2626         addIndentOrComma(sb, indent);
2627 
2628         sb.append("categories=");
2629         sb.append(mCategories);
2630 
2631         addIndentOrComma(sb, indent);
2632 
2633         sb.append("persons=");
2634         sb.append(mPersons);
2635 
2636         addIndentOrComma(sb, indent);
2637 
2638         sb.append("icon=");
2639         sb.append(mIcon);
2640 
2641         addIndentOrComma(sb, indent);
2642 
2643         sb.append("rank=");
2644         sb.append(mRank);
2645 
2646         sb.append(", timestamp=");
2647         sb.append(mLastChangedTimestamp);
2648 
2649         addIndentOrComma(sb, indent);
2650 
2651         sb.append("intents=");
2652         if (mIntents == null) {
2653             sb.append("null");
2654         } else {
2655             if (secure) {
2656                 sb.append("size:");
2657                 sb.append(mIntents.length);
2658             } else {
2659                 final int size = mIntents.length;
2660                 sb.append("[");
2661                 String sep = "";
2662                 for (int i = 0; i < size; i++) {
2663                     sb.append(sep);
2664                     sep = ", ";
2665                     sb.append(mIntents[i]);
2666                     sb.append("/");
2667                     sb.append(mIntentPersistableExtrases[i]);
2668                 }
2669                 sb.append("]");
2670             }
2671         }
2672 
2673         addIndentOrComma(sb, indent);
2674 
2675         sb.append("extras=");
2676         sb.append(mExtras);
2677 
2678         if (includeInternalData) {
2679             addIndentOrComma(sb, indent);
2680 
2681             sb.append("iconRes=");
2682             sb.append(mIconResId);
2683             sb.append("[");
2684             sb.append(mIconResName);
2685             sb.append("]");
2686 
2687             sb.append(", bitmapPath=");
2688             sb.append(mBitmapPath);
2689 
2690             sb.append(", iconUri=");
2691             sb.append(mIconUri);
2692         }
2693 
2694         if (mLocusId != null) {
2695             sb.append("locusId="); sb.append(mLocusId); // LocusId.toString() is PII-safe.
2696         }
2697 
2698         sb.append("}");
2699         return sb.toString();
2700     }
2701 
2702     /** @hide */
ShortcutInfo( @serIdInt int userId, String id, String packageName, ComponentName activity, Icon icon, CharSequence title, int titleResId, String titleResName, CharSequence text, int textResId, String textResName, CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName, Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras, long lastChangedTimestamp, int flags, int iconResId, String iconResName, String bitmapPath, String iconUri, int disabledReason, Person[] persons, LocusId locusId, @Nullable String startingThemeResName, @Nullable Map<String, Map<String, List<String>>> capabilityBindings)2703     public ShortcutInfo(
2704             @UserIdInt int userId, String id, String packageName, ComponentName activity,
2705             Icon icon, CharSequence title, int titleResId, String titleResName,
2706             CharSequence text, int textResId, String textResName,
2707             CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
2708             Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
2709             long lastChangedTimestamp,
2710             int flags, int iconResId, String iconResName, String bitmapPath, String iconUri,
2711             int disabledReason, Person[] persons, LocusId locusId,
2712             @Nullable String startingThemeResName,
2713             @Nullable Map<String, Map<String, List<String>>> capabilityBindings) {
2714         mUserId = userId;
2715         mId = id;
2716         mPackageName = packageName;
2717         mActivity = activity;
2718         mIcon = icon;
2719         mTitle = title;
2720         mTitleResId = titleResId;
2721         mTitleResName = titleResName;
2722         mText = text;
2723         mTextResId = textResId;
2724         mTextResName = textResName;
2725         mDisabledMessage = disabledMessage;
2726         mDisabledMessageResId = disabledMessageResId;
2727         mDisabledMessageResName = disabledMessageResName;
2728         mCategories = cloneCategories(categories);
2729         mIntents = cloneIntents(intentsWithExtras);
2730         fixUpIntentExtras();
2731         mRank = rank;
2732         mExtras = extras;
2733         mLastChangedTimestamp = lastChangedTimestamp;
2734         mFlags = flags;
2735         mIconResId = iconResId;
2736         mIconResName = iconResName;
2737         mBitmapPath = bitmapPath;
2738         mIconUri = iconUri;
2739         mDisabledReason = disabledReason;
2740         mPersons = persons;
2741         mLocusId = locusId;
2742         mStartingThemeResName = startingThemeResName;
2743         mCapabilityBindings = cloneCapabilityBindings(capabilityBindings);
2744     }
2745 }
2746