[go: nahoru, domu]

1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.content.pm;
17
18import android.annotation.IntDef;
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.annotation.UserIdInt;
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.graphics.drawable.Icon;
26import android.os.Bundle;
27import android.os.Parcel;
28import android.os.Parcelable;
29import android.os.PersistableBundle;
30import android.os.UserHandle;
31import android.util.ArraySet;
32
33import com.android.internal.util.Preconditions;
34
35import java.lang.annotation.Retention;
36import java.lang.annotation.RetentionPolicy;
37import 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 */
56public 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
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
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     */
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     */
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     */
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     */
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     */
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 */
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. */
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
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
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
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
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
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
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
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
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
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
471        public ShortcutInfo build() {
472            return new ShortcutInfo(this);
473        }
474    }
475
476    /**
477     * Return the ID of the shortcut.
478     */
479    @NonNull
480    public String getId() {
481        return mId;
482    }
483
484    /**
485     * Return the package name of the creator application.
486     */
487    @NonNull
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
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
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
527    public String getTitle() {
528        return mTitle;
529    }
530
531    /**
532     * Return the shortcut text.
533     */
534    @Nullable
535    public String getText() {
536        return mText;
537    }
538
539    /**
540     * Return the categories.
541     */
542    @Nullable
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
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
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
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     */
591    public int getWeight() {
592        return mWeight;
593    }
594
595    /**
596     * Optional values that application can set.
597     */
598    @Nullable
599    public PersistableBundle getExtras() {
600        return mExtras;
601    }
602
603    /** @hide */
604    public int getUserId() {
605        return mUserId;
606    }
607
608    /**
609     * {@link UserHandle} on which the publisher created shortcuts.
610     */
611    public UserHandle getUserHandle() {
612        return UserHandle.of(mUserId);
613    }
614
615    /**
616     * Last time when any of the fields was updated.
617     */
618    public long getLastChangedTimestamp() {
619        return mLastChangedTimestamp;
620    }
621
622    /** @hide */
623    @ShortcutFlags
624    public int getFlags() {
625        return mFlags;
626    }
627
628    /** @hide*/
629    public void replaceFlags(@ShortcutFlags int flags) {
630        mFlags = flags;
631    }
632
633    /** @hide*/
634    public void addFlags(@ShortcutFlags int flags) {
635        mFlags |= flags;
636    }
637
638    /** @hide*/
639    public void clearFlags(@ShortcutFlags int flags) {
640        mFlags &= ~flags;
641    }
642
643    /** @hide*/
644    public boolean hasFlags(@ShortcutFlags int flags) {
645        return (mFlags & flags) == flags;
646    }
647
648    /** Return whether a shortcut is dynamic. */
649    public boolean isDynamic() {
650        return hasFlags(FLAG_DYNAMIC);
651    }
652
653    /** Return whether a shortcut is pinned. */
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     */
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     */
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     */
689    public boolean hasKeyFieldsOnly() {
690        return hasFlags(FLAG_KEY_FIELDS_ONLY);
691    }
692
693    /** @hide */
694    public void updateTimestamp() {
695        mLastChangedTimestamp = System.currentTimeMillis();
696    }
697
698    /** @hide */
699    // VisibleForTesting
700    public void setTimestamp(long value) {
701        mLastChangedTimestamp = value;
702    }
703
704    /** @hide */
705    public void clearIcon() {
706        mIcon = null;
707    }
708
709    /** @hide */
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     */
717    public int getIconResourceId() {
718        return mIconResourceId;
719    }
720
721    /** @hide */
722    public String getBitmapPath() {
723        return mBitmapPath;
724    }
725
726    /** @hide */
727    public void setBitmapPath(String bitmapPath) {
728        mBitmapPath = bitmapPath;
729    }
730
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
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
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
810    public String toString() {
811        return toStringInner(/* secure =*/ true, /* includeInternalData =*/ false);
812    }
813
814    /** @hide */
815    public String toInsecureString() {
816        return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true);
817    }
818
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 */
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