blob: bde4f614a39e3a1a503385ad12e813a54fe9c6f5 [file] [log] [blame]
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001/*
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;
Mehdi Alizadeh88873652019-02-04 14:16:46 -080021import android.annotation.SystemApi;
Makoto Onukia4f89b12017-10-05 10:37:55 -070022import android.annotation.TestApi;
Makoto Onukiabe84422016-04-07 09:41:19 -070023import android.annotation.UserIdInt;
Felipe Leme90205ef2019-03-05 09:59:52 -080024import android.app.Notification;
Mehdi Alizadeh14242af2018-12-20 20:11:35 -080025import android.app.Person;
Makoto Onuki347a6bd2016-07-19 11:13:08 -070026import android.app.TaskStackBuilder;
Artur Satayeve23a0eb2019-12-10 17:47:52 +000027import android.compat.annotation.UnsupportedAppUsage;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080028import android.content.ComponentName;
29import android.content.Context;
30import android.content.Intent;
Felipe Leme90205ef2019-03-05 09:59:52 -080031import android.content.LocusId;
Makoto Onuki4a910962016-07-07 13:57:34 -070032import android.content.pm.LauncherApps.ShortcutQuery;
Makoto Onuki20c95f82016-05-11 16:51:01 -070033import android.content.res.Resources;
Makoto Onuki157b1622016-06-02 16:13:10 -070034import android.content.res.Resources.NotFoundException;
Makoto Onuki4a910962016-07-07 13:57:34 -070035import android.graphics.Bitmap;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080036import android.graphics.drawable.Icon;
Mathew Inwood8c854f82018-09-14 12:35:36 +010037import android.os.Build;
Makoto Onuki55046222016-03-08 10:49:47 -080038import android.os.Bundle;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080039import android.os.Parcel;
40import android.os.Parcelable;
41import android.os.PersistableBundle;
42import android.os.UserHandle;
Makoto Onukidf6da042016-06-16 09:51:40 -070043import android.text.TextUtils;
Makoto Onukibe73a802016-04-15 14:46:35 -070044import android.util.ArraySet;
Makoto Onuki157b1622016-06-02 16:13:10 -070045import android.util.Log;
Felipe Leme90205ef2019-03-05 09:59:52 -080046import android.view.contentcapture.ContentCaptureContext;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080047
Makoto Onuki157b1622016-06-02 16:13:10 -070048import com.android.internal.annotations.VisibleForTesting;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080049import com.android.internal.util.Preconditions;
50
51import java.lang.annotation.Retention;
52import java.lang.annotation.RetentionPolicy;
Makoto Onukib5a012f2016-06-21 11:13:53 -070053import java.util.List;
Daulet Zhanguzina2044e12019-12-30 16:34:59 +000054import java.util.Objects;
Makoto Onukibe73a802016-04-15 14:46:35 -070055import java.util.Set;
Makoto Onuki6f7362d92016-03-04 13:39:41 -080056
57/**
Kevin Hufnagle68d699d2016-10-14 19:04:51 -070058 * Represents a shortcut that can be published via {@link ShortcutManager}.
Makoto Onuki6f7362d92016-03-04 13:39:41 -080059 *
Makoto Onuki4a910962016-07-07 13:57:34 -070060 * @see ShortcutManager
Makoto Onuki6f7362d92016-03-04 13:39:41 -080061 */
Jeff Sharkey70168dd2016-03-30 21:47:16 -060062public final class ShortcutInfo implements Parcelable {
Makoto Onuki157b1622016-06-02 16:13:10 -070063 static final String TAG = "Shortcut";
64
65 private static final String RES_TYPE_STRING = "string";
66
67 private static final String ANDROID_PACKAGE_NAME = "android";
68
Makoto Onuki9e1f5592016-06-08 12:30:23 -070069 private static final int IMPLICIT_RANK_MASK = 0x7fffffff;
70
71 private static final int RANK_CHANGED_BIT = ~IMPLICIT_RANK_MASK;
72
73 /** @hide */
74 public static final int RANK_NOT_SET = Integer.MAX_VALUE;
75
Makoto Onuki157b1622016-06-02 16:13:10 -070076 /** @hide */
Makoto Onuki6f7362d92016-03-04 13:39:41 -080077 public static final int FLAG_DYNAMIC = 1 << 0;
78
Makoto Onuki157b1622016-06-02 16:13:10 -070079 /** @hide */
Makoto Onuki6f7362d92016-03-04 13:39:41 -080080 public static final int FLAG_PINNED = 1 << 1;
81
Makoto Onuki157b1622016-06-02 16:13:10 -070082 /** @hide */
Makoto Onuki6f7362d92016-03-04 13:39:41 -080083 public static final int FLAG_HAS_ICON_RES = 1 << 2;
84
Makoto Onuki157b1622016-06-02 16:13:10 -070085 /** @hide */
Makoto Onuki6f7362d92016-03-04 13:39:41 -080086 public static final int FLAG_HAS_ICON_FILE = 1 << 3;
87
Makoto Onuki157b1622016-06-02 16:13:10 -070088 /** @hide */
Makoto Onuki55046222016-03-08 10:49:47 -080089 public static final int FLAG_KEY_FIELDS_ONLY = 1 << 4;
90
Makoto Onuki157b1622016-06-02 16:13:10 -070091 /** @hide */
Makoto Onuki22fcc682016-05-17 14:52:19 -070092 public static final int FLAG_MANIFEST = 1 << 5;
Makoto Onuki20c95f82016-05-11 16:51:01 -070093
Makoto Onuki157b1622016-06-02 16:13:10 -070094 /** @hide */
Makoto Onuki20c95f82016-05-11 16:51:01 -070095 public static final int FLAG_DISABLED = 1 << 6;
96
Makoto Onuki157b1622016-06-02 16:13:10 -070097 /** @hide */
Makoto Onuki20c95f82016-05-11 16:51:01 -070098 public static final int FLAG_STRINGS_RESOLVED = 1 << 7;
99
Makoto Onuki157b1622016-06-02 16:13:10 -0700100 /** @hide */
Makoto Onuki22fcc682016-05-17 14:52:19 -0700101 public static final int FLAG_IMMUTABLE = 1 << 8;
102
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800103 /** @hide */
Hyunyoung Songe4179e22017-03-01 12:51:26 -0800104 public static final int FLAG_ADAPTIVE_BITMAP = 1 << 9;
Hyunyoung Songf281e7a2017-02-13 10:57:42 -0800105
106 /** @hide */
Makoto Onukibf563b62017-05-04 10:25:30 -0700107 public static final int FLAG_RETURNED_BY_SERVICE = 1 << 10;
108
Makoto Onuki475c3652017-05-08 14:29:03 -0700109 /** @hide When this is set, the bitmap icon is waiting to be saved. */
110 public static final int FLAG_ICON_FILE_PENDING_SAVE = 1 << 11;
111
Makoto Onukia4f89b12017-10-05 10:37:55 -0700112 /**
113 * "Shadow" shortcuts are the ones that are restored, but the owner package hasn't been
114 * installed yet.
115 * @hide
116 */
117 public static final int FLAG_SHADOW = 1 << 12;
118
Makoto Onukibf563b62017-05-04 10:25:30 -0700119 /** @hide */
Mehdi Alizadeh14242af2018-12-20 20:11:35 -0800120 public static final int FLAG_LONG_LIVED = 1 << 13;
121
122 /** @hide */
Mehdi Alizadeh505fb762020-01-21 17:26:24 -0800123 public static final int FLAG_CACHED = 1 << 14;
124
125 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700126 @IntDef(flag = true, prefix = { "FLAG_" }, value = {
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800127 FLAG_DYNAMIC,
128 FLAG_PINNED,
129 FLAG_HAS_ICON_RES,
130 FLAG_HAS_ICON_FILE,
Makoto Onuki55046222016-03-08 10:49:47 -0800131 FLAG_KEY_FIELDS_ONLY,
Makoto Onuki22fcc682016-05-17 14:52:19 -0700132 FLAG_MANIFEST,
Makoto Onuki20c95f82016-05-11 16:51:01 -0700133 FLAG_DISABLED,
134 FLAG_STRINGS_RESOLVED,
Makoto Onuki22fcc682016-05-17 14:52:19 -0700135 FLAG_IMMUTABLE,
Hyunyoung Songe4179e22017-03-01 12:51:26 -0800136 FLAG_ADAPTIVE_BITMAP,
Makoto Onuki475c3652017-05-08 14:29:03 -0700137 FLAG_RETURNED_BY_SERVICE,
138 FLAG_ICON_FILE_PENDING_SAVE,
Mehdi Alizadeh14242af2018-12-20 20:11:35 -0800139 FLAG_SHADOW,
140 FLAG_LONG_LIVED,
Mehdi Alizadeh505fb762020-01-21 17:26:24 -0800141 FLAG_CACHED,
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800142 })
143 @Retention(RetentionPolicy.SOURCE)
144 public @interface ShortcutFlags {}
145
146 // Cloning options.
147
Makoto Onuki157b1622016-06-02 16:13:10 -0700148 /** @hide */
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800149 private static final int CLONE_REMOVE_ICON = 1 << 0;
150
Makoto Onuki157b1622016-06-02 16:13:10 -0700151 /** @hide */
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800152 private static final int CLONE_REMOVE_INTENT = 1 << 1;
153
Makoto Onuki157b1622016-06-02 16:13:10 -0700154 /** @hide */
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800155 public static final int CLONE_REMOVE_NON_KEY_INFO = 1 << 2;
156
Makoto Onuki157b1622016-06-02 16:13:10 -0700157 /** @hide */
158 public static final int CLONE_REMOVE_RES_NAMES = 1 << 3;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800159
Makoto Onuki157b1622016-06-02 16:13:10 -0700160 /** @hide */
Mehdi Alizadehc6096022019-05-27 12:17:36 -0700161 public static final int CLONE_REMOVE_PERSON = 1 << 4;
162
163 /** @hide */
Makoto Onuki157b1622016-06-02 16:13:10 -0700164 public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON | CLONE_REMOVE_RES_NAMES;
165
166 /** @hide */
167 public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT
Mehdi Alizadehc6096022019-05-27 12:17:36 -0700168 | CLONE_REMOVE_RES_NAMES | CLONE_REMOVE_PERSON;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800169
170 /** @hide */
Makoto Onukia01f4f02016-12-15 15:58:41 -0800171 public static final int CLONE_REMOVE_FOR_LAUNCHER_APPROVAL = CLONE_REMOVE_INTENT
Mehdi Alizadehc6096022019-05-27 12:17:36 -0700172 | CLONE_REMOVE_RES_NAMES | CLONE_REMOVE_PERSON;
173
174 /** @hide */
175 public static final int CLONE_REMOVE_FOR_APP_PREDICTION = CLONE_REMOVE_ICON
Makoto Onukia01f4f02016-12-15 15:58:41 -0800176 | CLONE_REMOVE_RES_NAMES;
177
178 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700179 @IntDef(flag = true, prefix = { "CLONE_" }, value = {
180 CLONE_REMOVE_ICON,
181 CLONE_REMOVE_INTENT,
182 CLONE_REMOVE_NON_KEY_INFO,
183 CLONE_REMOVE_RES_NAMES,
Mehdi Alizadehc6096022019-05-27 12:17:36 -0700184 CLONE_REMOVE_PERSON,
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700185 CLONE_REMOVE_FOR_CREATOR,
Mehdi Alizadehc6096022019-05-27 12:17:36 -0700186 CLONE_REMOVE_FOR_LAUNCHER,
187 CLONE_REMOVE_FOR_LAUNCHER_APPROVAL,
188 CLONE_REMOVE_FOR_APP_PREDICTION
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700189 })
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800190 @Retention(RetentionPolicy.SOURCE)
191 public @interface CloneFlags {}
192
Makoto Onukib6d35232016-04-04 15:57:17 -0700193 /**
Makoto Onukia4f89b12017-10-05 10:37:55 -0700194 * Shortcut is not disabled.
195 */
196 public static final int DISABLED_REASON_NOT_DISABLED = 0;
197
198 /**
199 * Shortcut has been disabled by the publisher app with the
200 * {@link ShortcutManager#disableShortcuts(List)} API.
201 */
202 public static final int DISABLED_REASON_BY_APP = 1;
203
204 /**
205 * Shortcut has been disabled due to changes to the publisher app. (e.g. a manifest shortcut
206 * no longer exists.)
207 */
208 public static final int DISABLED_REASON_APP_CHANGED = 2;
209
210 /**
Makoto Onuki5482a8e62018-01-09 10:31:08 -0800211 * Shortcut is disabled for an unknown reason.
212 */
213 public static final int DISABLED_REASON_UNKNOWN = 3;
214
215 /**
Makoto Onukia4f89b12017-10-05 10:37:55 -0700216 * A disabled reason that's equal to or bigger than this is due to backup and restore issue.
217 * A shortcut with such a reason wil be visible to the launcher, but not to the publisher.
218 * ({@link #isVisibleToPublisher()} will be false.)
219 */
220 private static final int DISABLED_REASON_RESTORE_ISSUE_START = 100;
221
222 /**
223 * Shortcut has been restored from the previous device, but the publisher app on the current
224 * device is of a lower version. The shortcut will not be usable until the app is upgraded to
225 * the same version or higher.
226 */
227 public static final int DISABLED_REASON_VERSION_LOWER = 100;
228
229 /**
230 * Shortcut has not been restored because the publisher app does not support backup and restore.
231 */
232 public static final int DISABLED_REASON_BACKUP_NOT_SUPPORTED = 101;
233
234 /**
235 * Shortcut has not been restored because the publisher app's signature has changed.
236 */
237 public static final int DISABLED_REASON_SIGNATURE_MISMATCH = 102;
238
239 /**
240 * Shortcut has not been restored for unknown reason.
241 */
242 public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103;
243
244 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700245 @IntDef(prefix = { "DISABLED_REASON_" }, value = {
Makoto Onukia4f89b12017-10-05 10:37:55 -0700246 DISABLED_REASON_NOT_DISABLED,
247 DISABLED_REASON_BY_APP,
248 DISABLED_REASON_APP_CHANGED,
Makoto Onuki5482a8e62018-01-09 10:31:08 -0800249 DISABLED_REASON_UNKNOWN,
Makoto Onukia4f89b12017-10-05 10:37:55 -0700250 DISABLED_REASON_VERSION_LOWER,
251 DISABLED_REASON_BACKUP_NOT_SUPPORTED,
252 DISABLED_REASON_SIGNATURE_MISMATCH,
253 DISABLED_REASON_OTHER_RESTORE_ISSUE,
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700254 })
Makoto Onukia4f89b12017-10-05 10:37:55 -0700255 @Retention(RetentionPolicy.SOURCE)
256 public @interface DisabledReason{}
257
Makoto Onukib1588c02017-10-12 15:11:45 -0700258 /**
259 * Return a label for disabled reasons, which are *not* supposed to be shown to the user.
260 * @hide
261 */
262 public static String getDisabledReasonDebugString(@DisabledReason int disabledReason) {
Makoto Onukia4f89b12017-10-05 10:37:55 -0700263 switch (disabledReason) {
264 case DISABLED_REASON_NOT_DISABLED:
265 return "[Not disabled]";
266 case DISABLED_REASON_BY_APP:
267 return "[Disabled: by app]";
268 case DISABLED_REASON_APP_CHANGED:
269 return "[Disabled: app changed]";
270 case DISABLED_REASON_VERSION_LOWER:
271 return "[Disabled: lower version]";
272 case DISABLED_REASON_BACKUP_NOT_SUPPORTED:
273 return "[Disabled: backup not supported]";
274 case DISABLED_REASON_SIGNATURE_MISMATCH:
275 return "[Disabled: signature mismatch]";
276 case DISABLED_REASON_OTHER_RESTORE_ISSUE:
277 return "[Disabled: unknown restore issue]";
278 }
279 return "[Disabled: unknown reason:" + disabledReason + "]";
280 }
281
Makoto Onukib1588c02017-10-12 15:11:45 -0700282 /**
283 * Return a label for a disabled reason for shortcuts that are disabled due to a backup and
284 * restore issue. If the reason is not due to backup & restore, then it'll return null.
285 *
286 * This method returns localized, user-facing strings, which will be returned by
287 * {@link #getDisabledMessage()}.
288 *
289 * @hide
290 */
291 public static String getDisabledReasonForRestoreIssue(Context context,
292 @DisabledReason int disabledReason) {
293 final Resources res = context.getResources();
294
295 switch (disabledReason) {
296 case DISABLED_REASON_VERSION_LOWER:
297 return res.getString(
298 com.android.internal.R.string.shortcut_restored_on_lower_version);
299 case DISABLED_REASON_BACKUP_NOT_SUPPORTED:
300 return res.getString(
301 com.android.internal.R.string.shortcut_restore_not_supported);
302 case DISABLED_REASON_SIGNATURE_MISMATCH:
303 return res.getString(
304 com.android.internal.R.string.shortcut_restore_signature_mismatch);
305 case DISABLED_REASON_OTHER_RESTORE_ISSUE:
306 return res.getString(
307 com.android.internal.R.string.shortcut_restore_unknown_issue);
Makoto Onuki5482a8e62018-01-09 10:31:08 -0800308 case DISABLED_REASON_UNKNOWN:
309 return res.getString(
310 com.android.internal.R.string.shortcut_disabled_reason_unknown);
Makoto Onukib1588c02017-10-12 15:11:45 -0700311 }
312 return null;
313 }
314
Makoto Onukia4f89b12017-10-05 10:37:55 -0700315 /** @hide */
316 public static boolean isDisabledForRestoreIssue(@DisabledReason int disabledReason) {
317 return disabledReason >= DISABLED_REASON_RESTORE_ISSUE_START;
318 }
319
320 /**
Makoto Onuki4a910962016-07-07 13:57:34 -0700321 * Shortcut category for messaging related actions, such as chat.
Makoto Onukib6d35232016-04-04 15:57:17 -0700322 */
323 public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
324
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800325 private final String mId;
326
327 @NonNull
328 private final String mPackageName;
329
330 @Nullable
Makoto Onuki22fcc682016-05-17 14:52:19 -0700331 private ComponentName mActivity;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800332
333 @Nullable
334 private Icon mIcon;
335
Makoto Onuki20c95f82016-05-11 16:51:01 -0700336 private int mTitleResId;
337
Makoto Onuki157b1622016-06-02 16:13:10 -0700338 private String mTitleResName;
339
Makoto Onuki20c95f82016-05-11 16:51:01 -0700340 @Nullable
Makoto Onuki22fcc682016-05-17 14:52:19 -0700341 private CharSequence mTitle;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800342
Makoto Onuki20c95f82016-05-11 16:51:01 -0700343 private int mTextResId;
344
Makoto Onuki157b1622016-06-02 16:13:10 -0700345 private String mTextResName;
346
Makoto Onukie3ae7ec2016-03-29 15:45:25 -0700347 @Nullable
Makoto Onuki22fcc682016-05-17 14:52:19 -0700348 private CharSequence mText;
Makoto Onukie3ae7ec2016-03-29 15:45:25 -0700349
Makoto Onuki20c95f82016-05-11 16:51:01 -0700350 private int mDisabledMessageResId;
351
Makoto Onuki157b1622016-06-02 16:13:10 -0700352 private String mDisabledMessageResName;
353
Makoto Onuki20c95f82016-05-11 16:51:01 -0700354 @Nullable
Makoto Onuki22fcc682016-05-17 14:52:19 -0700355 private CharSequence mDisabledMessage;
Makoto Onuki20c95f82016-05-11 16:51:01 -0700356
357 @Nullable
Makoto Onukibe73a802016-04-15 14:46:35 -0700358 private ArraySet<String> mCategories;
Makoto Onukib6d35232016-04-04 15:57:17 -0700359
Makoto Onuki55046222016-03-08 10:49:47 -0800360 /**
Makoto Onuki440a1ea2016-07-20 14:21:18 -0700361 * Intents *with extras removed*.
Makoto Onuki55046222016-03-08 10:49:47 -0800362 */
Makoto Onuki20c95f82016-05-11 16:51:01 -0700363 @Nullable
Makoto Onuki440a1ea2016-07-20 14:21:18 -0700364 private Intent[] mIntents;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800365
Makoto Onuki55046222016-03-08 10:49:47 -0800366 /**
Makoto Onuki440a1ea2016-07-20 14:21:18 -0700367 * Extras for the intents.
Makoto Onuki55046222016-03-08 10:49:47 -0800368 */
Makoto Onuki20c95f82016-05-11 16:51:01 -0700369 @Nullable
Makoto Onuki440a1ea2016-07-20 14:21:18 -0700370 private PersistableBundle[] mIntentPersistableExtrases;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800371
Mehdi Alizadeh14242af2018-12-20 20:11:35 -0800372 @Nullable
373 private Person[] mPersons;
374
Felipe Leme90205ef2019-03-05 09:59:52 -0800375 @Nullable
376 private LocusId mLocusId;
377
Makoto Onuki20c95f82016-05-11 16:51:01 -0700378 private int mRank;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800379
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700380 /**
381 * Internally used for auto-rank-adjustment.
382 *
383 * RANK_CHANGED_BIT is used to denote that the rank of a shortcut is changing.
384 * The rest of the bits are used to denote the order in which shortcuts are passed to
385 * APIs, which is used to preserve the argument order when ranks are tie.
386 */
387 private int mImplicitRank;
388
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800389 @Nullable
390 private PersistableBundle mExtras;
391
392 private long mLastChangedTimestamp;
393
394 // Internal use only.
395 @ShortcutFlags
396 private int mFlags;
397
398 // Internal use only.
Makoto Onuki157b1622016-06-02 16:13:10 -0700399 private int mIconResId;
400
401 private String mIconResName;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800402
403 // Internal use only.
404 @Nullable
405 private String mBitmapPath;
406
Makoto Onukiabe84422016-04-07 09:41:19 -0700407 private final int mUserId;
408
Makoto Onukia4f89b12017-10-05 10:37:55 -0700409 /** @hide */
410 public static final int VERSION_CODE_UNKNOWN = -1;
411
412 private int mDisabledReason;
413
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800414 private ShortcutInfo(Builder b) {
Makoto Onukiabe84422016-04-07 09:41:19 -0700415 mUserId = b.mContext.getUserId();
416
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800417 mId = Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided");
418
419 // Note we can't do other null checks here because SM.updateShortcuts() takes partial
420 // information.
421 mPackageName = b.mContext.getPackageName();
Makoto Onuki22fcc682016-05-17 14:52:19 -0700422 mActivity = b.mActivity;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800423 mIcon = b.mIcon;
424 mTitle = b.mTitle;
Makoto Onuki20c95f82016-05-11 16:51:01 -0700425 mTitleResId = b.mTitleResId;
Makoto Onukie3ae7ec2016-03-29 15:45:25 -0700426 mText = b.mText;
Makoto Onuki20c95f82016-05-11 16:51:01 -0700427 mTextResId = b.mTextResId;
428 mDisabledMessage = b.mDisabledMessage;
429 mDisabledMessageResId = b.mDisabledMessageResId;
Makoto Onukidf6da042016-06-16 09:51:40 -0700430 mCategories = cloneCategories(b.mCategories);
Makoto Onuki440a1ea2016-07-20 14:21:18 -0700431 mIntents = cloneIntents(b.mIntents);
432 fixUpIntentExtras();
Mehdi Alizadeh14242af2018-12-20 20:11:35 -0800433 mPersons = clonePersons(b.mPersons);
434 if (b.mIsLongLived) {
435 setLongLived();
436 }
Makoto Onuki20c95f82016-05-11 16:51:01 -0700437 mRank = b.mRank;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800438 mExtras = b.mExtras;
Felipe Leme90205ef2019-03-05 09:59:52 -0800439 mLocusId = b.mLocusId;
440
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800441 updateTimestamp();
442 }
443
Makoto Onuki440a1ea2016-07-20 14:21:18 -0700444 /**
445 * Extract extras from {@link #mIntents} and set them to {@link #mIntentPersistableExtrases}
446 * as {@link PersistableBundle}, and remove extras from the original intents.
447 */
448 private void fixUpIntentExtras() {
449 if (mIntents == null) {
450 mIntentPersistableExtrases = null;
451 return;
452 }
453 mIntentPersistableExtrases = new PersistableBundle[mIntents.length];
454 for (int i = 0; i < mIntents.length; i++) {
455 final Intent intent = mIntents[i];
456 final Bundle extras = intent.getExtras();
457 if (extras == null) {
458 mIntentPersistableExtrases[i] = null;
459 } else {
460 mIntentPersistableExtrases[i] = new PersistableBundle(extras);
461 intent.replaceExtras((Bundle) null);
462 }
463 }
464 }
465
466 private static ArraySet<String> cloneCategories(Set<String> source) {
Makoto Onukidf6da042016-06-16 09:51:40 -0700467 if (source == null) {
468 return null;
469 }
470 final ArraySet<String> ret = new ArraySet<>(source.size());
471 for (CharSequence s : source) {
472 if (!TextUtils.isEmpty(s)) {
473 ret.add(s.toString().intern());
474 }
475 }
476 return ret;
Makoto Onukib6d35232016-04-04 15:57:17 -0700477 }
478
Makoto Onuki440a1ea2016-07-20 14:21:18 -0700479 private static Intent[] cloneIntents(Intent[] intents) {
480 if (intents == null) {
481 return null;
482 }
483 final Intent[] ret = new Intent[intents.length];
484 for (int i = 0; i < ret.length; i++) {
485 if (intents[i] != null) {
486 ret[i] = new Intent(intents[i]);
487 }
488 }
489 return ret;
490 }
491
492 private static PersistableBundle[] clonePersistableBundle(PersistableBundle[] bundle) {
493 if (bundle == null) {
494 return null;
495 }
496 final PersistableBundle[] ret = new PersistableBundle[bundle.length];
497 for (int i = 0; i < ret.length; i++) {
498 if (bundle[i] != null) {
499 ret[i] = new PersistableBundle(bundle[i]);
500 }
501 }
502 return ret;
503 }
504
Mehdi Alizadeh14242af2018-12-20 20:11:35 -0800505 private static Person[] clonePersons(Person[] persons) {
506 if (persons == null) {
507 return null;
508 }
509 final Person[] ret = new Person[persons.length];
510 for (int i = 0; i < ret.length; i++) {
511 if (persons[i] != null) {
512 // Don't need to keep the icon, remove it to save space
513 ret[i] = persons[i].toBuilder().setIcon(null).build();
514 }
515 }
516 return ret;
517 }
518
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800519 /**
520 * Throws if any of the mandatory fields is not set.
521 *
522 * @hide
523 */
Makoto Onuki106ff7a2016-12-01 10:17:57 -0800524 public void enforceMandatoryFields(boolean forPinned) {
Makoto Onukie3ae7ec2016-03-29 15:45:25 -0700525 Preconditions.checkStringNotEmpty(mId, "Shortcut ID must be provided");
Makoto Onuki106ff7a2016-12-01 10:17:57 -0800526 if (!forPinned) {
Daulet Zhanguzina2044e12019-12-30 16:34:59 +0000527 Objects.requireNonNull(mActivity, "Activity must be provided");
Makoto Onuki106ff7a2016-12-01 10:17:57 -0800528 }
Makoto Onuki20c95f82016-05-11 16:51:01 -0700529 if (mTitle == null && mTitleResId == 0) {
Makoto Onukia1d38b32016-06-10 15:32:26 -0700530 throw new IllegalArgumentException("Short label must be provided");
Makoto Onuki20c95f82016-05-11 16:51:01 -0700531 }
Daulet Zhanguzina2044e12019-12-30 16:34:59 +0000532 Objects.requireNonNull(mIntents, "Shortcut Intent must be provided");
Makoto Onuki99302b52017-03-29 12:42:26 -0700533 Preconditions.checkArgument(mIntents.length > 0, "Shortcut Intent must be provided");
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800534 }
535
536 /**
537 * Copy constructor.
538 */
539 private ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags) {
Makoto Onukiabe84422016-04-07 09:41:19 -0700540 mUserId = source.mUserId;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800541 mId = source.mId;
542 mPackageName = source.mPackageName;
Makoto Onuki4d6b87f2016-06-17 13:47:40 -0700543 mActivity = source.mActivity;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800544 mFlags = source.mFlags;
545 mLastChangedTimestamp = source.mLastChangedTimestamp;
Makoto Onukia4f89b12017-10-05 10:37:55 -0700546 mDisabledReason = source.mDisabledReason;
Felipe Leme90205ef2019-03-05 09:59:52 -0800547 mLocusId = source.mLocusId;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800548
Makoto Onukib6d35232016-04-04 15:57:17 -0700549 // Just always keep it since it's cheep.
Makoto Onuki157b1622016-06-02 16:13:10 -0700550 mIconResId = source.mIconResId;
Makoto Onukib6d35232016-04-04 15:57:17 -0700551
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800552 if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
Makoto Onuki55046222016-03-08 10:49:47 -0800553
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800554 if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
555 mIcon = source.mIcon;
Makoto Onuki7a6a05f2016-03-10 17:01:08 -0800556 mBitmapPath = source.mBitmapPath;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800557 }
558
559 mTitle = source.mTitle;
Makoto Onuki20c95f82016-05-11 16:51:01 -0700560 mTitleResId = source.mTitleResId;
Makoto Onukie3ae7ec2016-03-29 15:45:25 -0700561 mText = source.mText;
Makoto Onuki20c95f82016-05-11 16:51:01 -0700562 mTextResId = source.mTextResId;
563 mDisabledMessage = source.mDisabledMessage;
564 mDisabledMessageResId = source.mDisabledMessageResId;
Makoto Onukidf6da042016-06-16 09:51:40 -0700565 mCategories = cloneCategories(source.mCategories);
Mehdi Alizadehc6096022019-05-27 12:17:36 -0700566 if ((cloneFlags & CLONE_REMOVE_PERSON) == 0) {
567 mPersons = clonePersons(source.mPersons);
568 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800569 if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
Makoto Onuki440a1ea2016-07-20 14:21:18 -0700570 mIntents = cloneIntents(source.mIntents);
571 mIntentPersistableExtrases =
572 clonePersistableBundle(source.mIntentPersistableExtrases);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800573 }
Makoto Onuki20c95f82016-05-11 16:51:01 -0700574 mRank = source.mRank;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800575 mExtras = source.mExtras;
Makoto Onuki157b1622016-06-02 16:13:10 -0700576
577 if ((cloneFlags & CLONE_REMOVE_RES_NAMES) == 0) {
578 mTitleResName = source.mTitleResName;
579 mTextResName = source.mTextResName;
580 mDisabledMessageResName = source.mDisabledMessageResName;
581 mIconResName = source.mIconResName;
582 }
Makoto Onuki55046222016-03-08 10:49:47 -0800583 } else {
584 // Set this bit.
585 mFlags |= FLAG_KEY_FIELDS_ONLY;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800586 }
587 }
588
Makoto Onuki157b1622016-06-02 16:13:10 -0700589 /**
590 * Load a string resource from the publisher app.
591 *
592 * @param resId resource ID
593 * @param defValue default value to be returned when the specified resource isn't found.
594 */
595 private CharSequence getResourceString(Resources res, int resId, CharSequence defValue) {
596 try {
597 return res.getString(resId);
598 } catch (NotFoundException e) {
599 Log.e(TAG, "Resource for ID=" + resId + " not found in package " + mPackageName);
600 return defValue;
601 }
602 }
603
604 /**
605 * Load the string resources for the text fields and set them to the actual value fields.
606 * This will set {@link #FLAG_STRINGS_RESOLVED}.
607 *
608 * @param res {@link Resources} for the publisher. Must have been loaded with
609 * {@link PackageManager#getResourcesForApplicationAsUser}.
610 *
611 * @hide
612 */
613 public void resolveResourceStrings(@NonNull Resources res) {
Makoto Onuki20c95f82016-05-11 16:51:01 -0700614 mFlags |= FLAG_STRINGS_RESOLVED;
615
616 if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)) {
617 return; // Bail early.
618 }
Makoto Onuki20c95f82016-05-11 16:51:01 -0700619
620 if (mTitleResId != 0) {
Makoto Onuki157b1622016-06-02 16:13:10 -0700621 mTitle = getResourceString(res, mTitleResId, mTitle);
Makoto Onuki20c95f82016-05-11 16:51:01 -0700622 }
623 if (mTextResId != 0) {
Makoto Onuki157b1622016-06-02 16:13:10 -0700624 mText = getResourceString(res, mTextResId, mText);
Makoto Onuki20c95f82016-05-11 16:51:01 -0700625 }
626 if (mDisabledMessageResId != 0) {
Makoto Onuki157b1622016-06-02 16:13:10 -0700627 mDisabledMessage = getResourceString(res, mDisabledMessageResId, mDisabledMessage);
Makoto Onuki20c95f82016-05-11 16:51:01 -0700628 }
629 }
630
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800631 /**
Makoto Onuki157b1622016-06-02 16:13:10 -0700632 * Look up resource name for a given resource ID.
633 *
634 * @return a simple resource name (e.g. "text_1") when {@code withType} is false, or with the
635 * type (e.g. "string/text_1").
636 *
637 * @hide
638 */
639 @VisibleForTesting
640 public static String lookUpResourceName(@NonNull Resources res, int resId, boolean withType,
641 @NonNull String packageName) {
642 if (resId == 0) {
643 return null;
644 }
645 try {
646 final String fullName = res.getResourceName(resId);
647
648 if (ANDROID_PACKAGE_NAME.equals(getResourcePackageName(fullName))) {
649 // If it's a framework resource, the value won't change, so just return the ID
650 // value as a string.
651 return String.valueOf(resId);
652 }
653 return withType ? getResourceTypeAndEntryName(fullName)
654 : getResourceEntryName(fullName);
655 } catch (NotFoundException e) {
656 Log.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName
657 + ". Resource IDs may change when the application is upgraded, and the system"
658 + " may not be able to find the correct resource.");
659 return null;
660 }
661 }
662
663 /**
664 * Extract the package name from a fully-donated resource name.
665 * e.g. "com.android.app1:drawable/icon1" -> "com.android.app1"
666 * @hide
667 */
668 @VisibleForTesting
669 public static String getResourcePackageName(@NonNull String fullResourceName) {
670 final int p1 = fullResourceName.indexOf(':');
671 if (p1 < 0) {
672 return null;
673 }
674 return fullResourceName.substring(0, p1);
675 }
676
677 /**
678 * Extract the type name from a fully-donated resource name.
679 * e.g. "com.android.app1:drawable/icon1" -> "drawable"
680 * @hide
681 */
682 @VisibleForTesting
683 public static String getResourceTypeName(@NonNull String fullResourceName) {
684 final int p1 = fullResourceName.indexOf(':');
685 if (p1 < 0) {
686 return null;
687 }
688 final int p2 = fullResourceName.indexOf('/', p1 + 1);
689 if (p2 < 0) {
690 return null;
691 }
692 return fullResourceName.substring(p1 + 1, p2);
693 }
694
695 /**
696 * Extract the type name + the entry name from a fully-donated resource name.
697 * e.g. "com.android.app1:drawable/icon1" -> "drawable/icon1"
698 * @hide
699 */
700 @VisibleForTesting
701 public static String getResourceTypeAndEntryName(@NonNull String fullResourceName) {
702 final int p1 = fullResourceName.indexOf(':');
703 if (p1 < 0) {
704 return null;
705 }
706 return fullResourceName.substring(p1 + 1);
707 }
708
709 /**
710 * Extract the entry name from a fully-donated resource name.
711 * e.g. "com.android.app1:drawable/icon1" -> "icon1"
712 * @hide
713 */
714 @VisibleForTesting
715 public static String getResourceEntryName(@NonNull String fullResourceName) {
716 final int p1 = fullResourceName.indexOf('/');
717 if (p1 < 0) {
718 return null;
719 }
720 return fullResourceName.substring(p1 + 1);
721 }
722
723 /**
724 * Return the resource ID for a given resource ID.
725 *
726 * Basically its' a wrapper over {@link Resources#getIdentifier(String, String, String)}, except
727 * if {@code resourceName} is an integer then it'll just return its value. (Which also the
728 * aforementioned method would do internally, but not documented, so doing here explicitly.)
729 *
730 * @param res {@link Resources} for the publisher. Must have been loaded with
731 * {@link PackageManager#getResourcesForApplicationAsUser}.
732 *
733 * @hide
734 */
735 @VisibleForTesting
736 public static int lookUpResourceId(@NonNull Resources res, @Nullable String resourceName,
737 @Nullable String resourceType, String packageName) {
738 if (resourceName == null) {
739 return 0;
740 }
741 try {
742 try {
743 // It the name can be parsed as an integer, just use it.
744 return Integer.parseInt(resourceName);
745 } catch (NumberFormatException ignore) {
746 }
747
748 return res.getIdentifier(resourceName, resourceType, packageName);
749 } catch (NotFoundException e) {
750 Log.e(TAG, "Resource ID for name=" + resourceName + " not found in package "
751 + packageName);
752 return 0;
753 }
754 }
755
756 /**
757 * Look up resource names from the resource IDs for the icon res and the text fields, and fill
758 * in the resource name fields.
759 *
760 * @param res {@link Resources} for the publisher. Must have been loaded with
761 * {@link PackageManager#getResourcesForApplicationAsUser}.
762 *
763 * @hide
764 */
765 public void lookupAndFillInResourceNames(@NonNull Resources res) {
766 if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)
767 && (mIconResId == 0)) {
768 return; // Bail early.
769 }
770
771 // We don't need types for strings because their types are always "string".
772 mTitleResName = lookUpResourceName(res, mTitleResId, /*withType=*/ false, mPackageName);
773 mTextResName = lookUpResourceName(res, mTextResId, /*withType=*/ false, mPackageName);
774 mDisabledMessageResName = lookUpResourceName(res, mDisabledMessageResId,
775 /*withType=*/ false, mPackageName);
776
777 // But icons have multiple possible types, so include the type.
778 mIconResName = lookUpResourceName(res, mIconResId, /*withType=*/ true, mPackageName);
779 }
780
781 /**
782 * Look up resource IDs from the resource names for the icon res and the text fields, and fill
783 * in the resource ID fields.
784 *
785 * This is called when an app is updated.
786 *
787 * @hide
788 */
789 public void lookupAndFillInResourceIds(@NonNull Resources res) {
790 if ((mTitleResName == null) && (mTextResName == null) && (mDisabledMessageResName == null)
791 && (mIconResName == null)) {
792 return; // Bail early.
793 }
794
795 mTitleResId = lookUpResourceId(res, mTitleResName, RES_TYPE_STRING, mPackageName);
796 mTextResId = lookUpResourceId(res, mTextResName, RES_TYPE_STRING, mPackageName);
797 mDisabledMessageResId = lookUpResourceId(res, mDisabledMessageResName, RES_TYPE_STRING,
798 mPackageName);
799
800 // mIconResName already contains the type, so the third argument is not needed.
801 mIconResId = lookUpResourceId(res, mIconResName, null, mPackageName);
802 }
803
804 /**
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800805 * Copy a {@link ShortcutInfo}, optionally removing fields.
806 * @hide
807 */
808 public ShortcutInfo clone(@CloneFlags int cloneFlags) {
809 return new ShortcutInfo(this, cloneFlags);
810 }
811
812 /**
Makoto Onuki22fcc682016-05-17 14:52:19 -0700813 * @hide
Makoto Onukia4f89b12017-10-05 10:37:55 -0700814 *
815 * @isUpdating set true if it's "update", as opposed to "replace".
Makoto Onuki22fcc682016-05-17 14:52:19 -0700816 */
Makoto Onukia4f89b12017-10-05 10:37:55 -0700817 public void ensureUpdatableWith(ShortcutInfo source, boolean isUpdating) {
818 if (isUpdating) {
819 Preconditions.checkState(isVisibleToPublisher(),
820 "[Framework BUG] Invisible shortcuts can't be updated");
821 }
Makoto Onuki22fcc682016-05-17 14:52:19 -0700822 Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
823 Preconditions.checkState(mId.equals(source.mId), "ID must match");
824 Preconditions.checkState(mPackageName.equals(source.mPackageName),
825 "Package name must match");
Makoto Onukia4f89b12017-10-05 10:37:55 -0700826
827 if (isVisibleToPublisher()) {
828 // Don't do this check for restore-blocked shortcuts.
829 Preconditions.checkState(!isImmutable(), "Target ShortcutInfo is immutable");
830 }
Makoto Onuki22fcc682016-05-17 14:52:19 -0700831 }
832
833 /**
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800834 * Copy non-null/zero fields from another {@link ShortcutInfo}. Only "public" information
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700835 * will be overwritten. The timestamp will *not* be updated to be consistent with other
836 * setters (and also the clock is not injectable in this file).
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800837 *
838 * - Flags will not change
839 * - mBitmapPath will not change
840 * - Current time will be set to timestamp
841 *
Makoto Onuki22fcc682016-05-17 14:52:19 -0700842 * @throws IllegalStateException if source is not compatible.
843 *
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800844 * @hide
845 */
846 public void copyNonNullFieldsFrom(ShortcutInfo source) {
Makoto Onukia4f89b12017-10-05 10:37:55 -0700847 ensureUpdatableWith(source, /*isUpdating=*/ true);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800848
Makoto Onuki22fcc682016-05-17 14:52:19 -0700849 if (source.mActivity != null) {
850 mActivity = source.mActivity;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800851 }
852
853 if (source.mIcon != null) {
854 mIcon = source.mIcon;
Makoto Onuki157b1622016-06-02 16:13:10 -0700855
856 mIconResId = 0;
857 mIconResName = null;
858 mBitmapPath = null;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800859 }
860 if (source.mTitle != null) {
861 mTitle = source.mTitle;
Makoto Onuki20c95f82016-05-11 16:51:01 -0700862 mTitleResId = 0;
Makoto Onuki157b1622016-06-02 16:13:10 -0700863 mTitleResName = null;
Makoto Onuki20c95f82016-05-11 16:51:01 -0700864 } else if (source.mTitleResId != 0) {
865 mTitle = null;
Makoto Onukieddbfec2016-05-31 17:04:34 -0700866 mTitleResId = source.mTitleResId;
Makoto Onuki157b1622016-06-02 16:13:10 -0700867 mTitleResName = null;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800868 }
Makoto Onuki157b1622016-06-02 16:13:10 -0700869
Makoto Onukie3ae7ec2016-03-29 15:45:25 -0700870 if (source.mText != null) {
871 mText = source.mText;
Makoto Onuki20c95f82016-05-11 16:51:01 -0700872 mTextResId = 0;
Makoto Onuki157b1622016-06-02 16:13:10 -0700873 mTextResName = null;
Makoto Onuki20c95f82016-05-11 16:51:01 -0700874 } else if (source.mTextResId != 0) {
875 mText = null;
876 mTextResId = source.mTextResId;
Makoto Onuki157b1622016-06-02 16:13:10 -0700877 mTextResName = null;
Makoto Onuki20c95f82016-05-11 16:51:01 -0700878 }
879 if (source.mDisabledMessage != null) {
880 mDisabledMessage = source.mDisabledMessage;
881 mDisabledMessageResId = 0;
Makoto Onuki157b1622016-06-02 16:13:10 -0700882 mDisabledMessageResName = null;
Makoto Onuki20c95f82016-05-11 16:51:01 -0700883 } else if (source.mDisabledMessageResId != 0) {
884 mDisabledMessage = null;
885 mDisabledMessageResId = source.mDisabledMessageResId;
Makoto Onuki157b1622016-06-02 16:13:10 -0700886 mDisabledMessageResName = null;
Makoto Onukie3ae7ec2016-03-29 15:45:25 -0700887 }
Makoto Onukib6d35232016-04-04 15:57:17 -0700888 if (source.mCategories != null) {
Makoto Onukidf6da042016-06-16 09:51:40 -0700889 mCategories = cloneCategories(source.mCategories);
Makoto Onukib6d35232016-04-04 15:57:17 -0700890 }
Mehdi Alizadeh14242af2018-12-20 20:11:35 -0800891 if (source.mPersons != null) {
892 mPersons = clonePersons(source.mPersons);
893 }
Makoto Onuki440a1ea2016-07-20 14:21:18 -0700894 if (source.mIntents != null) {
895 mIntents = cloneIntents(source.mIntents);
896 mIntentPersistableExtrases =
897 clonePersistableBundle(source.mIntentPersistableExtrases);
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800898 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700899 if (source.mRank != RANK_NOT_SET) {
Makoto Onuki20c95f82016-05-11 16:51:01 -0700900 mRank = source.mRank;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800901 }
902 if (source.mExtras != null) {
903 mExtras = source.mExtras;
904 }
Felipe Leme90205ef2019-03-05 09:59:52 -0800905
906 if (source.mLocusId != null) {
907 mLocusId = source.mLocusId;
908 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800909 }
910
911 /**
Makoto Onuki55046222016-03-08 10:49:47 -0800912 * @hide
913 */
914 public static Icon validateIcon(Icon icon) {
915 switch (icon.getType()) {
916 case Icon.TYPE_RESOURCE:
917 case Icon.TYPE_BITMAP:
Hyunyoung Songe4179e22017-03-01 12:51:26 -0800918 case Icon.TYPE_ADAPTIVE_BITMAP:
Makoto Onuki55046222016-03-08 10:49:47 -0800919 break; // OK
Makoto Onuki55046222016-03-08 10:49:47 -0800920 default:
921 throw getInvalidIconException();
922 }
923 if (icon.hasTint()) {
Makoto Onuki55046222016-03-08 10:49:47 -0800924 throw new IllegalArgumentException("Icons with tints are not supported");
925 }
926
927 return icon;
928 }
929
930 /** @hide */
931 public static IllegalArgumentException getInvalidIconException() {
932 return new IllegalArgumentException("Unsupported icon type:"
Makoto Onukia97256b2016-07-15 13:23:54 -0700933 +" only the bitmap and resource types are supported");
Makoto Onuki55046222016-03-08 10:49:47 -0800934 }
935
936 /**
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800937 * Builder class for {@link ShortcutInfo} objects.
Makoto Onuki4a910962016-07-07 13:57:34 -0700938 *
939 * @see ShortcutManager
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800940 */
941 public static class Builder {
942 private final Context mContext;
943
944 private String mId;
945
Makoto Onuki22fcc682016-05-17 14:52:19 -0700946 private ComponentName mActivity;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800947
948 private Icon mIcon;
949
Makoto Onuki20c95f82016-05-11 16:51:01 -0700950 private int mTitleResId;
951
Makoto Onuki22fcc682016-05-17 14:52:19 -0700952 private CharSequence mTitle;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800953
Makoto Onuki20c95f82016-05-11 16:51:01 -0700954 private int mTextResId;
955
Makoto Onuki22fcc682016-05-17 14:52:19 -0700956 private CharSequence mText;
Makoto Onukie3ae7ec2016-03-29 15:45:25 -0700957
Makoto Onuki20c95f82016-05-11 16:51:01 -0700958 private int mDisabledMessageResId;
959
Makoto Onuki22fcc682016-05-17 14:52:19 -0700960 private CharSequence mDisabledMessage;
Makoto Onuki20c95f82016-05-11 16:51:01 -0700961
Makoto Onukibe73a802016-04-15 14:46:35 -0700962 private Set<String> mCategories;
Makoto Onukib6d35232016-04-04 15:57:17 -0700963
Makoto Onuki440a1ea2016-07-20 14:21:18 -0700964 private Intent[] mIntents;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800965
Mehdi Alizadeh14242af2018-12-20 20:11:35 -0800966 private Person[] mPersons;
967
968 private boolean mIsLongLived;
969
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700970 private int mRank = RANK_NOT_SET;
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800971
972 private PersistableBundle mExtras;
973
Felipe Leme90205ef2019-03-05 09:59:52 -0800974 private LocusId mLocusId;
975
Makoto Onukib5a012f2016-06-21 11:13:53 -0700976 /**
Makoto Onuki598aca42016-07-06 16:05:03 -0700977 * Old style constructor.
978 * @hide
Makoto Onukib5a012f2016-06-21 11:13:53 -0700979 */
980 @Deprecated
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800981 public Builder(Context context) {
982 mContext = context;
983 }
984
985 /**
Makoto Onuki598aca42016-07-06 16:05:03 -0700986 * Used with the old style constructor, kept for unit tests.
987 * @hide
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800988 */
989 @NonNull
Makoto Onukib5a012f2016-06-21 11:13:53 -0700990 @Deprecated
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800991 public Builder setId(@NonNull String id) {
Makoto Onukib08790c2016-06-23 14:05:46 -0700992 mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty");
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800993 return this;
994 }
995
996 /**
Makoto Onukib5a012f2016-06-21 11:13:53 -0700997 * Constructor.
998 *
999 * @param context Client context.
1000 * @param id ID of the shortcut.
1001 */
1002 public Builder(Context context, String id) {
1003 mContext = context;
Makoto Onukib08790c2016-06-23 14:05:46 -07001004 mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty");
Makoto Onukib5a012f2016-06-21 11:13:53 -07001005 }
1006
1007 /**
Felipe Leme90205ef2019-03-05 09:59:52 -08001008 * Sets the {@link LocusId} associated with this shortcut.
1009 *
1010 * <p>This method should be called when the {@link LocusId} is used in other places (such
1011 * as {@link Notification} and {@link ContentCaptureContext}) so the device's intelligence
1012 * services can correlate them.
1013 */
1014 @NonNull
1015 public Builder setLocusId(@NonNull LocusId locusId) {
Daulet Zhanguzina2044e12019-12-30 16:34:59 +00001016 mLocusId = Objects.requireNonNull(locusId, "locusId cannot be null");
Felipe Leme90205ef2019-03-05 09:59:52 -08001017 return this;
1018 }
1019
1020 /**
Makoto Onuki4a910962016-07-07 13:57:34 -07001021 * Sets the target activity. A shortcut will be shown along with this activity's icon
1022 * on the launcher.
Makoto Onukib5a012f2016-06-21 11:13:53 -07001023 *
Makoto Onukife9c9662016-07-25 15:12:23 -07001024 * When selecting a target activity, keep the following in mind:
Makoto Onuki4a910962016-07-07 13:57:34 -07001025 * <ul>
Makoto Onukife9c9662016-07-25 15:12:23 -07001026 * <li>All dynamic shortcuts must have a target activity. When a shortcut with no target
1027 * activity is published using
1028 * {@link ShortcutManager#addDynamicShortcuts(List)} or
1029 * {@link ShortcutManager#setDynamicShortcuts(List)},
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001030 * the first main activity defined in the app's <code>AndroidManifest.xml</code>
Makoto Onukife9c9662016-07-25 15:12:23 -07001031 * file is used.
1032 *
1033 * <li>Only "main" activities&mdash;ones that define the {@link Intent#ACTION_MAIN}
1034 * and {@link Intent#CATEGORY_LAUNCHER} intent filters&mdash;can be target
Makoto Onuki4a910962016-07-07 13:57:34 -07001035 * activities.
1036 *
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001037 * <li>By default, the first main activity defined in the app's manifest is
Makoto Onukife9c9662016-07-25 15:12:23 -07001038 * the target activity.
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001039 *
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001040 * <li>A target activity must belong to the publisher app.
Makoto Onuki4a910962016-07-07 13:57:34 -07001041 * </ul>
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001042 *
Makoto Onuki4a910962016-07-07 13:57:34 -07001043 * @see ShortcutInfo#getActivity()
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001044 */
1045 @NonNull
Makoto Onuki22fcc682016-05-17 14:52:19 -07001046 public Builder setActivity(@NonNull ComponentName activity) {
Daulet Zhanguzina2044e12019-12-30 16:34:59 +00001047 mActivity = Objects.requireNonNull(activity, "activity cannot be null");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001048 return this;
1049 }
1050
1051 /**
Makoto Onuki4a910962016-07-07 13:57:34 -07001052 * Sets an icon of a shortcut.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001053 *
Makoto Onuki4a910962016-07-07 13:57:34 -07001054 * <p>Icons are not available on {@link ShortcutInfo} instances
1055 * returned by {@link ShortcutManager} or {@link LauncherApps}. The default launcher
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001056 * app can use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}
Makoto Onukib5a012f2016-06-21 11:13:53 -07001057 * or {@link LauncherApps#getShortcutBadgedIconDrawable(ShortcutInfo, int)} to fetch
1058 * shortcut icons.
Makoto Onuki4a910962016-07-07 13:57:34 -07001059 *
1060 * <p>Tints set with {@link Icon#setTint} or {@link Icon#setTintList} are not supported
1061 * and will be ignored.
1062 *
Hyunyoung Songf281e7a2017-02-13 10:57:42 -08001063 * <p>Only icons created with {@link Icon#createWithBitmap(Bitmap)},
Hyunyoung Songe4179e22017-03-01 12:51:26 -08001064 * {@link Icon#createWithAdaptiveBitmap(Bitmap)}
Hyunyoung Songf281e7a2017-02-13 10:57:42 -08001065 * and {@link Icon#createWithResource} are supported.
Makoto Onukife9c9662016-07-25 15:12:23 -07001066 * Other types, such as URI-based icons, are not supported.
Makoto Onuki4a910962016-07-07 13:57:34 -07001067 *
1068 * @see LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)
1069 * @see LauncherApps#getShortcutBadgedIconDrawable(ShortcutInfo, int)
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001070 */
1071 @NonNull
1072 public Builder setIcon(Icon icon) {
Makoto Onuki55046222016-03-08 10:49:47 -08001073 mIcon = validateIcon(icon);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001074 return this;
1075 }
1076
Makoto Onukieddbfec2016-05-31 17:04:34 -07001077 /**
1078 * @hide We don't support resource strings for dynamic shortcuts for now. (But unit tests
1079 * use it.)
1080 */
Makoto Onukib5a012f2016-06-21 11:13:53 -07001081 @Deprecated
Makoto Onukieddbfec2016-05-31 17:04:34 -07001082 public Builder setShortLabelResId(int shortLabelResId) {
1083 Preconditions.checkState(mTitle == null, "shortLabel already set");
1084 mTitleResId = shortLabelResId;
Makoto Onuki20c95f82016-05-11 16:51:01 -07001085 return this;
1086 }
1087
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001088 /**
Makoto Onukib5a012f2016-06-21 11:13:53 -07001089 * Sets the short title of a shortcut.
1090 *
Makoto Onuki4a910962016-07-07 13:57:34 -07001091 * <p>This is a mandatory field when publishing a new shortcut with
1092 * {@link ShortcutManager#addDynamicShortcuts(List)} or
1093 * {@link ShortcutManager#setDynamicShortcuts(List)}.
Makoto Onuki0e65d362016-03-29 14:46:50 -07001094 *
Makoto Onukife9c9662016-07-25 15:12:23 -07001095 * <p>This field is intended to be a concise description of a shortcut.
Makoto Onuki4a910962016-07-07 13:57:34 -07001096 *
Makoto Onukife9c9662016-07-25 15:12:23 -07001097 * <p>The recommended maximum length is 10 characters.
Makoto Onuki4a910962016-07-07 13:57:34 -07001098 *
1099 * @see ShortcutInfo#getShortLabel()
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001100 */
1101 @NonNull
Makoto Onukidf6da042016-06-16 09:51:40 -07001102 public Builder setShortLabel(@NonNull CharSequence shortLabel) {
Makoto Onukieddbfec2016-05-31 17:04:34 -07001103 Preconditions.checkState(mTitleResId == 0, "shortLabelResId already set");
Makoto Onukib08790c2016-06-23 14:05:46 -07001104 mTitle = Preconditions.checkStringNotEmpty(shortLabel, "shortLabel cannot be empty");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001105 return this;
1106 }
1107
Makoto Onukieddbfec2016-05-31 17:04:34 -07001108 /**
1109 * @hide We don't support resource strings for dynamic shortcuts for now. (But unit tests
1110 * use it.)
1111 */
Makoto Onukib5a012f2016-06-21 11:13:53 -07001112 @Deprecated
Makoto Onukieddbfec2016-05-31 17:04:34 -07001113 public Builder setLongLabelResId(int longLabelResId) {
1114 Preconditions.checkState(mText == null, "longLabel already set");
1115 mTextResId = longLabelResId;
Makoto Onuki20c95f82016-05-11 16:51:01 -07001116 return this;
1117 }
1118
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001119 /**
Makoto Onukib5a012f2016-06-21 11:13:53 -07001120 * Sets the text of a shortcut.
Makoto Onuki0e65d362016-03-29 14:46:50 -07001121 *
Makoto Onukieddbfec2016-05-31 17:04:34 -07001122 * <p>This field is intended to be more descriptive than the shortcut title. The launcher
Makoto Onuki4a910962016-07-07 13:57:34 -07001123 * shows this instead of the short title when it has enough space.
1124 *
Makoto Onukife9c9662016-07-25 15:12:23 -07001125 * <p>The recommend maximum length is 25 characters.
Makoto Onuki4a910962016-07-07 13:57:34 -07001126 *
1127 * @see ShortcutInfo#getLongLabel()
Makoto Onukie3ae7ec2016-03-29 15:45:25 -07001128 */
1129 @NonNull
Makoto Onukidf6da042016-06-16 09:51:40 -07001130 public Builder setLongLabel(@NonNull CharSequence longLabel) {
Makoto Onukieddbfec2016-05-31 17:04:34 -07001131 Preconditions.checkState(mTextResId == 0, "longLabelResId already set");
Makoto Onukib08790c2016-06-23 14:05:46 -07001132 mText = Preconditions.checkStringNotEmpty(longLabel, "longLabel cannot be empty");
Makoto Onukie3ae7ec2016-03-29 15:45:25 -07001133 return this;
1134 }
1135
Makoto Onukieddbfec2016-05-31 17:04:34 -07001136 /** @hide -- old signature, the internal code still uses it. */
Makoto Onukib5a012f2016-06-21 11:13:53 -07001137 @Deprecated
Makoto Onukidf6da042016-06-16 09:51:40 -07001138 public Builder setTitle(@NonNull CharSequence value) {
Makoto Onukieddbfec2016-05-31 17:04:34 -07001139 return setShortLabel(value);
1140 }
1141
1142 /** @hide -- old signature, the internal code still uses it. */
Makoto Onukib5a012f2016-06-21 11:13:53 -07001143 @Deprecated
Makoto Onukieddbfec2016-05-31 17:04:34 -07001144 public Builder setTitleResId(int value) {
1145 return setShortLabelResId(value);
1146 }
1147
1148 /** @hide -- old signature, the internal code still uses it. */
Makoto Onukib5a012f2016-06-21 11:13:53 -07001149 @Deprecated
Makoto Onukidf6da042016-06-16 09:51:40 -07001150 public Builder setText(@NonNull CharSequence value) {
Makoto Onukieddbfec2016-05-31 17:04:34 -07001151 return setLongLabel(value);
1152 }
1153
1154 /** @hide -- old signature, the internal code still uses it. */
Makoto Onukib5a012f2016-06-21 11:13:53 -07001155 @Deprecated
Makoto Onukieddbfec2016-05-31 17:04:34 -07001156 public Builder setTextResId(int value) {
1157 return setLongLabelResId(value);
1158 }
1159
1160 /**
1161 * @hide We don't support resource strings for dynamic shortcuts for now. (But unit tests
1162 * use it.)
1163 */
Makoto Onukib5a012f2016-06-21 11:13:53 -07001164 @Deprecated
Makoto Onuki20c95f82016-05-11 16:51:01 -07001165 public Builder setDisabledMessageResId(int disabledMessageResId) {
1166 Preconditions.checkState(mDisabledMessage == null, "disabledMessage already set");
1167 mDisabledMessageResId = disabledMessageResId;
1168 return this;
1169 }
1170
Makoto Onuki4a910962016-07-07 13:57:34 -07001171 /**
Makoto Onukife9c9662016-07-25 15:12:23 -07001172 * Sets the message that should be shown when the user attempts to start a shortcut that
1173 * is disabled.
Makoto Onuki4a910962016-07-07 13:57:34 -07001174 *
1175 * @see ShortcutInfo#getDisabledMessage()
1176 */
Makoto Onuki20c95f82016-05-11 16:51:01 -07001177 @NonNull
Makoto Onukidf6da042016-06-16 09:51:40 -07001178 public Builder setDisabledMessage(@NonNull CharSequence disabledMessage) {
Makoto Onuki20c95f82016-05-11 16:51:01 -07001179 Preconditions.checkState(
1180 mDisabledMessageResId == 0, "disabledMessageResId already set");
1181 mDisabledMessage =
Makoto Onukib08790c2016-06-23 14:05:46 -07001182 Preconditions.checkStringNotEmpty(disabledMessage,
1183 "disabledMessage cannot be empty");
Makoto Onuki20c95f82016-05-11 16:51:01 -07001184 return this;
1185 }
1186
Makoto Onukie3ae7ec2016-03-29 15:45:25 -07001187 /**
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001188 * Sets categories for a shortcut. Launcher apps may use this information to
1189 * categorize shortcuts.
Makoto Onukib6d35232016-04-04 15:57:17 -07001190 *
1191 * @see #SHORTCUT_CATEGORY_CONVERSATION
Makoto Onuki4a910962016-07-07 13:57:34 -07001192 * @see ShortcutInfo#getCategories()
Makoto Onukib6d35232016-04-04 15:57:17 -07001193 */
1194 @NonNull
Makoto Onukibe73a802016-04-15 14:46:35 -07001195 public Builder setCategories(Set<String> categories) {
Makoto Onukib6d35232016-04-04 15:57:17 -07001196 mCategories = categories;
1197 return this;
1198 }
1199
1200 /**
Makoto Onuki0eed4412016-07-21 11:21:59 -07001201 * Sets the intent of a shortcut. Alternatively, {@link #setIntents(Intent[])} can be used
1202 * to launch an activity with other activities in the back stack.
Makoto Onuki4a910962016-07-07 13:57:34 -07001203 *
1204 * <p>This is a mandatory field when publishing a new shortcut with
1205 * {@link ShortcutManager#addDynamicShortcuts(List)} or
1206 * {@link ShortcutManager#setDynamicShortcuts(List)}.
1207 *
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001208 * <p>A shortcut can launch any intent that the publisher app has permission to
Makoto Onukife9c9662016-07-25 15:12:23 -07001209 * launch. For example, a shortcut can launch an unexported activity within the publisher
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001210 * app. A shortcut intent doesn't have to point at the target activity.
Makoto Onuki4a910962016-07-07 13:57:34 -07001211 *
Makoto Onukife9c9662016-07-25 15:12:23 -07001212 * <p>The given {@code intent} can contain extras, but these extras must contain values
1213 * of primitive types in order for the system to persist these values.
Makoto Onuki4a910962016-07-07 13:57:34 -07001214 *
1215 * @see ShortcutInfo#getIntent()
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001216 * @see #setIntents(Intent[])
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001217 */
1218 @NonNull
1219 public Builder setIntent(@NonNull Intent intent) {
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001220 return setIntents(new Intent[]{intent});
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001221 }
1222
1223 /**
Makoto Onuki0eed4412016-07-21 11:21:59 -07001224 * Sets multiple intents instead of a single intent, in order to launch an activity with
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001225 * other activities in back stack. Use {@link TaskStackBuilder} to build intents. The
1226 * last element in the list represents the only intent that doesn't place an activity on
1227 * the back stack.
Makoto Onuki0eed4412016-07-21 11:21:59 -07001228 * See the {@link ShortcutManager} javadoc for details.
Makoto Onuki347a6bd2016-07-19 11:13:08 -07001229 *
1230 * @see Builder#setIntent(Intent)
1231 * @see ShortcutInfo#getIntents()
1232 * @see Context#startActivities(Intent[])
1233 * @see TaskStackBuilder
1234 */
1235 @NonNull
1236 public Builder setIntents(@NonNull Intent[] intents) {
Daulet Zhanguzina2044e12019-12-30 16:34:59 +00001237 Objects.requireNonNull(intents, "intents cannot be null");
1238 Objects.requireNonNull(intents.length, "intents cannot be empty");
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001239 for (Intent intent : intents) {
Daulet Zhanguzina2044e12019-12-30 16:34:59 +00001240 Objects.requireNonNull(intent, "intents cannot contain null");
1241 Objects.requireNonNull(intent.getAction(), "intent's action must be set");
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001242 }
1243 // Make sure always clone incoming intents.
1244 mIntents = cloneIntents(intents);
1245 return this;
Makoto Onuki347a6bd2016-07-19 11:13:08 -07001246 }
1247
1248 /**
Mehdi Alizadeh14242af2018-12-20 20:11:35 -08001249 * Add a person that is relevant to this shortcut. Alternatively,
1250 * {@link #setPersons(Person[])} can be used to add multiple persons to a shortcut.
1251 *
1252 * <p> This is an optional field, but the addition of person may cause this shortcut to
1253 * appear more prominently in the user interface (e.g. ShareSheet).
1254 *
1255 * <p> A person should usually contain a uri in order to benefit from the ranking boost.
1256 * However, even if no uri is provided, it's beneficial to provide people in the shortcut,
1257 * such that listeners and voice only devices can announce and handle them properly.
1258 *
1259 * @see Person
1260 * @see #setPersons(Person[])
1261 */
1262 @NonNull
1263 public Builder setPerson(@NonNull Person person) {
1264 return setPersons(new Person[]{person});
1265 }
1266
1267 /**
1268 * Sets multiple persons instead of a single person.
1269 *
1270 * @see Person
1271 * @see #setPerson(Person)
1272 */
1273 @NonNull
1274 public Builder setPersons(@NonNull Person[] persons) {
Daulet Zhanguzina2044e12019-12-30 16:34:59 +00001275 Objects.requireNonNull(persons, "persons cannot be null");
1276 Objects.requireNonNull(persons.length, "persons cannot be empty");
Mehdi Alizadeh14242af2018-12-20 20:11:35 -08001277 for (Person person : persons) {
Daulet Zhanguzina2044e12019-12-30 16:34:59 +00001278 Objects.requireNonNull(person, "persons cannot contain null");
Mehdi Alizadeh14242af2018-12-20 20:11:35 -08001279 }
1280 mPersons = clonePersons(persons);
1281 return this;
1282 }
1283
1284 /**
1285 * Sets if a shortcut would be valid even if it has been unpublished/invisible by the app
1286 * (as a dynamic or pinned shortcut). If it is long lived, it can be cached by various
1287 * system services even after it has been unpublished as a dynamic shortcut.
1288 */
1289 @NonNull
Mehdi Alizadeh3d2ff4c2019-03-13 15:45:03 -07001290 public Builder setLongLived(boolean londLived) {
1291 mIsLongLived = londLived;
Mehdi Alizadeh14242af2018-12-20 20:11:35 -08001292 return this;
1293 }
1294
1295 /**
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001296 * "Rank" of a shortcut, which is a non-negative value that's used by the launcher app
1297 * to sort shortcuts.
Makoto Onuki4a910962016-07-07 13:57:34 -07001298 *
1299 * See {@link ShortcutInfo#getRank()} for details.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001300 */
1301 @NonNull
Makoto Onuki20c95f82016-05-11 16:51:01 -07001302 public Builder setRank(int rank) {
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001303 Preconditions.checkArgument((0 <= rank),
1304 "Rank cannot be negative or bigger than MAX_RANK");
Makoto Onuki20c95f82016-05-11 16:51:01 -07001305 mRank = rank;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001306 return this;
1307 }
1308
1309 /**
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001310 * Extras that the app can set for any purpose.
Makoto Onukib5a012f2016-06-21 11:13:53 -07001311 *
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001312 * <p>Apps can store arbitrary shortcut metadata in extras and retrieve the
Makoto Onukife9c9662016-07-25 15:12:23 -07001313 * metadata later using {@link ShortcutInfo#getExtras()}.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001314 */
1315 @NonNull
1316 public Builder setExtras(@NonNull PersistableBundle extras) {
1317 mExtras = extras;
1318 return this;
1319 }
1320
Hakan Seyalioglu58fc95d2016-12-13 15:23:22 -08001321 /**
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001322 * Creates a {@link ShortcutInfo} instance.
1323 */
1324 @NonNull
1325 public ShortcutInfo build() {
1326 return new ShortcutInfo(this);
1327 }
1328 }
1329
1330 /**
Makoto Onuki4a910962016-07-07 13:57:34 -07001331 * Returns the ID of a shortcut.
1332 *
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001333 * <p>Shortcut IDs are unique within each publisher app and must be stable across
Makoto Onukife9c9662016-07-25 15:12:23 -07001334 * devices so that shortcuts will still be valid when restored on a different device.
1335 * See {@link ShortcutManager} for details.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001336 */
1337 @NonNull
1338 public String getId() {
1339 return mId;
1340 }
1341
1342 /**
Felipe Leme90205ef2019-03-05 09:59:52 -08001343 * Gets the {@link LocusId} associated with this shortcut.
1344 *
1345 * <p>Used by the device's intelligence services to correlate objects (such as
1346 * {@link Notification} and {@link ContentCaptureContext}) that are correlated.
1347 */
1348 @Nullable
1349 public LocusId getLocusId() {
1350 return mLocusId;
1351 }
1352
1353 /**
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001354 * Return the package name of the publisher app.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001355 */
1356 @NonNull
Makoto Onuki22fcc682016-05-17 14:52:19 -07001357 public String getPackage() {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001358 return mPackageName;
1359 }
1360
1361 /**
Makoto Onuki4a910962016-07-07 13:57:34 -07001362 * Return the target activity.
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001363 *
Makoto Onukife9c9662016-07-25 15:12:23 -07001364 * <p>This has nothing to do with the activity that this shortcut will launch.
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001365 * Launcher apps should show the launcher icon for the returned activity alongside
Makoto Onukife9c9662016-07-25 15:12:23 -07001366 * this shortcut.
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001367 *
Makoto Onuki22fcc682016-05-17 14:52:19 -07001368 * @see Builder#setActivity
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001369 */
1370 @Nullable
Makoto Onuki22fcc682016-05-17 14:52:19 -07001371 public ComponentName getActivity() {
1372 return mActivity;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001373 }
1374
Makoto Onukib08790c2016-06-23 14:05:46 -07001375 /** @hide */
1376 public void setActivity(ComponentName activity) {
1377 mActivity = activity;
1378 }
1379
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001380 /**
Makoto Onuki4a910962016-07-07 13:57:34 -07001381 * Returns the shortcut icon.
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001382 *
1383 * @hide
1384 */
1385 @Nullable
Mathew Inwood8c854f82018-09-14 12:35:36 +01001386 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001387 public Icon getIcon() {
1388 return mIcon;
1389 }
1390
Makoto Onukieddbfec2016-05-31 17:04:34 -07001391 /** @hide -- old signature, the internal code still uses it. */
Makoto Onuki55046222016-03-08 10:49:47 -08001392 @Nullable
Makoto Onukib5a012f2016-06-21 11:13:53 -07001393 @Deprecated
Makoto Onuki22fcc682016-05-17 14:52:19 -07001394 public CharSequence getTitle() {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001395 return mTitle;
1396 }
1397
Makoto Onukieddbfec2016-05-31 17:04:34 -07001398 /** @hide -- old signature, the internal code still uses it. */
Makoto Onukib5a012f2016-06-21 11:13:53 -07001399 @Deprecated
Makoto Onuki20c95f82016-05-11 16:51:01 -07001400 public int getTitleResId() {
1401 return mTitleResId;
1402 }
1403
Makoto Onukieddbfec2016-05-31 17:04:34 -07001404 /** @hide -- old signature, the internal code still uses it. */
1405 @Nullable
Makoto Onukib5a012f2016-06-21 11:13:53 -07001406 @Deprecated
Makoto Onukieddbfec2016-05-31 17:04:34 -07001407 public CharSequence getText() {
1408 return mText;
1409 }
1410
1411 /** @hide -- old signature, the internal code still uses it. */
Makoto Onukib5a012f2016-06-21 11:13:53 -07001412 @Deprecated
Makoto Onukieddbfec2016-05-31 17:04:34 -07001413 public int getTextResId() {
1414 return mTextResId;
1415 }
1416
1417 /**
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001418 * Return the short description of a shortcut.
Makoto Onukieddbfec2016-05-31 17:04:34 -07001419 *
Makoto Onuki4a910962016-07-07 13:57:34 -07001420 * @see Builder#setShortLabel(CharSequence)
Makoto Onukieddbfec2016-05-31 17:04:34 -07001421 */
1422 @Nullable
1423 public CharSequence getShortLabel() {
1424 return mTitle;
1425 }
1426
Makoto Onuki598aca42016-07-06 16:05:03 -07001427 /** @hide */
Makoto Onukieddbfec2016-05-31 17:04:34 -07001428 public int getShortLabelResourceId() {
1429 return mTitleResId;
1430 }
1431
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001432 /**
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001433 * Return the long description of a shortcut.
Makoto Onuki4a910962016-07-07 13:57:34 -07001434 *
1435 * @see Builder#setLongLabel(CharSequence)
Makoto Onukie3ae7ec2016-03-29 15:45:25 -07001436 */
1437 @Nullable
Makoto Onukieddbfec2016-05-31 17:04:34 -07001438 public CharSequence getLongLabel() {
Makoto Onukie3ae7ec2016-03-29 15:45:25 -07001439 return mText;
1440 }
1441
Makoto Onuki598aca42016-07-06 16:05:03 -07001442 /** @hide */
Makoto Onukieddbfec2016-05-31 17:04:34 -07001443 public int getLongLabelResourceId() {
Makoto Onuki20c95f82016-05-11 16:51:01 -07001444 return mTextResId;
1445 }
1446
1447 /**
Makoto Onukife9c9662016-07-25 15:12:23 -07001448 * Return the message that should be shown when the user attempts to start a shortcut
1449 * that is disabled.
Makoto Onuki4a910962016-07-07 13:57:34 -07001450 *
1451 * @see Builder#setDisabledMessage(CharSequence)
Makoto Onuki20c95f82016-05-11 16:51:01 -07001452 */
1453 @Nullable
Makoto Onuki22fcc682016-05-17 14:52:19 -07001454 public CharSequence getDisabledMessage() {
Makoto Onuki20c95f82016-05-11 16:51:01 -07001455 return mDisabledMessage;
1456 }
1457
Makoto Onuki598aca42016-07-06 16:05:03 -07001458 /** @hide */
Makoto Onukieddbfec2016-05-31 17:04:34 -07001459 public int getDisabledMessageResourceId() {
Makoto Onuki20c95f82016-05-11 16:51:01 -07001460 return mDisabledMessageResId;
1461 }
1462
Makoto Onukia4f89b12017-10-05 10:37:55 -07001463 /** @hide */
1464 public void setDisabledReason(@DisabledReason int reason) {
1465 mDisabledReason = reason;
1466 }
1467
1468 /**
1469 * Returns why a shortcut has been disabled.
1470 */
1471 @DisabledReason
1472 public int getDisabledReason() {
1473 return mDisabledReason;
1474 }
1475
Makoto Onukie3ae7ec2016-03-29 15:45:25 -07001476 /**
Makoto Onukife9c9662016-07-25 15:12:23 -07001477 * Return the shortcut's categories.
Makoto Onuki4a910962016-07-07 13:57:34 -07001478 *
1479 * @see Builder#setCategories(Set)
Makoto Onukib6d35232016-04-04 15:57:17 -07001480 */
1481 @Nullable
Makoto Onukibe73a802016-04-15 14:46:35 -07001482 public Set<String> getCategories() {
Makoto Onukib6d35232016-04-04 15:57:17 -07001483 return mCategories;
1484 }
1485
1486 /**
Makoto Onukife9c9662016-07-25 15:12:23 -07001487 * Returns the intent that is executed when the user selects this shortcut.
1488 * If setIntents() was used, then return the last intent in the array.
Makoto Onuki55046222016-03-08 10:49:47 -08001489 *
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001490 * <p>Launcher apps <b>cannot</b> see the intent. If a {@link ShortcutInfo} is
Makoto Onuki4a910962016-07-07 13:57:34 -07001491 * obtained via {@link LauncherApps}, then this method will always return null.
1492 * Launchers can only start a shortcut intent with {@link LauncherApps#startShortcut}.
Makoto Onuki4d36b3a2016-04-27 12:00:17 -07001493 *
Makoto Onuki4a910962016-07-07 13:57:34 -07001494 * @see Builder#setIntent(Intent)
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001495 */
Makoto Onuki55046222016-03-08 10:49:47 -08001496 @Nullable
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001497 public Intent getIntent() {
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001498 if (mIntents == null || mIntents.length == 0) {
Makoto Onuki55046222016-03-08 10:49:47 -08001499 return null;
1500 }
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001501 final int last = mIntents.length - 1;
1502 final Intent intent = new Intent(mIntents[last]);
1503 return setIntentExtras(intent, mIntentPersistableExtrases[last]);
Makoto Onuki55046222016-03-08 10:49:47 -08001504 }
1505
1506 /**
Makoto Onuki347a6bd2016-07-19 11:13:08 -07001507 * Return the intent set with {@link Builder#setIntents(Intent[])}.
1508 *
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001509 * <p>Launcher apps <b>cannot</b> see the intents. If a {@link ShortcutInfo} is
Makoto Onuki347a6bd2016-07-19 11:13:08 -07001510 * obtained via {@link LauncherApps}, then this method will always return null.
1511 * Launchers can only start a shortcut intent with {@link LauncherApps#startShortcut}.
1512 *
1513 * @see Builder#setIntents(Intent[])
1514 */
1515 @Nullable
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001516 public Intent[] getIntents() {
1517 final Intent[] ret = new Intent[mIntents.length];
1518
1519 for (int i = 0; i < ret.length; i++) {
1520 ret[i] = new Intent(mIntents[i]);
1521 setIntentExtras(ret[i], mIntentPersistableExtrases[i]);
1522 }
1523
1524 return ret;
Makoto Onuki347a6bd2016-07-19 11:13:08 -07001525 }
1526
1527 /**
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001528 * Return "raw" intents, which is the original intents without the extras.
Makoto Onuki55046222016-03-08 10:49:47 -08001529 * @hide
1530 */
1531 @Nullable
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001532 public Intent[] getIntentsNoExtras() {
1533 return mIntents;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001534 }
1535
Makoto Onuki55046222016-03-08 10:49:47 -08001536 /**
Mehdi Alizadeh14242af2018-12-20 20:11:35 -08001537 * Return the Persons set with {@link Builder#setPersons(Person[])}.
1538 *
1539 * @hide
1540 */
1541 @Nullable
Mehdi Alizadeh88873652019-02-04 14:16:46 -08001542 @SystemApi
Mehdi Alizadeh14242af2018-12-20 20:11:35 -08001543 public Person[] getPersons() {
1544 return clonePersons(mPersons);
1545 }
1546
1547 /**
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001548 * The extras in the intents. We convert extras into {@link PersistableBundle} so we can
Makoto Onuki55046222016-03-08 10:49:47 -08001549 * persist them.
1550 * @hide
1551 */
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001552 @Nullable
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001553 public PersistableBundle[] getIntentPersistableExtrases() {
1554 return mIntentPersistableExtrases;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001555 }
1556
Hakan Seyalioglu58fc95d2016-12-13 15:23:22 -08001557 /**
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001558 * "Rank" of a shortcut, which is a non-negative, sequential value that's unique for each
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001559 * {@link #getActivity} for each of the two types of shortcuts (static and dynamic).
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001560 *
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001561 * <p>Because static shortcuts and dynamic shortcuts have overlapping ranks,
1562 * when a launcher app shows shortcuts for an activity, it should first show
1563 * the static shortcuts, followed by the dynamic shortcuts. Within each of those categories,
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001564 * shortcuts should be sorted by rank in ascending order.
1565 *
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001566 * <p><em>Floating shortcuts</em>, or shortcuts that are neither static nor dynamic, will all
1567 * have rank 0, because they aren't sorted.
Makoto Onuki4a910962016-07-07 13:57:34 -07001568 *
1569 * See the {@link ShortcutManager}'s class javadoc for details.
1570 *
1571 * @see Builder#setRank(int)
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001572 */
Makoto Onuki20c95f82016-05-11 16:51:01 -07001573 public int getRank() {
1574 return mRank;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001575 }
1576
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001577 /** @hide */
1578 public boolean hasRank() {
1579 return mRank != RANK_NOT_SET;
1580 }
1581
1582 /** @hide */
1583 public void setRank(int rank) {
1584 mRank = rank;
1585 }
1586
1587 /** @hide */
1588 public void clearImplicitRankAndRankChangedFlag() {
1589 mImplicitRank = 0;
1590 }
1591
1592 /** @hide */
1593 public void setImplicitRank(int rank) {
1594 // Make sure to keep RANK_CHANGED_BIT.
1595 mImplicitRank = (mImplicitRank & RANK_CHANGED_BIT) | (rank & IMPLICIT_RANK_MASK);
1596 }
1597
1598 /** @hide */
1599 public int getImplicitRank() {
1600 return mImplicitRank & IMPLICIT_RANK_MASK;
1601 }
1602
1603 /** @hide */
1604 public void setRankChanged() {
1605 mImplicitRank |= RANK_CHANGED_BIT;
1606 }
1607
1608 /** @hide */
1609 public boolean isRankChanged() {
1610 return (mImplicitRank & RANK_CHANGED_BIT) != 0;
1611 }
1612
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001613 /**
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001614 * Extras that the app can set for any purpose.
Makoto Onuki4a910962016-07-07 13:57:34 -07001615 *
1616 * @see Builder#setExtras(PersistableBundle)
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001617 */
1618 @Nullable
1619 public PersistableBundle getExtras() {
1620 return mExtras;
1621 }
1622
Makoto Onukiabe84422016-04-07 09:41:19 -07001623 /** @hide */
1624 public int getUserId() {
1625 return mUserId;
1626 }
1627
1628 /**
Makoto Onukife9c9662016-07-25 15:12:23 -07001629 * {@link UserHandle} on which the publisher created this shortcut.
Makoto Onukiabe84422016-04-07 09:41:19 -07001630 */
1631 public UserHandle getUserHandle() {
1632 return UserHandle.of(mUserId);
1633 }
1634
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001635 /**
1636 * Last time when any of the fields was updated.
1637 */
1638 public long getLastChangedTimestamp() {
1639 return mLastChangedTimestamp;
1640 }
1641
1642 /** @hide */
1643 @ShortcutFlags
1644 public int getFlags() {
1645 return mFlags;
1646 }
1647
1648 /** @hide*/
Makoto Onukide667372016-03-15 14:29:20 -07001649 public void replaceFlags(@ShortcutFlags int flags) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001650 mFlags = flags;
1651 }
1652
1653 /** @hide*/
1654 public void addFlags(@ShortcutFlags int flags) {
1655 mFlags |= flags;
1656 }
1657
1658 /** @hide*/
1659 public void clearFlags(@ShortcutFlags int flags) {
1660 mFlags &= ~flags;
1661 }
1662
1663 /** @hide*/
1664 public boolean hasFlags(@ShortcutFlags int flags) {
1665 return (mFlags & flags) == flags;
1666 }
1667
Makoto Onukibf563b62017-05-04 10:25:30 -07001668 /** @hide */
1669 public boolean isReturnedByServer() {
1670 return hasFlags(FLAG_RETURNED_BY_SERVICE);
1671 }
1672
1673 /** @hide */
1674 public void setReturnedByServer() {
1675 addFlags(FLAG_RETURNED_BY_SERVICE);
1676 }
1677
Mehdi Alizadeh14242af2018-12-20 20:11:35 -08001678 /** @hide */
1679 public boolean isLongLived() {
1680 return hasFlags(FLAG_LONG_LIVED);
1681 }
1682
1683 /** @hide */
1684 public void setLongLived() {
1685 addFlags(FLAG_LONG_LIVED);
1686 }
1687
Mehdi Alizadeh505fb762020-01-21 17:26:24 -08001688 /** @hide */
1689 public void setCached() {
1690 addFlags(FLAG_CACHED);
1691 }
1692
1693 /** Return whether a shortcut is cached. */
1694 public boolean isCached() {
1695 return hasFlags(FLAG_CACHED);
1696 }
1697
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001698 /** Return whether a shortcut is dynamic. */
1699 public boolean isDynamic() {
1700 return hasFlags(FLAG_DYNAMIC);
1701 }
1702
1703 /** Return whether a shortcut is pinned. */
1704 public boolean isPinned() {
1705 return hasFlags(FLAG_PINNED);
1706 }
1707
1708 /**
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001709 * Return whether a shortcut is static; that is, whether a shortcut is
1710 * published from AndroidManifest.xml. If {@code true}, the shortcut is
1711 * also {@link #isImmutable()}.
Makoto Onuki22fcc682016-05-17 14:52:19 -07001712 *
1713 * <p>When an app is upgraded and a shortcut is no longer published from AndroidManifest.xml,
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001714 * this will be set to {@code false}. If the shortcut is not pinned, then it'll disappear.
1715 * However, if it's pinned, it will still be visible, {@link #isEnabled()} will be
Makoto Onuki22fcc682016-05-17 14:52:19 -07001716 * {@code false} and {@link #isImmutable()} will be {@code true}.
Makoto Onuki20c95f82016-05-11 16:51:01 -07001717 */
Makoto Onukib5a012f2016-06-21 11:13:53 -07001718 public boolean isDeclaredInManifest() {
Makoto Onuki22fcc682016-05-17 14:52:19 -07001719 return hasFlags(FLAG_MANIFEST);
Makoto Onuki20c95f82016-05-11 16:51:01 -07001720 }
1721
Makoto Onukib5a012f2016-06-21 11:13:53 -07001722 /** @hide kept for unit tests */
1723 @Deprecated
1724 public boolean isManifestShortcut() {
1725 return isDeclaredInManifest();
1726 }
1727
Makoto Onuki22fcc682016-05-17 14:52:19 -07001728 /**
Makoto Onuki9fd90192017-01-06 18:31:03 +00001729 * @return true if pinned but neither static nor dynamic.
Makoto Onuki22fcc682016-05-17 14:52:19 -07001730 * @hide
1731 */
1732 public boolean isFloating() {
1733 return isPinned() && !(isDynamic() || isManifestShortcut());
1734 }
1735
1736 /** @hide */
1737 public boolean isOriginallyFromManifest() {
1738 return hasFlags(FLAG_IMMUTABLE);
1739 }
1740
Makoto Onukia4f89b12017-10-05 10:37:55 -07001741 /** @hide */
1742 public boolean isDynamicVisible() {
1743 return isDynamic() && isVisibleToPublisher();
1744 }
1745
1746 /** @hide */
1747 public boolean isPinnedVisible() {
1748 return isPinned() && isVisibleToPublisher();
1749 }
1750
1751 /** @hide */
1752 public boolean isManifestVisible() {
1753 return isDeclaredInManifest() && isVisibleToPublisher();
1754 }
1755
Makoto Onuki22fcc682016-05-17 14:52:19 -07001756 /**
1757 * Return if a shortcut is immutable, in which case it cannot be modified with any of
1758 * {@link ShortcutManager} APIs.
1759 *
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001760 * <p>All static shortcuts are immutable. When a static shortcut is pinned and is then
1761 * disabled because it doesn't appear in AndroidManifest.xml for a newer version of the
1762 * app, {@link #isDeclaredInManifest()} returns {@code false}, but the shortcut
1763 * is still immutable.
Makoto Onuki22fcc682016-05-17 14:52:19 -07001764 *
1765 * <p>All shortcuts originally published via the {@link ShortcutManager} APIs
1766 * are all mutable.
1767 */
1768 public boolean isImmutable() {
1769 return hasFlags(FLAG_IMMUTABLE);
1770 }
1771
1772 /**
1773 * Returns {@code false} if a shortcut is disabled with
1774 * {@link ShortcutManager#disableShortcuts}.
1775 */
1776 public boolean isEnabled() {
Makoto Onuki20c95f82016-05-11 16:51:01 -07001777 return !hasFlags(FLAG_DISABLED);
1778 }
1779
Makoto Onuki22fcc682016-05-17 14:52:19 -07001780 /** @hide */
1781 public boolean isAlive() {
Mehdi Alizadeh505fb762020-01-21 17:26:24 -08001782 return hasFlags(FLAG_PINNED) || hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_MANIFEST)
1783 || hasFlags(FLAG_CACHED);
Makoto Onuki22fcc682016-05-17 14:52:19 -07001784 }
1785
1786 /** @hide */
1787 public boolean usesQuota() {
1788 return hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_MANIFEST);
1789 }
1790
Makoto Onuki20c95f82016-05-11 16:51:01 -07001791 /**
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001792 * Return whether a shortcut's icon is a resource in the owning package.
1793 *
Makoto Onukib5a012f2016-06-21 11:13:53 -07001794 * @hide internal/unit tests only
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001795 */
1796 public boolean hasIconResource() {
1797 return hasFlags(FLAG_HAS_ICON_RES);
1798 }
1799
Makoto Onuki20c95f82016-05-11 16:51:01 -07001800 /** @hide */
1801 public boolean hasStringResources() {
1802 return (mTitleResId != 0) || (mTextResId != 0) || (mDisabledMessageResId != 0);
1803 }
1804
1805 /** @hide */
1806 public boolean hasAnyResources() {
1807 return hasIconResource() || hasStringResources();
1808 }
1809
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001810 /**
1811 * Return whether a shortcut's icon is stored as a file.
1812 *
Makoto Onukib5a012f2016-06-21 11:13:53 -07001813 * @hide internal/unit tests only
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001814 */
1815 public boolean hasIconFile() {
1816 return hasFlags(FLAG_HAS_ICON_FILE);
1817 }
1818
Makoto Onuki55046222016-03-08 10:49:47 -08001819 /**
Hyunyoung Songe4179e22017-03-01 12:51:26 -08001820 * Return whether a shortcut's icon is adaptive bitmap following design guideline
Makoto Onukibf563b62017-05-04 10:25:30 -07001821 * defined in {@link android.graphics.drawable.AdaptiveIconDrawable}.
Hyunyoung Songf281e7a2017-02-13 10:57:42 -08001822 *
1823 * @hide internal/unit tests only
1824 */
Hyunyoung Songe4179e22017-03-01 12:51:26 -08001825 public boolean hasAdaptiveBitmap() {
1826 return hasFlags(FLAG_ADAPTIVE_BITMAP);
Hyunyoung Songf281e7a2017-02-13 10:57:42 -08001827 }
1828
Makoto Onuki475c3652017-05-08 14:29:03 -07001829 /** @hide */
1830 public boolean isIconPendingSave() {
1831 return hasFlags(FLAG_ICON_FILE_PENDING_SAVE);
1832 }
1833
1834 /** @hide */
1835 public void setIconPendingSave() {
1836 addFlags(FLAG_ICON_FILE_PENDING_SAVE);
1837 }
1838
1839 /** @hide */
1840 public void clearIconPendingSave() {
1841 clearFlags(FLAG_ICON_FILE_PENDING_SAVE);
1842 }
1843
Hyunyoung Songf281e7a2017-02-13 10:57:42 -08001844 /**
Makoto Onukia4f89b12017-10-05 10:37:55 -07001845 * When the system wasn't able to restore a shortcut, it'll still be registered to the system
1846 * but disabled, and such shortcuts will not be visible to the publisher. They're still visible
1847 * to launchers though.
1848 *
1849 * @hide
1850 */
1851 @TestApi
1852 public boolean isVisibleToPublisher() {
1853 return !isDisabledForRestoreIssue(mDisabledReason);
1854 }
1855
1856 /**
Makoto Onuki55046222016-03-08 10:49:47 -08001857 * Return whether a shortcut only contains "key" information only or not. If true, only the
1858 * following fields are available.
1859 * <ul>
1860 * <li>{@link #getId()}
Makoto Onuki22fcc682016-05-17 14:52:19 -07001861 * <li>{@link #getPackage()}
Makoto Onuki4d6b87f2016-06-17 13:47:40 -07001862 * <li>{@link #getActivity()}
Makoto Onuki55046222016-03-08 10:49:47 -08001863 * <li>{@link #getLastChangedTimestamp()}
1864 * <li>{@link #isDynamic()}
1865 * <li>{@link #isPinned()}
Makoto Onukib5a012f2016-06-21 11:13:53 -07001866 * <li>{@link #isDeclaredInManifest()}
1867 * <li>{@link #isImmutable()}
1868 * <li>{@link #isEnabled()}
1869 * <li>{@link #getUserHandle()}
Makoto Onuki55046222016-03-08 10:49:47 -08001870 * </ul>
Makoto Onuki4a910962016-07-07 13:57:34 -07001871 *
Makoto Onukife9c9662016-07-25 15:12:23 -07001872 * <p>For performance reasons, shortcuts passed to
1873 * {@link LauncherApps.Callback#onShortcutsChanged(String, List, UserHandle)} as well as those
1874 * returned from {@link LauncherApps#getShortcuts(ShortcutQuery, UserHandle)}
1875 * while using the {@link ShortcutQuery#FLAG_GET_KEY_FIELDS_ONLY} option contain only key
1876 * information.
Makoto Onuki55046222016-03-08 10:49:47 -08001877 */
1878 public boolean hasKeyFieldsOnly() {
1879 return hasFlags(FLAG_KEY_FIELDS_ONLY);
1880 }
1881
Makoto Onukib5a012f2016-06-21 11:13:53 -07001882 /** @hide */
Makoto Onuki20c95f82016-05-11 16:51:01 -07001883 public boolean hasStringResourcesResolved() {
1884 return hasFlags(FLAG_STRINGS_RESOLVED);
1885 }
1886
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001887 /** @hide */
1888 public void updateTimestamp() {
1889 mLastChangedTimestamp = System.currentTimeMillis();
1890 }
1891
1892 /** @hide */
1893 // VisibleForTesting
1894 public void setTimestamp(long value) {
1895 mLastChangedTimestamp = value;
1896 }
1897
1898 /** @hide */
Makoto Onuki55046222016-03-08 10:49:47 -08001899 public void clearIcon() {
1900 mIcon = null;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001901 }
1902
1903 /** @hide */
Makoto Onuki55046222016-03-08 10:49:47 -08001904 public void setIconResourceId(int iconResourceId) {
Makoto Onuki157b1622016-06-02 16:13:10 -07001905 if (mIconResId != iconResourceId) {
1906 mIconResName = null;
1907 }
1908 mIconResId = iconResourceId;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001909 }
1910
Makoto Onukib6d35232016-04-04 15:57:17 -07001911 /**
1912 * Get the resource ID for the icon, valid only when {@link #hasIconResource()} } is true.
Makoto Onukib5a012f2016-06-21 11:13:53 -07001913 * @hide internal / tests only.
Makoto Onukib6d35232016-04-04 15:57:17 -07001914 */
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001915 public int getIconResourceId() {
Makoto Onuki157b1622016-06-02 16:13:10 -07001916 return mIconResId;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001917 }
1918
Makoto Onuki475c3652017-05-08 14:29:03 -07001919 /**
1920 * Bitmap path. Note this will be null even if {@link #hasIconFile()} is set when the save
1921 * is pending. Use {@link #isIconPendingSave()} to check it.
1922 *
1923 * @hide
1924 */
Makoto Onuki6f7362d92016-03-04 13:39:41 -08001925 public String getBitmapPath() {
1926 return mBitmapPath;
1927 }
1928
1929 /** @hide */
1930 public void setBitmapPath(String bitmapPath) {
1931 mBitmapPath = bitmapPath;
1932 }
1933
Makoto Onuki22fcc682016-05-17 14:52:19 -07001934 /** @hide */
1935 public void setDisabledMessageResId(int disabledMessageResId) {
Makoto Onuki157b1622016-06-02 16:13:10 -07001936 if (mDisabledMessageResId != disabledMessageResId) {
1937 mDisabledMessageResName = null;
1938 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07001939 mDisabledMessageResId = disabledMessageResId;
1940 mDisabledMessage = null;
1941 }
1942
1943 /** @hide */
1944 public void setDisabledMessage(String disabledMessage) {
1945 mDisabledMessage = disabledMessage;
1946 mDisabledMessageResId = 0;
Makoto Onuki157b1622016-06-02 16:13:10 -07001947 mDisabledMessageResName = null;
1948 }
1949
1950 /** @hide */
1951 public String getTitleResName() {
1952 return mTitleResName;
1953 }
1954
1955 /** @hide */
1956 public void setTitleResName(String titleResName) {
1957 mTitleResName = titleResName;
1958 }
1959
1960 /** @hide */
1961 public String getTextResName() {
1962 return mTextResName;
1963 }
1964
1965 /** @hide */
1966 public void setTextResName(String textResName) {
1967 mTextResName = textResName;
1968 }
1969
1970 /** @hide */
1971 public String getDisabledMessageResName() {
1972 return mDisabledMessageResName;
1973 }
1974
1975 /** @hide */
1976 public void setDisabledMessageResName(String disabledMessageResName) {
1977 mDisabledMessageResName = disabledMessageResName;
1978 }
1979
1980 /** @hide */
1981 public String getIconResName() {
1982 return mIconResName;
1983 }
1984
1985 /** @hide */
1986 public void setIconResName(String iconResName) {
1987 mIconResName = iconResName;
Makoto Onuki22fcc682016-05-17 14:52:19 -07001988 }
1989
Makoto Onukidf6da042016-06-16 09:51:40 -07001990 /**
Kevin Hufnagle68d699d2016-10-14 19:04:51 -07001991 * Replaces the intent.
Makoto Onukidf6da042016-06-16 09:51:40 -07001992 *
1993 * @throws IllegalArgumentException when extra is not compatible with {@link PersistableBundle}.
1994 *
1995 * @hide
1996 */
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001997 public void setIntents(Intent[] intents) throws IllegalArgumentException {
Daulet Zhanguzina2044e12019-12-30 16:34:59 +00001998 Objects.requireNonNull(intents);
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001999 Preconditions.checkArgument(intents.length > 0);
Makoto Onukidf6da042016-06-16 09:51:40 -07002000
Makoto Onuki440a1ea2016-07-20 14:21:18 -07002001 mIntents = cloneIntents(intents);
2002 fixUpIntentExtras();
2003 }
Makoto Onukidf6da042016-06-16 09:51:40 -07002004
Makoto Onuki440a1ea2016-07-20 14:21:18 -07002005 /** @hide */
2006 public static Intent setIntentExtras(Intent intent, PersistableBundle extras) {
2007 if (extras == null) {
Makoto Onukidf6da042016-06-16 09:51:40 -07002008 intent.replaceExtras((Bundle) null);
Makoto Onukidf6da042016-06-16 09:51:40 -07002009 } else {
Makoto Onuki440a1ea2016-07-20 14:21:18 -07002010 intent.replaceExtras(new Bundle(extras));
Makoto Onukidf6da042016-06-16 09:51:40 -07002011 }
Makoto Onuki440a1ea2016-07-20 14:21:18 -07002012 return intent;
Makoto Onukidf6da042016-06-16 09:51:40 -07002013 }
2014
2015 /**
2016 * Replaces the categories.
2017 *
2018 * @hide
2019 */
2020 public void setCategories(Set<String> categories) {
2021 mCategories = cloneCategories(categories);
2022 }
2023
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002024 private ShortcutInfo(Parcel source) {
2025 final ClassLoader cl = getClass().getClassLoader();
2026
Makoto Onukiabe84422016-04-07 09:41:19 -07002027 mUserId = source.readInt();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002028 mId = source.readString();
2029 mPackageName = source.readString();
Makoto Onuki22fcc682016-05-17 14:52:19 -07002030 mActivity = source.readParcelable(cl);
Makoto Onuki4d6b87f2016-06-17 13:47:40 -07002031 mFlags = source.readInt();
2032 mIconResId = source.readInt();
2033 mLastChangedTimestamp = source.readLong();
Makoto Onukia4f89b12017-10-05 10:37:55 -07002034 mDisabledReason = source.readInt();
Makoto Onuki4d6b87f2016-06-17 13:47:40 -07002035
2036 if (source.readInt() == 0) {
2037 return; // key information only.
2038 }
2039
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002040 mIcon = source.readParcelable(cl);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002041 mTitle = source.readCharSequence();
Makoto Onuki20c95f82016-05-11 16:51:01 -07002042 mTitleResId = source.readInt();
Makoto Onuki22fcc682016-05-17 14:52:19 -07002043 mText = source.readCharSequence();
Makoto Onuki20c95f82016-05-11 16:51:01 -07002044 mTextResId = source.readInt();
Makoto Onuki22fcc682016-05-17 14:52:19 -07002045 mDisabledMessage = source.readCharSequence();
Makoto Onuki20c95f82016-05-11 16:51:01 -07002046 mDisabledMessageResId = source.readInt();
Makoto Onuki440a1ea2016-07-20 14:21:18 -07002047 mIntents = source.readParcelableArray(cl, Intent.class);
2048 mIntentPersistableExtrases = source.readParcelableArray(cl, PersistableBundle.class);
Makoto Onuki20c95f82016-05-11 16:51:01 -07002049 mRank = source.readInt();
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002050 mExtras = source.readParcelable(cl);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002051 mBitmapPath = source.readString();
Makoto Onukibe73a802016-04-15 14:46:35 -07002052
Makoto Onuki157b1622016-06-02 16:13:10 -07002053 mIconResName = source.readString();
2054 mTitleResName = source.readString();
2055 mTextResName = source.readString();
2056 mDisabledMessageResName = source.readString();
2057
Makoto Onukibe73a802016-04-15 14:46:35 -07002058 int N = source.readInt();
2059 if (N == 0) {
2060 mCategories = null;
2061 } else {
2062 mCategories = new ArraySet<>(N);
2063 for (int i = 0; i < N; i++) {
2064 mCategories.add(source.readString().intern());
2065 }
2066 }
Mehdi Alizadeh14242af2018-12-20 20:11:35 -08002067
2068 mPersons = source.readParcelableArray(cl, Person.class);
Felipe Leme90205ef2019-03-05 09:59:52 -08002069 mLocusId = source.readParcelable(cl);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002070 }
2071
2072 @Override
2073 public void writeToParcel(Parcel dest, int flags) {
Makoto Onukiabe84422016-04-07 09:41:19 -07002074 dest.writeInt(mUserId);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002075 dest.writeString(mId);
2076 dest.writeString(mPackageName);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002077 dest.writeParcelable(mActivity, flags);
Makoto Onuki4d6b87f2016-06-17 13:47:40 -07002078 dest.writeInt(mFlags);
2079 dest.writeInt(mIconResId);
2080 dest.writeLong(mLastChangedTimestamp);
Makoto Onukia4f89b12017-10-05 10:37:55 -07002081 dest.writeInt(mDisabledReason);
Makoto Onuki4d6b87f2016-06-17 13:47:40 -07002082
2083 if (hasKeyFieldsOnly()) {
2084 dest.writeInt(0);
2085 return;
2086 }
2087 dest.writeInt(1);
2088
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002089 dest.writeParcelable(mIcon, flags);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002090 dest.writeCharSequence(mTitle);
Makoto Onuki20c95f82016-05-11 16:51:01 -07002091 dest.writeInt(mTitleResId);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002092 dest.writeCharSequence(mText);
Makoto Onuki20c95f82016-05-11 16:51:01 -07002093 dest.writeInt(mTextResId);
Makoto Onuki22fcc682016-05-17 14:52:19 -07002094 dest.writeCharSequence(mDisabledMessage);
Makoto Onuki20c95f82016-05-11 16:51:01 -07002095 dest.writeInt(mDisabledMessageResId);
Makoto Onukibe73a802016-04-15 14:46:35 -07002096
Makoto Onuki440a1ea2016-07-20 14:21:18 -07002097 dest.writeParcelableArray(mIntents, flags);
2098 dest.writeParcelableArray(mIntentPersistableExtrases, flags);
Makoto Onuki20c95f82016-05-11 16:51:01 -07002099 dest.writeInt(mRank);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002100 dest.writeParcelable(mExtras, flags);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002101 dest.writeString(mBitmapPath);
Makoto Onukibe73a802016-04-15 14:46:35 -07002102
Makoto Onuki157b1622016-06-02 16:13:10 -07002103 dest.writeString(mIconResName);
2104 dest.writeString(mTitleResName);
2105 dest.writeString(mTextResName);
2106 dest.writeString(mDisabledMessageResName);
2107
Makoto Onukibe73a802016-04-15 14:46:35 -07002108 if (mCategories != null) {
2109 final int N = mCategories.size();
2110 dest.writeInt(N);
2111 for (int i = 0; i < N; i++) {
2112 dest.writeString(mCategories.valueAt(i));
2113 }
2114 } else {
2115 dest.writeInt(0);
2116 }
Mehdi Alizadeh14242af2018-12-20 20:11:35 -08002117
2118 dest.writeParcelableArray(mPersons, flags);
Felipe Leme90205ef2019-03-05 09:59:52 -08002119 dest.writeParcelable(mLocusId, flags);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002120 }
2121
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07002122 public static final @android.annotation.NonNull Creator<ShortcutInfo> CREATOR =
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002123 new Creator<ShortcutInfo>() {
2124 public ShortcutInfo createFromParcel(Parcel source) {
2125 return new ShortcutInfo(source);
2126 }
2127 public ShortcutInfo[] newArray(int size) {
2128 return new ShortcutInfo[size];
2129 }
2130 };
2131
2132 @Override
2133 public int describeContents() {
2134 return 0;
2135 }
2136
Makoto Onuki6208c672017-10-02 16:20:35 -07002137
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002138 /**
2139 * Return a string representation, intended for logging. Some fields will be retracted.
2140 */
2141 @Override
2142 public String toString() {
Makoto Onuki6208c672017-10-02 16:20:35 -07002143 return toStringInner(/* secure =*/ true, /* includeInternalData =*/ false,
2144 /*indent=*/ null);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002145 }
2146
2147 /** @hide */
2148 public String toInsecureString() {
Makoto Onuki6208c672017-10-02 16:20:35 -07002149 return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true,
2150 /*indent=*/ null);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002151 }
2152
Makoto Onuki6208c672017-10-02 16:20:35 -07002153 /** @hide */
2154 public String toDumpString(String indent) {
2155 return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true, indent);
2156 }
2157
2158 private void addIndentOrComma(StringBuilder sb, String indent) {
2159 if (indent != null) {
2160 sb.append("\n ");
2161 sb.append(indent);
2162 } else {
2163 sb.append(", ");
2164 }
2165 }
2166
2167 private String toStringInner(boolean secure, boolean includeInternalData, String indent) {
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002168 final StringBuilder sb = new StringBuilder();
Makoto Onuki6208c672017-10-02 16:20:35 -07002169
2170 if (indent != null) {
2171 sb.append(indent);
2172 }
2173
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002174 sb.append("ShortcutInfo {");
2175
2176 sb.append("id=");
2177 sb.append(secure ? "***" : mId);
2178
Makoto Onuki22fcc682016-05-17 14:52:19 -07002179 sb.append(", flags=0x");
2180 sb.append(Integer.toHexString(mFlags));
2181 sb.append(" [");
Makoto Onukia4f89b12017-10-05 10:37:55 -07002182 if ((mFlags & FLAG_SHADOW) != 0) {
2183 // Note the shadow flag isn't actually used anywhere and it's just for dumpsys, so
2184 // we don't have an isXxx for this.
2185 sb.append("Sdw");
2186 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07002187 if (!isEnabled()) {
Makoto Onuki6208c672017-10-02 16:20:35 -07002188 sb.append("Dis");
Makoto Onuki22fcc682016-05-17 14:52:19 -07002189 }
2190 if (isImmutable()) {
2191 sb.append("Im");
2192 }
2193 if (isManifestShortcut()) {
Makoto Onuki6208c672017-10-02 16:20:35 -07002194 sb.append("Man");
Makoto Onuki22fcc682016-05-17 14:52:19 -07002195 }
2196 if (isDynamic()) {
Makoto Onuki6208c672017-10-02 16:20:35 -07002197 sb.append("Dyn");
Makoto Onuki22fcc682016-05-17 14:52:19 -07002198 }
2199 if (isPinned()) {
Makoto Onuki6208c672017-10-02 16:20:35 -07002200 sb.append("Pin");
Makoto Onuki22fcc682016-05-17 14:52:19 -07002201 }
2202 if (hasIconFile()) {
Makoto Onuki6208c672017-10-02 16:20:35 -07002203 sb.append("Ic-f");
Makoto Onuki22fcc682016-05-17 14:52:19 -07002204 }
Makoto Onuki475c3652017-05-08 14:29:03 -07002205 if (isIconPendingSave()) {
Makoto Onuki6208c672017-10-02 16:20:35 -07002206 sb.append("Pens");
Makoto Onuki475c3652017-05-08 14:29:03 -07002207 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07002208 if (hasIconResource()) {
Makoto Onuki6208c672017-10-02 16:20:35 -07002209 sb.append("Ic-r");
Makoto Onuki22fcc682016-05-17 14:52:19 -07002210 }
2211 if (hasKeyFieldsOnly()) {
Makoto Onuki6208c672017-10-02 16:20:35 -07002212 sb.append("Key");
Makoto Onuki22fcc682016-05-17 14:52:19 -07002213 }
2214 if (hasStringResourcesResolved()) {
Makoto Onuki6208c672017-10-02 16:20:35 -07002215 sb.append("Str");
Makoto Onuki22fcc682016-05-17 14:52:19 -07002216 }
Makoto Onukibf563b62017-05-04 10:25:30 -07002217 if (isReturnedByServer()) {
Makoto Onuki6208c672017-10-02 16:20:35 -07002218 sb.append("Rets");
Makoto Onukibf563b62017-05-04 10:25:30 -07002219 }
Mehdi Alizadeh14242af2018-12-20 20:11:35 -08002220 if (isLongLived()) {
2221 sb.append("Liv");
2222 }
Makoto Onuki22fcc682016-05-17 14:52:19 -07002223 sb.append("]");
2224
Makoto Onuki6208c672017-10-02 16:20:35 -07002225 addIndentOrComma(sb, indent);
2226
2227 sb.append("packageName=");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002228 sb.append(mPackageName);
2229
Makoto Onukia4f89b12017-10-05 10:37:55 -07002230 addIndentOrComma(sb, indent);
2231
2232 sb.append("activity=");
Makoto Onuki22fcc682016-05-17 14:52:19 -07002233 sb.append(mActivity);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002234
Makoto Onuki6208c672017-10-02 16:20:35 -07002235 addIndentOrComma(sb, indent);
2236
2237 sb.append("shortLabel=");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002238 sb.append(secure ? "***" : mTitle);
Makoto Onukieddbfec2016-05-31 17:04:34 -07002239 sb.append(", resId=");
Makoto Onuki20c95f82016-05-11 16:51:01 -07002240 sb.append(mTitleResId);
Makoto Onuki157b1622016-06-02 16:13:10 -07002241 sb.append("[");
2242 sb.append(mTitleResName);
2243 sb.append("]");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002244
Makoto Onuki6208c672017-10-02 16:20:35 -07002245 addIndentOrComma(sb, indent);
2246
2247 sb.append("longLabel=");
Makoto Onukie3ae7ec2016-03-29 15:45:25 -07002248 sb.append(secure ? "***" : mText);
Makoto Onukieddbfec2016-05-31 17:04:34 -07002249 sb.append(", resId=");
Makoto Onuki20c95f82016-05-11 16:51:01 -07002250 sb.append(mTextResId);
Makoto Onuki157b1622016-06-02 16:13:10 -07002251 sb.append("[");
2252 sb.append(mTextResName);
2253 sb.append("]");
Makoto Onuki20c95f82016-05-11 16:51:01 -07002254
Makoto Onuki6208c672017-10-02 16:20:35 -07002255 addIndentOrComma(sb, indent);
2256
2257 sb.append("disabledMessage=");
Makoto Onuki20c95f82016-05-11 16:51:01 -07002258 sb.append(secure ? "***" : mDisabledMessage);
Makoto Onukieddbfec2016-05-31 17:04:34 -07002259 sb.append(", resId=");
Makoto Onuki20c95f82016-05-11 16:51:01 -07002260 sb.append(mDisabledMessageResId);
Makoto Onuki157b1622016-06-02 16:13:10 -07002261 sb.append("[");
2262 sb.append(mDisabledMessageResName);
2263 sb.append("]");
Makoto Onukie3ae7ec2016-03-29 15:45:25 -07002264
Makoto Onuki6208c672017-10-02 16:20:35 -07002265 addIndentOrComma(sb, indent);
2266
Makoto Onukia4f89b12017-10-05 10:37:55 -07002267 sb.append("disabledReason=");
Makoto Onukib1588c02017-10-12 15:11:45 -07002268 sb.append(getDisabledReasonDebugString(mDisabledReason));
Makoto Onukia4f89b12017-10-05 10:37:55 -07002269
2270 addIndentOrComma(sb, indent);
2271
Makoto Onuki6208c672017-10-02 16:20:35 -07002272 sb.append("categories=");
Makoto Onukib6d35232016-04-04 15:57:17 -07002273 sb.append(mCategories);
2274
Makoto Onuki6208c672017-10-02 16:20:35 -07002275 addIndentOrComma(sb, indent);
2276
Mehdi Alizadeh14242af2018-12-20 20:11:35 -08002277 sb.append("persons=");
2278 sb.append(mPersons);
2279
2280 addIndentOrComma(sb, indent);
2281
Makoto Onuki6208c672017-10-02 16:20:35 -07002282 sb.append("icon=");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002283 sb.append(mIcon);
2284
Makoto Onuki6208c672017-10-02 16:20:35 -07002285 addIndentOrComma(sb, indent);
2286
2287 sb.append("rank=");
Makoto Onuki20c95f82016-05-11 16:51:01 -07002288 sb.append(mRank);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002289
2290 sb.append(", timestamp=");
2291 sb.append(mLastChangedTimestamp);
2292
Makoto Onuki6208c672017-10-02 16:20:35 -07002293 addIndentOrComma(sb, indent);
2294
2295 sb.append("intents=");
Makoto Onuki440a1ea2016-07-20 14:21:18 -07002296 if (mIntents == null) {
2297 sb.append("null");
2298 } else {
2299 if (secure) {
2300 sb.append("size:");
2301 sb.append(mIntents.length);
2302 } else {
2303 final int size = mIntents.length;
2304 sb.append("[");
2305 String sep = "";
2306 for (int i = 0; i < size; i++) {
2307 sb.append(sep);
2308 sep = ", ";
2309 sb.append(mIntents[i]);
2310 sb.append("/");
2311 sb.append(mIntentPersistableExtrases[i]);
2312 }
2313 sb.append("]");
2314 }
2315 }
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002316
Makoto Onuki6208c672017-10-02 16:20:35 -07002317 addIndentOrComma(sb, indent);
2318
2319 sb.append("extras=");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002320 sb.append(mExtras);
2321
2322 if (includeInternalData) {
Makoto Onuki6208c672017-10-02 16:20:35 -07002323 addIndentOrComma(sb, indent);
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002324
Makoto Onuki6208c672017-10-02 16:20:35 -07002325 sb.append("iconRes=");
Makoto Onuki157b1622016-06-02 16:13:10 -07002326 sb.append(mIconResId);
2327 sb.append("[");
2328 sb.append(mIconResName);
2329 sb.append("]");
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002330
2331 sb.append(", bitmapPath=");
2332 sb.append(mBitmapPath);
2333 }
2334
Felipe Leme90205ef2019-03-05 09:59:52 -08002335 if (mLocusId != null) {
2336 sb.append("locusId="); sb.append(mLocusId); // LocusId.toString() is PII-safe.
2337 }
2338
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002339 sb.append("}");
2340 return sb.toString();
2341 }
2342
2343 /** @hide */
Makoto Onukiabe84422016-04-07 09:41:19 -07002344 public ShortcutInfo(
Makoto Onuki22fcc682016-05-17 14:52:19 -07002345 @UserIdInt int userId, String id, String packageName, ComponentName activity,
Makoto Onuki157b1622016-06-02 16:13:10 -07002346 Icon icon, CharSequence title, int titleResId, String titleResName,
2347 CharSequence text, int textResId, String textResName,
2348 CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
Makoto Onuki440a1ea2016-07-20 14:21:18 -07002349 Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
2350 long lastChangedTimestamp,
Mehdi Alizadehebb4b602019-02-05 15:52:18 -08002351 int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason,
Felipe Leme90205ef2019-03-05 09:59:52 -08002352 Person[] persons, LocusId locusId) {
Makoto Onukiabe84422016-04-07 09:41:19 -07002353 mUserId = userId;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002354 mId = id;
2355 mPackageName = packageName;
Makoto Onuki22fcc682016-05-17 14:52:19 -07002356 mActivity = activity;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002357 mIcon = icon;
2358 mTitle = title;
Makoto Onuki20c95f82016-05-11 16:51:01 -07002359 mTitleResId = titleResId;
Makoto Onuki157b1622016-06-02 16:13:10 -07002360 mTitleResName = titleResName;
Makoto Onukie3ae7ec2016-03-29 15:45:25 -07002361 mText = text;
Makoto Onuki20c95f82016-05-11 16:51:01 -07002362 mTextResId = textResId;
Makoto Onuki157b1622016-06-02 16:13:10 -07002363 mTextResName = textResName;
Makoto Onuki20c95f82016-05-11 16:51:01 -07002364 mDisabledMessage = disabledMessage;
2365 mDisabledMessageResId = disabledMessageResId;
Makoto Onuki157b1622016-06-02 16:13:10 -07002366 mDisabledMessageResName = disabledMessageResName;
Makoto Onukidf6da042016-06-16 09:51:40 -07002367 mCategories = cloneCategories(categories);
Makoto Onuki440a1ea2016-07-20 14:21:18 -07002368 mIntents = cloneIntents(intentsWithExtras);
2369 fixUpIntentExtras();
Makoto Onuki20c95f82016-05-11 16:51:01 -07002370 mRank = rank;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002371 mExtras = extras;
2372 mLastChangedTimestamp = lastChangedTimestamp;
2373 mFlags = flags;
Makoto Onuki157b1622016-06-02 16:13:10 -07002374 mIconResId = iconResId;
2375 mIconResName = iconResName;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002376 mBitmapPath = bitmapPath;
Makoto Onukia4f89b12017-10-05 10:37:55 -07002377 mDisabledReason = disabledReason;
Mehdi Alizadehebb4b602019-02-05 15:52:18 -08002378 mPersons = persons;
Felipe Leme90205ef2019-03-05 09:59:52 -08002379 mLocusId = locusId;
Makoto Onuki6f7362d92016-03-04 13:39:41 -08002380 }
2381}