• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.content.pm;
17 
18 import android.annotation.IntDef;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.UserIdInt;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.graphics.drawable.Icon;
26 import android.os.Bundle;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.os.PersistableBundle;
30 import android.os.UserHandle;
31 import android.util.ArraySet;
32 
33 import com.android.internal.util.Preconditions;
34 
35 import java.lang.annotation.Retention;
36 import java.lang.annotation.RetentionPolicy;
37 import java.util.Set;
38 
39 // TODO Enhance javadoc
40 /**
41  *
42  * Represents a shortcut from an application.
43  *
44  * <p>Notes about icons:
45  * <ul>
46  *     <li>If an {@link Icon} is a resource, the system keeps the package name and the resource ID.
47  *     Otherwise, the bitmap is fetched when it's registered to ShortcutManager,
48  *     then shrunk if necessary, and persisted.
49  *     <li>The system disallows byte[] icons, because they can easily go over the binder size limit.
50  * </ul>
51  *
52  * @see {@link ShortcutManager}.
53  *
54  * @hide
55  */
56 public final class ShortcutInfo implements Parcelable {
57     /* @hide */
58     public static final int FLAG_DYNAMIC = 1 << 0;
59 
60     /* @hide */
61     public static final int FLAG_PINNED = 1 << 1;
62 
63     /* @hide */
64     public static final int FLAG_HAS_ICON_RES = 1 << 2;
65 
66     /* @hide */
67     public static final int FLAG_HAS_ICON_FILE = 1 << 3;
68 
69     /* @hide */
70     public static final int FLAG_KEY_FIELDS_ONLY = 1 << 4;
71 
72     /** @hide */
73     @IntDef(flag = true,
74             value = {
75             FLAG_DYNAMIC,
76             FLAG_PINNED,
77             FLAG_HAS_ICON_RES,
78             FLAG_HAS_ICON_FILE,
79             FLAG_KEY_FIELDS_ONLY,
80     })
81     @Retention(RetentionPolicy.SOURCE)
82     public @interface ShortcutFlags {}
83 
84     // Cloning options.
85 
86     /* @hide */
87     private static final int CLONE_REMOVE_ICON = 1 << 0;
88 
89     /* @hide */
90     private static final int CLONE_REMOVE_INTENT = 1 << 1;
91 
92     /* @hide */
93     public static final int CLONE_REMOVE_NON_KEY_INFO = 1 << 2;
94 
95     /* @hide */
96     public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON;
97 
98     /* @hide */
99     public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT;
100 
101     /** @hide */
102     @IntDef(flag = true,
103             value = {
104                     CLONE_REMOVE_ICON,
105                     CLONE_REMOVE_INTENT,
106                     CLONE_REMOVE_NON_KEY_INFO,
107                     CLONE_REMOVE_FOR_CREATOR,
108                     CLONE_REMOVE_FOR_LAUNCHER
109             })
110     @Retention(RetentionPolicy.SOURCE)
111     public @interface CloneFlags {}
112 
113     /**
114      * Shortcut category for
115      */
116     public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
117 
118     private final String mId;
119 
120     @NonNull
121     private final String mPackageName;
122 
123     @Nullable
124     private ComponentName mActivityComponent;
125 
126     @Nullable
127     private Icon mIcon;
128 
129     @NonNull
130     private String mTitle;
131 
132     @Nullable
133     private String mText;
134 
135     @NonNull
136     private ArraySet<String> mCategories;
137 
138     /**
139      * Intent *with extras removed*.
140      */
141     @NonNull
142     private Intent mIntent;
143 
144     /**
145      * Extras for the intent.
146      */
147     @NonNull
148     private PersistableBundle mIntentPersistableExtras;
149 
150     private int mWeight;
151 
152     @Nullable
153     private PersistableBundle mExtras;
154 
155     private long mLastChangedTimestamp;
156 
157     // Internal use only.
158     @ShortcutFlags
159     private int mFlags;
160 
161     // Internal use only.
162     private int mIconResourceId;
163 
164     // Internal use only.
165     @Nullable
166     private String mBitmapPath;
167 
168     private final int mUserId;
169 
ShortcutInfo(Builder b)170     private ShortcutInfo(Builder b) {
171         mUserId = b.mContext.getUserId();
172 
173         mId = Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided");
174 
175         // Note we can't do other null checks here because SM.updateShortcuts() takes partial
176         // information.
177         mPackageName = b.mContext.getPackageName();
178         mActivityComponent = b.mActivityComponent;
179         mIcon = b.mIcon;
180         mTitle = b.mTitle;
181         mText = b.mText;
182         mCategories = clone(b.mCategories);
183         mIntent = b.mIntent;
184         if (mIntent != null) {
185             final Bundle intentExtras = mIntent.getExtras();
186             if (intentExtras != null) {
187                 mIntent.replaceExtras((Bundle) null);
188                 mIntentPersistableExtras = new PersistableBundle(intentExtras);
189             }
190         }
191         mWeight = b.mWeight;
192         mExtras = b.mExtras;
193         updateTimestamp();
194     }
195 
clone(Set<T> source)196     private <T> ArraySet<T> clone(Set<T> source) {
197         return (source == null) ? null : new ArraySet<>(source);
198     }
199 
200     /**
201      * Throws if any of the mandatory fields is not set.
202      *
203      * @hide
204      */
enforceMandatoryFields()205     public void enforceMandatoryFields() {
206         Preconditions.checkStringNotEmpty(mId, "Shortcut ID must be provided");
207         Preconditions.checkStringNotEmpty(mTitle, "Shortcut title must be provided");
208         Preconditions.checkNotNull(mIntent, "Shortcut Intent must be provided");
209     }
210 
211     /**
212      * Copy constructor.
213      */
ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags)214     private ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags) {
215         mUserId = source.mUserId;
216         mId = source.mId;
217         mPackageName = source.mPackageName;
218         mFlags = source.mFlags;
219         mLastChangedTimestamp = source.mLastChangedTimestamp;
220 
221         // Just always keep it since it's cheep.
222         mIconResourceId = source.mIconResourceId;
223 
224         if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
225             mActivityComponent = source.mActivityComponent;
226 
227             if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
228                 mIcon = source.mIcon;
229                 mBitmapPath = source.mBitmapPath;
230             }
231 
232             mTitle = source.mTitle;
233             mText = source.mText;
234             mCategories = clone(source.mCategories);
235             if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
236                 mIntent = source.mIntent;
237                 mIntentPersistableExtras = source.mIntentPersistableExtras;
238             }
239             mWeight = source.mWeight;
240             mExtras = source.mExtras;
241         } else {
242             // Set this bit.
243             mFlags |= FLAG_KEY_FIELDS_ONLY;
244         }
245     }
246 
247     /**
248      * Copy a {@link ShortcutInfo}, optionally removing fields.
249      * @hide
250      */
clone(@loneFlags int cloneFlags)251     public ShortcutInfo clone(@CloneFlags int cloneFlags) {
252         return new ShortcutInfo(this, cloneFlags);
253     }
254 
255     /**
256      * Copy non-null/zero fields from another {@link ShortcutInfo}.  Only "public" information
257      * will be overwritten.  The timestamp will be updated.
258      *
259      * - Flags will not change
260      * - mBitmapPath will not change
261      * - Current time will be set to timestamp
262      *
263      * @hide
264      */
copyNonNullFieldsFrom(ShortcutInfo source)265     public void copyNonNullFieldsFrom(ShortcutInfo source) {
266         Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
267         Preconditions.checkState(mId.equals(source.mId), "ID must match");
268         Preconditions.checkState(mPackageName.equals(source.mPackageName),
269                 "Package name must match");
270 
271         if (source.mActivityComponent != null) {
272             mActivityComponent = source.mActivityComponent;
273         }
274 
275         if (source.mIcon != null) {
276             mIcon = source.mIcon;
277         }
278         if (source.mTitle != null) {
279             mTitle = source.mTitle;
280         }
281         if (source.mText != null) {
282             mText = source.mText;
283         }
284         if (source.mCategories != null) {
285             mCategories = clone(source.mCategories);
286         }
287         if (source.mIntent != null) {
288             mIntent = source.mIntent;
289             mIntentPersistableExtras = source.mIntentPersistableExtras;
290         }
291         if (source.mWeight != 0) {
292             mWeight = source.mWeight;
293         }
294         if (source.mExtras != null) {
295             mExtras = source.mExtras;
296         }
297 
298         updateTimestamp();
299     }
300 
301     /**
302      * @hide
303      */
validateIcon(Icon icon)304     public static Icon validateIcon(Icon icon) {
305         switch (icon.getType()) {
306             case Icon.TYPE_RESOURCE:
307             case Icon.TYPE_BITMAP:
308                 break; // OK
309             default:
310                 throw getInvalidIconException();
311         }
312         if (icon.hasTint()) {
313             // TODO support it
314             throw new IllegalArgumentException("Icons with tints are not supported");
315         }
316 
317         return icon;
318     }
319 
320     /** @hide */
getInvalidIconException()321     public static IllegalArgumentException getInvalidIconException() {
322         return new IllegalArgumentException("Unsupported icon type:"
323                 +" only bitmap, resource and content URI are supported");
324     }
325 
326     /**
327      * Builder class for {@link ShortcutInfo} objects.
328      */
329     public static class Builder {
330         private final Context mContext;
331 
332         private String mId;
333 
334         private ComponentName mActivityComponent;
335 
336         private Icon mIcon;
337 
338         private String mTitle;
339 
340         private String mText;
341 
342         private Set<String> mCategories;
343 
344         private Intent mIntent;
345 
346         private int mWeight;
347 
348         private PersistableBundle mExtras;
349 
350         /** Constructor. */
Builder(Context context)351         public Builder(Context context) {
352             mContext = context;
353         }
354 
355         /**
356          * Sets the ID of the shortcut.  This is a mandatory field.
357          */
358         @NonNull
setId(@onNull String id)359         public Builder setId(@NonNull String id) {
360             mId = Preconditions.checkStringNotEmpty(id, "id");
361             return this;
362         }
363 
364         /**
365          * Optionally sets the target activity.  If it's not set, and if the caller application
366          * has multiple launcher icons, this shortcut will be shown on all those icons.
367          * If it's set, this shortcut will be only shown on this activity.
368          *
369          * <p>The package name of the target activity must match the package name of the shortcut
370          * publisher.
371          *
372          * <p>This has nothing to do with the activity that this shortcut will launch.  This is
373          * a hint to the launcher app about which launcher icon to associate this shortcut with.
374          */
375         @NonNull
setActivityComponent(@onNull ComponentName activityComponent)376         public Builder setActivityComponent(@NonNull ComponentName activityComponent) {
377             mActivityComponent = Preconditions.checkNotNull(activityComponent, "activityComponent");
378             return this;
379         }
380 
381         /**
382          * Optionally sets an icon.
383          *
384          * <ul>
385          *     <li>Tints set by {@link Icon#setTint} or {@link Icon#setTintList} are not supported.
386          *     <li>Bitmaps and resources are supported, but "content:" URIs are not supported.
387          * </ul>
388          *
389          * <p>For performance reasons, icons will <b>NOT</b> be available on instances
390          * returned by {@link ShortcutManager} or {@link LauncherApps}.  Launcher applications
391          * can use {@link ShortcutInfo#getIconResourceId()} if {@link #hasIconResource()} is true.
392          * Otherwise, if {@link #hasIconFile()} is true, use
393          * {@link LauncherApps#getShortcutIconFd} to load the image.
394          */
395         @NonNull
setIcon(Icon icon)396         public Builder setIcon(Icon icon) {
397             mIcon = validateIcon(icon);
398             return this;
399         }
400 
401         /**
402          * Sets the title of a shortcut.  This is a mandatory field.
403          *
404          * <p>This field is intended for a concise description of a shortcut displayed under
405          * an icon.  The recommend max length is 10 characters.
406          */
407         @NonNull
setTitle(@onNull String title)408         public Builder setTitle(@NonNull String title) {
409             mTitle = Preconditions.checkStringNotEmpty(title, "title");
410             return this;
411         }
412 
413         /**
414          * Sets the text of a shortcut.  This is an optional field.
415          *
416          * <p>This field is intended to be more descriptive than the shortcut title.
417          * The recommend max length is 25 characters.
418          */
419         @NonNull
setText(@onNull String text)420         public Builder setText(@NonNull String text) {
421             mText = Preconditions.checkStringNotEmpty(text, "text");
422             return this;
423         }
424 
425         /**
426          * Sets categories for a shortcut.  Launcher applications may use this information to
427          * categorise shortcuts.
428          *
429          * @see #SHORTCUT_CATEGORY_CONVERSATION
430          */
431         @NonNull
setCategories(Set<String> categories)432         public Builder setCategories(Set<String> categories) {
433             mCategories = categories;
434             return this;
435         }
436 
437         /**
438          * Sets the intent of a shortcut.  This is a mandatory field.  The extras must only contain
439          * persistable information.  (See {@link PersistableBundle}).
440          */
441         @NonNull
setIntent(@onNull Intent intent)442         public Builder setIntent(@NonNull Intent intent) {
443             mIntent = Preconditions.checkNotNull(intent, "intent");
444             return this;
445         }
446 
447         /**
448          * Optionally sets the weight of a shortcut, which will be used by the launcher for sorting.
449          * The larger the weight, the more "important" a shortcut is.
450          */
451         @NonNull
setWeight(int weight)452         public Builder setWeight(int weight) {
453             mWeight = weight;
454             return this;
455         }
456 
457         /**
458          * Optional values that applications can set.  Applications can store any meta-data of
459          * shortcuts in this, and retrieve later from {@link ShortcutInfo#getExtras()}.
460          */
461         @NonNull
setExtras(@onNull PersistableBundle extras)462         public Builder setExtras(@NonNull PersistableBundle extras) {
463             mExtras = extras;
464             return this;
465         }
466 
467         /**
468          * Creates a {@link ShortcutInfo} instance.
469          */
470         @NonNull
build()471         public ShortcutInfo build() {
472             return new ShortcutInfo(this);
473         }
474     }
475 
476     /**
477      * Return the ID of the shortcut.
478      */
479     @NonNull
getId()480     public String getId() {
481         return mId;
482     }
483 
484     /**
485      * Return the package name of the creator application.
486      */
487     @NonNull
getPackageName()488     public String getPackageName() {
489         return mPackageName;
490     }
491 
492     /**
493      * Return the target activity, which may be null, in which case the shortcut is not associated
494      * with a specific activity.
495      *
496      * <p>This has nothing to do with the activity that this shortcut will launch.  This is
497      * a hint to the launcher app that on which launcher icon this shortcut should be shown.
498      *
499      * @see Builder#setActivityComponent
500      */
501     @Nullable
getActivityComponent()502     public ComponentName getActivityComponent() {
503         return mActivityComponent;
504     }
505 
506     /**
507      * Icon.
508      *
509      * For performance reasons, this will <b>NOT</b> be available when an instance is returned
510      * by {@link ShortcutManager} or {@link LauncherApps}.  A launcher application needs to use
511      * other APIs in LauncherApps to fetch the bitmap.
512      *
513      * @hide
514      */
515     @Nullable
getIcon()516     public Icon getIcon() {
517         return mIcon;
518     }
519 
520     /**
521      * Return the shortcut title.
522      *
523      * <p>All shortcuts must have a non-empty title, but this method will return null when
524      * {@link #hasKeyFieldsOnly()} is true.
525      */
526     @Nullable
getTitle()527     public String getTitle() {
528         return mTitle;
529     }
530 
531     /**
532      * Return the shortcut text.
533      */
534     @Nullable
getText()535     public String getText() {
536         return mText;
537     }
538 
539     /**
540      * Return the categories.
541      */
542     @Nullable
getCategories()543     public Set<String> getCategories() {
544         return mCategories;
545     }
546 
547     /**
548      * Return the intent.
549      *
550      * <p>All shortcuts must have an intent, but this method will return null when
551      * {@link #hasKeyFieldsOnly()} is true.
552      *
553      * <p>Launcher apps <b>cannot</b> see the intent.  If a {@link ShortcutInfo} is obtained via
554      * {@link LauncherApps}, then this method will always return null.  Launcher apps can only
555      * start a shortcut intent with {@link LauncherApps#startShortcut}.
556      */
557     @Nullable
getIntent()558     public Intent getIntent() {
559         if (mIntent == null) {
560             return null;
561         }
562         final Intent intent = new Intent(mIntent);
563         intent.replaceExtras(
564                 mIntentPersistableExtras != null ? new Bundle(mIntentPersistableExtras) : null);
565         return intent;
566     }
567 
568     /**
569      * Return "raw" intent, which is the original intent without the extras.
570      * @hide
571      */
572     @Nullable
getIntentNoExtras()573     public Intent getIntentNoExtras() {
574         return mIntent;
575     }
576 
577     /**
578      * The extras in the intent.  We convert extras into {@link PersistableBundle} so we can
579      * persist them.
580      * @hide
581      */
582     @Nullable
getIntentPersistableExtras()583     public PersistableBundle getIntentPersistableExtras() {
584         return mIntentPersistableExtras;
585     }
586 
587     /**
588      * Return the weight of a shortcut, which will be used by Launcher for sorting.
589      * The larger the weight, the more "important" a shortcut is.
590      */
getWeight()591     public int getWeight() {
592         return mWeight;
593     }
594 
595     /**
596      * Optional values that application can set.
597      */
598     @Nullable
getExtras()599     public PersistableBundle getExtras() {
600         return mExtras;
601     }
602 
603     /** @hide */
getUserId()604     public int getUserId() {
605         return mUserId;
606     }
607 
608     /**
609      * {@link UserHandle} on which the publisher created shortcuts.
610      */
getUserHandle()611     public UserHandle getUserHandle() {
612         return UserHandle.of(mUserId);
613     }
614 
615     /**
616      * Last time when any of the fields was updated.
617      */
getLastChangedTimestamp()618     public long getLastChangedTimestamp() {
619         return mLastChangedTimestamp;
620     }
621 
622     /** @hide */
623     @ShortcutFlags
getFlags()624     public int getFlags() {
625         return mFlags;
626     }
627 
628     /** @hide*/
replaceFlags(@hortcutFlags int flags)629     public void replaceFlags(@ShortcutFlags int flags) {
630         mFlags = flags;
631     }
632 
633     /** @hide*/
addFlags(@hortcutFlags int flags)634     public void addFlags(@ShortcutFlags int flags) {
635         mFlags |= flags;
636     }
637 
638     /** @hide*/
clearFlags(@hortcutFlags int flags)639     public void clearFlags(@ShortcutFlags int flags) {
640         mFlags &= ~flags;
641     }
642 
643     /** @hide*/
hasFlags(@hortcutFlags int flags)644     public boolean hasFlags(@ShortcutFlags int flags) {
645         return (mFlags & flags) == flags;
646     }
647 
648     /** Return whether a shortcut is dynamic. */
isDynamic()649     public boolean isDynamic() {
650         return hasFlags(FLAG_DYNAMIC);
651     }
652 
653     /** Return whether a shortcut is pinned. */
isPinned()654     public boolean isPinned() {
655         return hasFlags(FLAG_PINNED);
656     }
657 
658     /**
659      * Return whether a shortcut's icon is a resource in the owning package.
660      *
661      * @see LauncherApps#getShortcutIconResId(ShortcutInfo)
662      */
hasIconResource()663     public boolean hasIconResource() {
664         return hasFlags(FLAG_HAS_ICON_RES);
665     }
666 
667     /**
668      * Return whether a shortcut's icon is stored as a file.
669      *
670      * @see LauncherApps#getShortcutIconFd(ShortcutInfo)
671      */
hasIconFile()672     public boolean hasIconFile() {
673         return hasFlags(FLAG_HAS_ICON_FILE);
674     }
675 
676     /**
677      * Return whether a shortcut only contains "key" information only or not.  If true, only the
678      * following fields are available.
679      * <ul>
680      *     <li>{@link #getId()}
681      *     <li>{@link #getPackageName()}
682      *     <li>{@link #getLastChangedTimestamp()}
683      *     <li>{@link #isDynamic()}
684      *     <li>{@link #isPinned()}
685      *     <li>{@link #hasIconResource()}
686      *     <li>{@link #hasIconFile()}
687      * </ul>
688      */
hasKeyFieldsOnly()689     public boolean hasKeyFieldsOnly() {
690         return hasFlags(FLAG_KEY_FIELDS_ONLY);
691     }
692 
693     /** @hide */
updateTimestamp()694     public void updateTimestamp() {
695         mLastChangedTimestamp = System.currentTimeMillis();
696     }
697 
698     /** @hide */
699     // VisibleForTesting
setTimestamp(long value)700     public void setTimestamp(long value) {
701         mLastChangedTimestamp = value;
702     }
703 
704     /** @hide */
clearIcon()705     public void clearIcon() {
706         mIcon = null;
707     }
708 
709     /** @hide */
setIconResourceId(int iconResourceId)710     public void setIconResourceId(int iconResourceId) {
711         mIconResourceId = iconResourceId;
712     }
713 
714     /**
715      * Get the resource ID for the icon, valid only when {@link #hasIconResource()} } is true.
716      */
getIconResourceId()717     public int getIconResourceId() {
718         return mIconResourceId;
719     }
720 
721     /** @hide */
getBitmapPath()722     public String getBitmapPath() {
723         return mBitmapPath;
724     }
725 
726     /** @hide */
setBitmapPath(String bitmapPath)727     public void setBitmapPath(String bitmapPath) {
728         mBitmapPath = bitmapPath;
729     }
730 
ShortcutInfo(Parcel source)731     private ShortcutInfo(Parcel source) {
732         final ClassLoader cl = getClass().getClassLoader();
733 
734         mUserId = source.readInt();
735         mId = source.readString();
736         mPackageName = source.readString();
737         mActivityComponent = source.readParcelable(cl);
738         mIcon = source.readParcelable(cl);
739         mTitle = source.readString();
740         mText = source.readString();
741         mIntent = source.readParcelable(cl);
742         mIntentPersistableExtras = source.readParcelable(cl);
743         mWeight = source.readInt();
744         mExtras = source.readParcelable(cl);
745         mLastChangedTimestamp = source.readLong();
746         mFlags = source.readInt();
747         mIconResourceId = source.readInt();
748         mBitmapPath = source.readString();
749 
750         int N = source.readInt();
751         if (N == 0) {
752             mCategories = null;
753         } else {
754             mCategories = new ArraySet<>(N);
755             for (int i = 0; i < N; i++) {
756                 mCategories.add(source.readString().intern());
757             }
758         }
759     }
760 
761     @Override
writeToParcel(Parcel dest, int flags)762     public void writeToParcel(Parcel dest, int flags) {
763         dest.writeInt(mUserId);
764         dest.writeString(mId);
765         dest.writeString(mPackageName);
766         dest.writeParcelable(mActivityComponent, flags);
767         dest.writeParcelable(mIcon, flags);
768         dest.writeString(mTitle);
769         dest.writeString(mText);
770 
771         dest.writeParcelable(mIntent, flags);
772         dest.writeParcelable(mIntentPersistableExtras, flags);
773         dest.writeInt(mWeight);
774         dest.writeParcelable(mExtras, flags);
775         dest.writeLong(mLastChangedTimestamp);
776         dest.writeInt(mFlags);
777         dest.writeInt(mIconResourceId);
778         dest.writeString(mBitmapPath);
779 
780         if (mCategories != null) {
781             final int N = mCategories.size();
782             dest.writeInt(N);
783             for (int i = 0; i < N; i++) {
784                 dest.writeString(mCategories.valueAt(i));
785             }
786         } else {
787             dest.writeInt(0);
788         }
789     }
790 
791     public static final Creator<ShortcutInfo> CREATOR =
792             new Creator<ShortcutInfo>() {
793                 public ShortcutInfo createFromParcel(Parcel source) {
794                     return new ShortcutInfo(source);
795                 }
796                 public ShortcutInfo[] newArray(int size) {
797                     return new ShortcutInfo[size];
798                 }
799             };
800 
801     @Override
describeContents()802     public int describeContents() {
803         return 0;
804     }
805 
806     /**
807      * Return a string representation, intended for logging.  Some fields will be retracted.
808      */
809     @Override
toString()810     public String toString() {
811         return toStringInner(/* secure =*/ true, /* includeInternalData =*/ false);
812     }
813 
814     /** @hide */
toInsecureString()815     public String toInsecureString() {
816         return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true);
817     }
818 
toStringInner(boolean secure, boolean includeInternalData)819     private String toStringInner(boolean secure, boolean includeInternalData) {
820         final StringBuilder sb = new StringBuilder();
821         sb.append("ShortcutInfo {");
822 
823         sb.append("id=");
824         sb.append(secure ? "***" : mId);
825 
826         sb.append(", packageName=");
827         sb.append(mPackageName);
828 
829         if (isDynamic()) {
830             sb.append(", dynamic");
831         }
832         if (isPinned()) {
833             sb.append(", pinned");
834         }
835 
836         sb.append(", activity=");
837         sb.append(mActivityComponent);
838 
839         sb.append(", title=");
840         sb.append(secure ? "***" : mTitle);
841 
842         sb.append(", text=");
843         sb.append(secure ? "***" : mText);
844 
845         sb.append(", categories=");
846         sb.append(mCategories);
847 
848         sb.append(", icon=");
849         sb.append(mIcon);
850 
851         sb.append(", weight=");
852         sb.append(mWeight);
853 
854         sb.append(", timestamp=");
855         sb.append(mLastChangedTimestamp);
856 
857         sb.append(", intent=");
858         sb.append(mIntent);
859 
860         sb.append(", intentExtras=");
861         sb.append(secure ? "***" : mIntentPersistableExtras);
862 
863         sb.append(", extras=");
864         sb.append(mExtras);
865 
866         sb.append(", flags=");
867         sb.append(mFlags);
868 
869         if (includeInternalData) {
870 
871             sb.append(", iconRes=");
872             sb.append(mIconResourceId);
873 
874             sb.append(", bitmapPath=");
875             sb.append(mBitmapPath);
876         }
877 
878         sb.append("}");
879         return sb.toString();
880     }
881 
882     /** @hide */
ShortcutInfo( @serIdInt int userId, String id, String packageName, ComponentName activityComponent, Icon icon, String title, String text, Set<String> categories, Intent intent, PersistableBundle intentPersistableExtras, int weight, PersistableBundle extras, long lastChangedTimestamp, int flags, int iconResId, String bitmapPath)883     public ShortcutInfo(
884             @UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
885             Icon icon, String title, String text, Set<String> categories, Intent intent,
886             PersistableBundle intentPersistableExtras,
887             int weight, PersistableBundle extras, long lastChangedTimestamp,
888             int flags, int iconResId, String bitmapPath) {
889         mUserId = userId;
890         mId = id;
891         mPackageName = packageName;
892         mActivityComponent = activityComponent;
893         mIcon = icon;
894         mTitle = title;
895         mText = text;
896         mCategories = clone(categories);
897         mIntent = intent;
898         mIntentPersistableExtras = intentPersistableExtras;
899         mWeight = weight;
900         mExtras = extras;
901         mLastChangedTimestamp = lastChangedTimestamp;
902         mFlags = flags;
903         mIconResourceId = iconResId;
904         mBitmapPath = bitmapPath;
905     }
906 }
907