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