diff --git a/api/current.txt b/api/current.txt index eb5c17371693d..23f9c64dbab5b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9502,6 +9502,8 @@ package android.content.pm { public class LauncherApps { method public java.util.List getActivityList(java.lang.String, android.os.UserHandle); + method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(int); + method public android.graphics.drawable.Drawable getShortcutIconDrawable(int); method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo); method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle); method public java.util.List getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle); @@ -10045,31 +10047,41 @@ package android.content.pm { method public int describeContents(); method public android.content.ComponentName getActivityComponent(); method public java.util.Set getCategories(); + method public java.lang.String getDisabledMessage(); + method public int getDisabledMessageResId(); method public android.os.PersistableBundle getExtras(); method public int getIconResourceId(); method public java.lang.String getId(); method public android.content.Intent getIntent(); method public long getLastChangedTimestamp(); method public java.lang.String getPackageName(); + method public int getRank(); method public java.lang.String getText(); + method public int getTextResId(); method public java.lang.String getTitle(); + method public int getTitleResId(); method public android.os.UserHandle getUserHandle(); - method public int getWeight(); method public boolean hasIconFile(); method public boolean hasIconResource(); method public boolean hasKeyFieldsOnly(); + method public boolean hasStringResourcesResolved(); + method public boolean isDisabled(); method public boolean isDynamic(); + method public boolean isFromManifest(); method public boolean isPinned(); method public void writeToParcel(android.os.Parcel, int); field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1 field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3 field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4 field public static final android.os.Parcelable.Creator CREATOR; + field public static final int FLAG_DISABLED = 64; // 0x40 field public static final int FLAG_DYNAMIC = 1; // 0x1 + field public static final int FLAG_FROM_MANIFEST = 32; // 0x20 field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8 field public static final int FLAG_HAS_ICON_RES = 4; // 0x4 field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10 field public static final int FLAG_PINNED = 2; // 0x2 + field public static final int FLAG_STRINGS_RESOLVED = 128; // 0x80 field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation"; } @@ -10078,17 +10090,24 @@ package android.content.pm { method public android.content.pm.ShortcutInfo build(); method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName); method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set); + method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.String); + method public android.content.pm.ShortcutInfo.Builder setDisabledMessageResId(int); method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String); method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent); + method public android.content.pm.ShortcutInfo.Builder setRank(int); method public android.content.pm.ShortcutInfo.Builder setText(java.lang.String); + method public android.content.pm.ShortcutInfo.Builder setTextResId(int); method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String); - method public android.content.pm.ShortcutInfo.Builder setWeight(int); + method public android.content.pm.ShortcutInfo.Builder setTitleResId(int); } public class ShortcutManager { method public boolean addDynamicShortcuts(java.util.List); + method public void disableShortcuts(java.util.List); + method public void disableShortcuts(java.util.List, int); + method public void disableShortcuts(java.util.List, java.lang.String); method public java.util.List getDynamicShortcuts(); method public int getIconMaxDimensions(); method public int getMaxDynamicShortcutCount(); @@ -10097,6 +10116,7 @@ package android.content.pm { method public int getRemainingCallCount(); method public void removeAllDynamicShortcuts(); method public void removeDynamicShortcuts(java.util.List); + method public void reportShortcutUsed(java.lang.String); method public boolean setDynamicShortcuts(java.util.List); method public boolean updateShortcuts(java.util.List); } diff --git a/api/system-current.txt b/api/system-current.txt index b08cc28b1cc39..c9cdf53a00ca1 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -9859,6 +9859,8 @@ package android.content.pm { public class LauncherApps { method public java.util.List getActivityList(java.lang.String, android.os.UserHandle); + method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(int); + method public android.graphics.drawable.Drawable getShortcutIconDrawable(int); method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo); method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle); method public java.util.List getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle); @@ -10472,31 +10474,41 @@ package android.content.pm { method public int describeContents(); method public android.content.ComponentName getActivityComponent(); method public java.util.Set getCategories(); + method public java.lang.String getDisabledMessage(); + method public int getDisabledMessageResId(); method public android.os.PersistableBundle getExtras(); method public int getIconResourceId(); method public java.lang.String getId(); method public android.content.Intent getIntent(); method public long getLastChangedTimestamp(); method public java.lang.String getPackageName(); + method public int getRank(); method public java.lang.String getText(); + method public int getTextResId(); method public java.lang.String getTitle(); + method public int getTitleResId(); method public android.os.UserHandle getUserHandle(); - method public int getWeight(); method public boolean hasIconFile(); method public boolean hasIconResource(); method public boolean hasKeyFieldsOnly(); + method public boolean hasStringResourcesResolved(); + method public boolean isDisabled(); method public boolean isDynamic(); + method public boolean isFromManifest(); method public boolean isPinned(); method public void writeToParcel(android.os.Parcel, int); field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1 field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3 field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4 field public static final android.os.Parcelable.Creator CREATOR; + field public static final int FLAG_DISABLED = 64; // 0x40 field public static final int FLAG_DYNAMIC = 1; // 0x1 + field public static final int FLAG_FROM_MANIFEST = 32; // 0x20 field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8 field public static final int FLAG_HAS_ICON_RES = 4; // 0x4 field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10 field public static final int FLAG_PINNED = 2; // 0x2 + field public static final int FLAG_STRINGS_RESOLVED = 128; // 0x80 field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation"; } @@ -10505,17 +10517,24 @@ package android.content.pm { method public android.content.pm.ShortcutInfo build(); method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName); method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set); + method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.String); + method public android.content.pm.ShortcutInfo.Builder setDisabledMessageResId(int); method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String); method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent); + method public android.content.pm.ShortcutInfo.Builder setRank(int); method public android.content.pm.ShortcutInfo.Builder setText(java.lang.String); + method public android.content.pm.ShortcutInfo.Builder setTextResId(int); method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String); - method public android.content.pm.ShortcutInfo.Builder setWeight(int); + method public android.content.pm.ShortcutInfo.Builder setTitleResId(int); } public class ShortcutManager { method public boolean addDynamicShortcuts(java.util.List); + method public void disableShortcuts(java.util.List); + method public void disableShortcuts(java.util.List, int); + method public void disableShortcuts(java.util.List, java.lang.String); method public java.util.List getDynamicShortcuts(); method public int getIconMaxDimensions(); method public int getMaxDynamicShortcutCount(); @@ -10524,6 +10543,7 @@ package android.content.pm { method public int getRemainingCallCount(); method public void removeAllDynamicShortcuts(); method public void removeDynamicShortcuts(java.util.List); + method public void reportShortcutUsed(java.lang.String); method public boolean setDynamicShortcuts(java.util.List); method public boolean updateShortcuts(java.util.List); } diff --git a/api/test-current.txt b/api/test-current.txt index 37b35da8a38ac..c1103e146c6be 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -9513,6 +9513,8 @@ package android.content.pm { public class LauncherApps { ctor public LauncherApps(android.content.Context); method public java.util.List getActivityList(java.lang.String, android.os.UserHandle); + method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(int); + method public android.graphics.drawable.Drawable getShortcutIconDrawable(int); method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo); method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle); method public java.util.List getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle); @@ -10057,31 +10059,41 @@ package android.content.pm { method public int describeContents(); method public android.content.ComponentName getActivityComponent(); method public java.util.Set getCategories(); + method public java.lang.String getDisabledMessage(); + method public int getDisabledMessageResId(); method public android.os.PersistableBundle getExtras(); method public int getIconResourceId(); method public java.lang.String getId(); method public android.content.Intent getIntent(); method public long getLastChangedTimestamp(); method public java.lang.String getPackageName(); + method public int getRank(); method public java.lang.String getText(); + method public int getTextResId(); method public java.lang.String getTitle(); + method public int getTitleResId(); method public android.os.UserHandle getUserHandle(); - method public int getWeight(); method public boolean hasIconFile(); method public boolean hasIconResource(); method public boolean hasKeyFieldsOnly(); + method public boolean hasStringResourcesResolved(); + method public boolean isDisabled(); method public boolean isDynamic(); + method public boolean isFromManifest(); method public boolean isPinned(); method public void writeToParcel(android.os.Parcel, int); field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1 field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3 field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4 field public static final android.os.Parcelable.Creator CREATOR; + field public static final int FLAG_DISABLED = 64; // 0x40 field public static final int FLAG_DYNAMIC = 1; // 0x1 + field public static final int FLAG_FROM_MANIFEST = 32; // 0x20 field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8 field public static final int FLAG_HAS_ICON_RES = 4; // 0x4 field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10 field public static final int FLAG_PINNED = 2; // 0x2 + field public static final int FLAG_STRINGS_RESOLVED = 128; // 0x80 field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation"; } @@ -10090,18 +10102,25 @@ package android.content.pm { method public android.content.pm.ShortcutInfo build(); method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName); method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set); + method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.String); + method public android.content.pm.ShortcutInfo.Builder setDisabledMessageResId(int); method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String); method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent); + method public android.content.pm.ShortcutInfo.Builder setRank(int); method public android.content.pm.ShortcutInfo.Builder setText(java.lang.String); + method public android.content.pm.ShortcutInfo.Builder setTextResId(int); method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String); - method public android.content.pm.ShortcutInfo.Builder setWeight(int); + method public android.content.pm.ShortcutInfo.Builder setTitleResId(int); } public class ShortcutManager { ctor public ShortcutManager(android.content.Context); method public boolean addDynamicShortcuts(java.util.List); + method public void disableShortcuts(java.util.List); + method public void disableShortcuts(java.util.List, int); + method public void disableShortcuts(java.util.List, java.lang.String); method public java.util.List getDynamicShortcuts(); method public int getIconMaxDimensions(); method public int getMaxDynamicShortcutCount(); @@ -10110,6 +10129,7 @@ package android.content.pm { method public int getRemainingCallCount(); method public void removeAllDynamicShortcuts(); method public void removeDynamicShortcuts(java.util.List); + method public void reportShortcutUsed(java.lang.String); method public boolean setDynamicShortcuts(java.util.List); method public boolean updateShortcuts(java.util.List); } diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl index 2ba24f6eecdba..06200bfdb929c 100644 --- a/core/java/android/content/pm/IShortcutService.aidl +++ b/core/java/android/content/pm/IShortcutService.aidl @@ -39,6 +39,9 @@ interface IShortcutService { boolean updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId); + void disableShortcuts(String packageName, in List shortcutIds, String disabledMessage, + int disabledMessageResId, int userId); + int getMaxDynamicShortcutCount(String packageName, int userId); int getRemainingCallCount(String packageName, int userId); @@ -47,6 +50,8 @@ interface IShortcutService { int getIconMaxDimensions(String packageName, int userId); + void reportShortcutUsed(String packageName, String shortcutId, int userId); + void resetThrottling(); // system only API for developer opsions void onApplicationActive(String packageName, int userId); // system only API for sysUI diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index eaec1d7a5894c..acd85cb1ed30d 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager.ApplicationInfoFlags; import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -554,6 +555,16 @@ public class LauncherApps { } } + /** TODO Javadoc */ + public Drawable getShortcutIconDrawable(int density) { + throw new RuntimeException("TODO implement it"); + } + + /** TODO Javadoc */ + public Drawable getShortcutBadgedIconDrawable(int density) { + throw new RuntimeException("TODO implement it"); + } + /** * Launches a shortcut. * diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 988ba4410133f..3648d9e1014d6 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -22,6 +22,8 @@ import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Parcel; @@ -67,6 +69,15 @@ public final class ShortcutInfo implements Parcelable { /* @hide */ public static final int FLAG_KEY_FIELDS_ONLY = 1 << 4; + /* @hide */ + public static final int FLAG_FROM_MANIFEST = 1 << 5; + + /* @hide */ + public static final int FLAG_DISABLED = 1 << 6; + + /* @hide */ + public static final int FLAG_STRINGS_RESOLVED = 1 << 7; + /** @hide */ @IntDef(flag = true, value = { @@ -75,6 +86,9 @@ public final class ShortcutInfo implements Parcelable { FLAG_HAS_ICON_RES, FLAG_HAS_ICON_FILE, FLAG_KEY_FIELDS_ONLY, + FLAG_FROM_MANIFEST, + FLAG_DISABLED, + FLAG_STRINGS_RESOLVED, }) @Retention(RetentionPolicy.SOURCE) public @interface ShortcutFlags {} @@ -124,28 +138,37 @@ public final class ShortcutInfo implements Parcelable { @Nullable private Icon mIcon; - @NonNull + private int mTitleResId; + + @Nullable private String mTitle; + private int mTextResId; + @Nullable private String mText; - @NonNull + private int mDisabledMessageResId; + + @Nullable + private String mDisabledMessage; + + @Nullable private ArraySet mCategories; /** * Intent *with extras removed*. */ - @NonNull + @Nullable private Intent mIntent; /** * Extras for the intent. */ - @NonNull + @Nullable private PersistableBundle mIntentPersistableExtras; - private int mWeight; + private int mRank; @Nullable private PersistableBundle mExtras; @@ -176,7 +199,11 @@ public final class ShortcutInfo implements Parcelable { mActivityComponent = b.mActivityComponent; mIcon = b.mIcon; mTitle = b.mTitle; + mTitleResId = b.mTitleResId; mText = b.mText; + mTextResId = b.mTextResId; + mDisabledMessage = b.mDisabledMessage; + mDisabledMessageResId = b.mDisabledMessageResId; mCategories = clone(b.mCategories); mIntent = b.mIntent; if (mIntent != null) { @@ -186,7 +213,7 @@ public final class ShortcutInfo implements Parcelable { mIntentPersistableExtras = new PersistableBundle(intentExtras); } } - mWeight = b.mWeight; + mRank = b.mRank; mExtras = b.mExtras; updateTimestamp(); } @@ -202,7 +229,10 @@ public final class ShortcutInfo implements Parcelable { */ public void enforceMandatoryFields() { Preconditions.checkStringNotEmpty(mId, "Shortcut ID must be provided"); - Preconditions.checkStringNotEmpty(mTitle, "Shortcut title must be provided"); + Preconditions.checkNotNull(mActivityComponent, "activityComponent must be provided"); + if (mTitle == null && mTitleResId == 0) { + throw new IllegalArgumentException("Shortcut title must be provided"); + } Preconditions.checkNotNull(mIntent, "Shortcut Intent must be provided"); } @@ -228,13 +258,17 @@ public final class ShortcutInfo implements Parcelable { } mTitle = source.mTitle; + mTitleResId = source.mTitleResId; mText = source.mText; + mTextResId = source.mTextResId; + mDisabledMessage = source.mDisabledMessage; + mDisabledMessageResId = source.mDisabledMessageResId; mCategories = clone(source.mCategories); if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) { mIntent = source.mIntent; mIntentPersistableExtras = source.mIntentPersistableExtras; } - mWeight = source.mWeight; + mRank = source.mRank; mExtras = source.mExtras; } else { // Set this bit. @@ -242,6 +276,30 @@ public final class ShortcutInfo implements Parcelable { } } + /** @hide */ + public void resolveStringsRequiringCrossUser(Context context) throws NameNotFoundException { + mFlags |= FLAG_STRINGS_RESOLVED; + + if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)) { + return; // Bail early. + } + final Resources res = context.getPackageManager().getResourcesForApplicationAsUser( + mPackageName, mUserId); + + if (mTitleResId != 0) { + mTitle = res.getString(mTitleResId); + mTitleResId = 0; + } + if (mTextResId != 0) { + mText = res.getString(mTextResId); + mTextResId = 0; + } + if (mDisabledMessageResId != 0) { + mDisabledMessage = res.getString(mDisabledMessageResId); + mDisabledMessageResId = 0; + } + } + /** * Copy a {@link ShortcutInfo}, optionally removing fields. * @hide @@ -275,9 +333,24 @@ public final class ShortcutInfo implements Parcelable { } if (source.mTitle != null) { mTitle = source.mTitle; + mTitleResId = 0; + } else if (source.mTitleResId != 0) { + mTitle = null; + mTitleResId = source.getTitleResId(); } if (source.mText != null) { mText = source.mText; + mTextResId = 0; + } else if (source.mTextResId != 0) { + mText = null; + mTextResId = source.mTextResId; + } + if (source.mDisabledMessage != null) { + mDisabledMessage = source.mDisabledMessage; + mDisabledMessageResId = 0; + } else if (source.mDisabledMessageResId != 0) { + mDisabledMessage = null; + mDisabledMessageResId = source.mDisabledMessageResId; } if (source.mCategories != null) { mCategories = clone(source.mCategories); @@ -286,8 +359,8 @@ public final class ShortcutInfo implements Parcelable { mIntent = source.mIntent; mIntentPersistableExtras = source.mIntentPersistableExtras; } - if (source.mWeight != 0) { - mWeight = source.mWeight; + if (source.mRank != 0) { + mRank = source.mRank; } if (source.mExtras != null) { mExtras = source.mExtras; @@ -308,7 +381,6 @@ public final class ShortcutInfo implements Parcelable { throw getInvalidIconException(); } if (icon.hasTint()) { - // TODO support it throw new IllegalArgumentException("Icons with tints are not supported"); } @@ -333,15 +405,23 @@ public final class ShortcutInfo implements Parcelable { private Icon mIcon; + private int mTitleResId; + private String mTitle; + private int mTextResId; + private String mText; + private int mDisabledMessageResId; + + private String mDisabledMessage; + private Set mCategories; private Intent mIntent; - private int mWeight; + private int mRank; private PersistableBundle mExtras; @@ -396,6 +476,13 @@ public final class ShortcutInfo implements Parcelable { return this; } + /** TODO Javadoc */ + public Builder setTitleResId(int titleResId) { + Preconditions.checkState(mTitle == null, "title already set"); + mTitleResId = titleResId; + return this; + } + /** * Sets the title of a shortcut. This is a mandatory field. * @@ -404,10 +491,18 @@ public final class ShortcutInfo implements Parcelable { */ @NonNull public Builder setTitle(@NonNull String title) { + Preconditions.checkState(mTitleResId == 0, "titleResId already set"); mTitle = Preconditions.checkStringNotEmpty(title, "title"); return this; } + /** TODO Javadoc */ + public Builder setTextResId(int textResId) { + Preconditions.checkState(mText == null, "text already set"); + mTextResId = textResId; + return this; + } + /** * Sets the text of a shortcut. This is an optional field. * @@ -416,10 +511,27 @@ public final class ShortcutInfo implements Parcelable { */ @NonNull public Builder setText(@NonNull String text) { + Preconditions.checkState(mTextResId == 0, "textResId already set"); mText = Preconditions.checkStringNotEmpty(text, "text"); return this; } + /** TODO Javadoc */ + public Builder setDisabledMessageResId(int disabledMessageResId) { + Preconditions.checkState(mDisabledMessage == null, "disabledMessage already set"); + mDisabledMessageResId = disabledMessageResId; + return this; + } + + @NonNull + public Builder setDisabledMessage(@NonNull String disabledMessage) { + Preconditions.checkState( + mDisabledMessageResId == 0, "disabledMessageResId already set"); + mDisabledMessage = + Preconditions.checkStringNotEmpty(disabledMessage, "disabledMessage"); + return this; + } + /** * Sets categories for a shortcut. Launcher applications may use this information to * categorise shortcuts. @@ -444,12 +556,11 @@ public final class ShortcutInfo implements Parcelable { } /** - * Optionally sets the weight of a shortcut, which will be used by the launcher for sorting. - * The larger the weight, the more "important" a shortcut is. + * TODO javadoc. */ @NonNull - public Builder setWeight(int weight) { - mWeight = weight; + public Builder setRank(int rank) { + mRank = rank; return this; } @@ -527,6 +638,11 @@ public final class ShortcutInfo implements Parcelable { return mTitle; } + /** TODO Javadoc */ + public int getTitleResId() { + return mTitleResId; + } + /** * Return the shortcut text. */ @@ -535,6 +651,24 @@ public final class ShortcutInfo implements Parcelable { return mText; } + /** TODO Javadoc */ + public int getTextResId() { + return mTextResId; + } + + /** + * Return the message that should be shown when a shortcut in disabled state is launched. + */ + @Nullable + public String getDisabledMessage() { + return mDisabledMessage; + } + + /** TODO Javadoc */ + public int getDisabledMessageResId() { + return mDisabledMessageResId; + } + /** * Return the categories. */ @@ -584,11 +718,10 @@ public final class ShortcutInfo implements Parcelable { } /** - * Return the weight of a shortcut, which will be used by Launcher for sorting. - * The larger the weight, the more "important" a shortcut is. + * TODO Javadoc */ - public int getWeight() { - return mWeight; + public int getRank() { + return mRank; } /** @@ -654,6 +787,19 @@ public final class ShortcutInfo implements Parcelable { return hasFlags(FLAG_PINNED); } + /** + * Return whether a shortcut is published via manifest or not. If true, the shortcut is + * immutable. + */ + public boolean isFromManifest() { + return hasFlags(FLAG_FROM_MANIFEST); + } + + /** Return whether a shortcut is disabled by publisher or not. */ + public boolean isDisabled() { + return !hasFlags(FLAG_DISABLED); + } + /** * Return whether a shortcut's icon is a resource in the owning package. * @@ -663,6 +809,16 @@ public final class ShortcutInfo implements Parcelable { return hasFlags(FLAG_HAS_ICON_RES); } + /** @hide */ + public boolean hasStringResources() { + return (mTitleResId != 0) || (mTextResId != 0) || (mDisabledMessageResId != 0); + } + + /** @hide */ + public boolean hasAnyResources() { + return hasIconResource() || hasStringResources(); + } + /** * Return whether a shortcut's icon is stored as a file. * @@ -689,6 +845,11 @@ public final class ShortcutInfo implements Parcelable { return hasFlags(FLAG_KEY_FIELDS_ONLY); } + /** TODO Javadoc */ + public boolean hasStringResourcesResolved() { + return hasFlags(FLAG_STRINGS_RESOLVED); + } + /** @hide */ public void updateTimestamp() { mLastChangedTimestamp = System.currentTimeMillis(); @@ -736,10 +897,14 @@ public final class ShortcutInfo implements Parcelable { mActivityComponent = source.readParcelable(cl); mIcon = source.readParcelable(cl); mTitle = source.readString(); + mTitleResId = source.readInt(); mText = source.readString(); + mTextResId = source.readInt(); + mDisabledMessage = source.readString(); + mDisabledMessageResId = source.readInt(); mIntent = source.readParcelable(cl); mIntentPersistableExtras = source.readParcelable(cl); - mWeight = source.readInt(); + mRank = source.readInt(); mExtras = source.readParcelable(cl); mLastChangedTimestamp = source.readLong(); mFlags = source.readInt(); @@ -765,11 +930,15 @@ public final class ShortcutInfo implements Parcelable { dest.writeParcelable(mActivityComponent, flags); dest.writeParcelable(mIcon, flags); dest.writeString(mTitle); + dest.writeInt(mTitleResId); dest.writeString(mText); + dest.writeInt(mTextResId); + dest.writeString(mDisabledMessage); + dest.writeInt(mDisabledMessageResId); dest.writeParcelable(mIntent, flags); dest.writeParcelable(mIntentPersistableExtras, flags); - dest.writeInt(mWeight); + dest.writeInt(mRank); dest.writeParcelable(mExtras, flags); dest.writeLong(mLastChangedTimestamp); dest.writeInt(mFlags); @@ -837,9 +1006,18 @@ public final class ShortcutInfo implements Parcelable { sb.append(", title="); sb.append(secure ? "***" : mTitle); + sb.append(", titleResId="); + sb.append(mTitleResId); sb.append(", text="); sb.append(secure ? "***" : mText); + sb.append(", textResId="); + sb.append(mTextResId); + + sb.append(", disabledMessage="); + sb.append(secure ? "***" : mDisabledMessage); + sb.append(", disabledMessageResId="); + sb.append(mDisabledMessageResId); sb.append(", categories="); sb.append(mCategories); @@ -847,8 +1025,8 @@ public final class ShortcutInfo implements Parcelable { sb.append(", icon="); sb.append(mIcon); - sb.append(", weight="); - sb.append(mWeight); + sb.append(", rank="); + sb.append(mRank); sb.append(", timestamp="); sb.append(mLastChangedTimestamp); @@ -864,6 +1042,32 @@ public final class ShortcutInfo implements Parcelable { sb.append(", flags="); sb.append(mFlags); + sb.append(" ["); + if (hasFlags(FLAG_DISABLED)) { + sb.append("X"); + } + if (hasFlags(FLAG_FROM_MANIFEST)) { + sb.append("M"); + } + if (hasFlags(FLAG_DYNAMIC)) { + sb.append("D"); + } + if (hasFlags(FLAG_PINNED)) { + sb.append("P"); + } + if (hasFlags(FLAG_HAS_ICON_FILE)) { + sb.append("If"); + } + if (hasFlags(FLAG_HAS_ICON_RES)) { + sb.append("Ir"); + } + if (hasFlags(FLAG_KEY_FIELDS_ONLY)) { + sb.append("K"); + } + if (hasFlags(FLAG_STRINGS_RESOLVED)) { + sb.append("S"); + } + sb.append("]"); if (includeInternalData) { @@ -881,9 +1085,10 @@ public final class ShortcutInfo implements Parcelable { /** @hide */ public ShortcutInfo( @UserIdInt int userId, String id, String packageName, ComponentName activityComponent, - Icon icon, String title, String text, Set categories, Intent intent, + Icon icon, String title, int titleResId, String text, int textResId, + String disabledMessage, int disabledMessageResId, Set categories, Intent intent, PersistableBundle intentPersistableExtras, - int weight, PersistableBundle extras, long lastChangedTimestamp, + int rank, PersistableBundle extras, long lastChangedTimestamp, int flags, int iconResId, String bitmapPath) { mUserId = userId; mId = id; @@ -891,11 +1096,15 @@ public final class ShortcutInfo implements Parcelable { mActivityComponent = activityComponent; mIcon = icon; mTitle = title; + mTitleResId = titleResId; mText = text; + mTextResId = textResId; + mDisabledMessage = disabledMessage; + mDisabledMessageResId = disabledMessageResId; mCategories = clone(categories); mIntent = intent; mIntentPersistableExtras = intentPersistableExtras; - mWeight = weight; + mRank = rank; mExtras = extras; mLastChangedTimestamp = lastChangedTimestamp; mFlags = flags; diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index c12137ac07f23..44fa98d75a06c 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -28,6 +28,8 @@ import java.util.List; // TODO Enhance javadoc /** + * TODO: Update to reflect DR changes.
+ * * {@link ShortcutManager} manages shortcuts created by applications. * *

Dynamic shortcuts and pinned shortcuts

@@ -227,6 +229,45 @@ public class ShortcutManager { } } + /** + * TODO Javadoc + */ + public void disableShortcuts(@NonNull List shortcutIds) { + try { + mService.disableShortcuts(mContext.getPackageName(), shortcutIds, + /* disabledMessage =*/ null, /* disabledMessageResId =*/ 0, + injectMyUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * TODO Javadoc + */ + public void disableShortcuts(@NonNull List shortcutIds, int disabledMessageResId) { + try { + mService.disableShortcuts(mContext.getPackageName(), shortcutIds, + /* disabledMessage =*/ null, disabledMessageResId, + injectMyUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * TODO Javadoc + */ + public void disableShortcuts(@NonNull List shortcutIds, String disabledMessage) { + try { + mService.disableShortcuts(mContext.getPackageName(), shortcutIds, + disabledMessage, /* disabledMessageResId =*/ 0, + injectMyUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Return the max number of dynamic shortcuts that each application can have at a time. */ @@ -277,6 +318,15 @@ public class ShortcutManager { } } + public void reportShortcutUsed(String shortcutId) { + try { + mService.reportShortcutUsed(mContext.getPackageName(), shortcutId, + injectMyUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** @hide injection point */ @VisibleForTesting protected int injectMyUserId() { diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 1b086639ddd32..c298683dc0607 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -61,9 +61,13 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String ATTR_ID = "id"; private static final String ATTR_ACTIVITY = "activity"; private static final String ATTR_TITLE = "title"; + private static final String ATTR_TITLE_RES_ID = "titleid"; private static final String ATTR_TEXT = "text"; + private static final String ATTR_TEXT_RES_ID = "textid"; + private static final String ATTR_DISABLED_MESSAGE = "dmessage"; + private static final String ATTR_DISABLED_MESSAGE_RES_ID = "dmessageid"; private static final String ATTR_INTENT = "intent"; - private static final String ATTR_WEIGHT = "weight"; + private static final String ATTR_RANK = "rank"; private static final String ATTR_TIMESTAMP = "timestamp"; private static final String ATTR_FLAGS = "flags"; private static final String ATTR_ICON_RES = "icon-res"; @@ -503,7 +507,7 @@ class ShortcutPackage extends ShortcutPackageItem { for (int i = mShortcuts.size() - 1; i >= 0; i--) { final ShortcutInfo si = mShortcuts.valueAt(i); - if (si.hasIconResource()) { + if (si.hasAnyResources()) { changed = true; si.setTimestamp(s.injectCurrentTimeMillis()); } @@ -618,9 +622,13 @@ class ShortcutPackage extends ShortcutPackageItem { ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivityComponent()); // writeAttr(out, "icon", si.getIcon()); // We don't save it. ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle()); + ShortcutService.writeAttr(out, ATTR_TITLE_RES_ID, si.getTitleResId()); ShortcutService.writeAttr(out, ATTR_TEXT, si.getText()); + ShortcutService.writeAttr(out, ATTR_TEXT_RES_ID, si.getTextResId()); + ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE, si.getDisabledMessage()); + ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_ID, si.getDisabledMessageResId()); ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras()); - ShortcutService.writeAttr(out, ATTR_WEIGHT, si.getWeight()); + ShortcutService.writeAttr(out, ATTR_RANK, si.getRank()); ShortcutService.writeAttr(out, ATTR_TIMESTAMP, si.getLastChangedTimestamp()); if (forBackup) { @@ -703,10 +711,14 @@ class ShortcutPackage extends ShortcutPackageItem { ComponentName activityComponent; // Icon icon; String title; + int titleResId; String text; + int textResId; + String disabledMessage; + int disabledMessageResId; Intent intent; PersistableBundle intentPersistableExtras = null; - int weight; + int rank; PersistableBundle extras = null; long lastChangedTimestamp; int flags; @@ -718,9 +730,14 @@ class ShortcutPackage extends ShortcutPackageItem { activityComponent = ShortcutService.parseComponentNameAttribute(parser, ATTR_ACTIVITY); title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE); + titleResId = ShortcutService.parseIntAttribute(parser, ATTR_TITLE_RES_ID); text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT); + textResId = ShortcutService.parseIntAttribute(parser, ATTR_TEXT_RES_ID); + disabledMessage = ShortcutService.parseStringAttribute(parser, ATTR_DISABLED_MESSAGE); + disabledMessageResId = ShortcutService.parseIntAttribute(parser, + ATTR_DISABLED_MESSAGE_RES_ID); intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT); - weight = (int) ShortcutService.parseLongAttribute(parser, ATTR_WEIGHT); + rank = (int) ShortcutService.parseLongAttribute(parser, ATTR_RANK); lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP); flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS); iconRes = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES); @@ -765,9 +782,10 @@ class ShortcutPackage extends ShortcutPackageItem { } return new ShortcutInfo( - userId, id, packageName, activityComponent, /* icon =*/ null, title, text, + userId, id, packageName, activityComponent, /* icon =*/ null, + title, titleResId, text, textResId, disabledMessage, disabledMessageResId, categories, intent, - intentPersistableExtras, weight, extras, lastChangedTimestamp, flags, + intentPersistableExtras, rank, extras, lastChangedTimestamp, flags, iconRes, bitmapPath); } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 5219201629a93..6a10f4198c4da 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -32,6 +32,7 @@ import android.content.pm.LauncherApps; import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; @@ -110,13 +111,19 @@ import java.util.function.Predicate; /** * TODO: + * - Manifest shortcuts. + * + * - Implement disableShortcuts(). + * + * - Implement reportShortcutUsed(). + * + * - Ranks should be recalculated after each update. + * + * - When the system locale changes, update timestamps for shortcuts with string resources, + * and notify the launcher. * * - Default launcher check does take a few ms. Worth caching. * - * - Clear data -> remove all dynamic? but not the pinned? - * - * - Scan and remove orphan bitmaps (just in case). - * * - Detect when already registered instances are passed to APIs again, which might break * internal bitmap handling. * @@ -1514,6 +1521,18 @@ public class ShortcutService extends IShortcutService.Stub { return true; } + @Override + public void disableShortcuts(String packageName, List shortcutIds, + String disabledMessage, int disabledMessageResId, @UserIdInt int userId) { + verifyCaller(packageName, userId); + Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); + + synchronized (mLock) { + // TODO implement it. + } + packageShortcutsChanged(packageName, userId); + } + @Override public void removeDynamicShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) { @@ -1599,7 +1618,7 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public int getIconMaxDimensions(String packageName, int userId) throws RemoteException { + public int getIconMaxDimensions(String packageName, int userId) { verifyCaller(packageName, userId); synchronized (mLock) { @@ -1607,6 +1626,13 @@ public class ShortcutService extends IShortcutService.Stub { } } + @Override + public void reportShortcutUsed(String packageName, String shortcutId, int userId) { + verifyCaller(packageName, userId); + + // TODO Implement it. + } + /** * Reset all throttling, for developer options and command line. Only system/shell can call it. */ @@ -1792,10 +1818,10 @@ public class ShortcutService extends IShortcutService.Stub { @Nullable ComponentName componentName, int queryFlags, int userId) { final ArrayList ret = new ArrayList<>(); - final int cloneFlag = - ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0) - ? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER - : ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO; + final boolean cloneKeyFieldOnly = + ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0); + final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO + : ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER; if (packageName == null) { shortcutIds = null; // LauncherAppsService already threw for it though. } @@ -1817,6 +1843,21 @@ public class ShortcutService extends IShortcutService.Stub { }); } } + // Resolve all strings if needed. + if (!cloneKeyFieldOnly) { + final long token = injectClearCallingIdentity(); + try { + for (int i = ret.size() - 1; i >= 0; i--) { + try { + ret.get(i).resolveStringsRequiringCrossUser(mContext); + } catch (NameNotFoundException e) { + continue; + } + } + } finally { + injectRestoreCallingIdentity(token); + } + } return ret; } diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java index 918fdabb313dd..8a2a58cc90194 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java @@ -27,6 +27,7 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotHaveTitle; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotKeyFieldsOnly; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllPinned; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllStringsResolved; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllUnique; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBitmapSize; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundleEmpty; @@ -945,11 +946,12 @@ public class ShortcutManagerTest extends InstrumentationTestCase { * Make a shortcut with details. */ private ShortcutInfo makeShortcut(String id, String title, ComponentName activity, - Icon icon, Intent intent, int weight) { + Icon icon, Intent intent, int rank) { final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext) .setId(id) + .setActivityComponent(new ComponentName(mClientContext.getPackageName(), "dummy")) .setTitle(title) - .setWeight(weight) + .setRank(rank) .setIntent(intent); if (icon != null) { b.setIcon(icon); @@ -2269,11 +2271,12 @@ public class ShortcutManagerTest extends InstrumentationTestCase { setCaller(LAUNCHER_1); // Get dynamic - assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds( + assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertAllStringsResolved( + assertShortcutIds( assertAllNotKeyFieldsOnly( mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())), - "s1", "s2")))); + "s1", "s2"))))); // Get pinned assertShortcutIds( @@ -2307,7 +2310,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()), ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())), - "s2", "s4")))); + "s4")))); // With ID. assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds( @@ -2371,6 +2374,70 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // TODO More tests: pinned but dynamic. } + public void testGetShortcuts_resolveStrings() throws Exception { + doAnswer(pmInvocation -> { + assertEquals(Process.SYSTEM_UID, mInjectedCallingUid); + + final String packageName = (String) pmInvocation.getArguments()[0]; + final int userId = (Integer) pmInvocation.getArguments()[1]; + + final Resources res = mock(Resources.class); + doAnswer(resInvocation -> { + final int resId = (Integer) resInvocation.getArguments()[0]; + + return "string-" + packageName + "-user:" + userId + "-res:" + resId; + }).when(res).getString(anyInt()); + return res; + }).when(mMockPackageManager).getResourcesForApplicationAsUser(anyString(), anyInt()); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + ShortcutInfo si = new ShortcutInfo.Builder(mClientContext) + .setId("id") + .setActivityComponent(new ComponentName(mClientContext, "dummy")) + .setTitleResId(10) + .setTextResId(11) + .setDisabledMessageResId(12) + .setIntent(makeIntent("action", ShortcutActivity.class)) + .build(); + mManager.setDynamicShortcuts(list(si)); + }); + runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { + ShortcutInfo si = new ShortcutInfo.Builder(mClientContext) + .setId("id") + .setActivityComponent(new ComponentName(mClientContext, "dummy")) + .setTitleResId(10) + .setTextResId(11) + .setDisabledMessageResId(12) + .setIntent(makeIntent("action", ShortcutActivity.class)) + .build(); + mManager.setDynamicShortcuts(list(si)); + }); + + runWithCaller(LAUNCHER_1, USER_0, () -> { + final ShortcutQuery q = new ShortcutQuery(); + q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC); + + // USER 0 + List ret = assertShortcutIds( + assertAllStringsResolved(mLauncherApps.getShortcuts(q, HANDLE_USER_0)), + "id"); + assertEquals("string-com.android.test.1-user:0-res:10", ret.get(0).getTitle()); + assertEquals("string-com.android.test.1-user:0-res:11", ret.get(0).getText()); + assertEquals("string-com.android.test.1-user:0-res:12", + ret.get(0).getDisabledMessage()); + + // USER P0 + ret = assertShortcutIds( + assertAllStringsResolved(mLauncherApps.getShortcuts(q, HANDLE_USER_P0)), + "id"); + assertEquals("string-com.android.test.1-user:20-res:10", ret.get(0).getTitle()); + assertEquals("string-com.android.test.1-user:20-res:11", ret.get(0).getText()); + assertEquals("string-com.android.test.1-user:20-res:12", + ret.get(0).getDisabledMessage()); + }); + } + + // TODO resource public void testGetShortcutInfo() { // Create shortcuts. setCaller(CALLING_PACKAGE_1); @@ -5938,15 +6005,26 @@ public class ShortcutManagerTest extends InstrumentationTestCase { NullPointerException.class, "Intent action must be set", () -> new ShortcutInfo.Builder(getTestContext()).setIntent(new Intent())); + assertExpectException( + NullPointerException.class, + "activityComponent must be provided", + () -> new ShortcutInfo.Builder(getTestContext()).setId("id").build() + .enforceMandatoryFields()); assertExpectException( IllegalArgumentException.class, "title must be provided", - () -> new ShortcutInfo.Builder(getTestContext()).setId("id").build() + () -> new ShortcutInfo.Builder(getTestContext()).setId("id") + .setActivityComponent( + new ComponentName(getTestContext().getPackageName(), "s")) + .build() .enforceMandatoryFields()); assertExpectException( NullPointerException.class, "Intent must be provided", - () -> new ShortcutInfo.Builder(getTestContext()).setId("id").setTitle("x").build() + () -> new ShortcutInfo.Builder(getTestContext()).setId("id") + .setActivityComponent( + new ComponentName(getTestContext().getPackageName(), "s")) + .setTitle("x").build() .enforceMandatoryFields()); } @@ -5973,9 +6051,10 @@ public class ShortcutManagerTest extends InstrumentationTestCase { .setIcon(Icon.createWithResource(mClientContext, 123)) .setTitle("title") .setText("text") + .setDisabledMessage("dismes") .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) - .setWeight(123) + .setRank(123) .setExtras(pb) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); @@ -5990,10 +6069,54 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertEquals(123, si.getIcon().getResId()); assertEquals("title", si.getTitle()); assertEquals("text", si.getText()); + assertEquals("dismes", si.getDisabledMessage()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); - assertEquals(123, si.getWeight()); + assertEquals(123, si.getRank()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals("abc", si.getBitmapPath()); + assertEquals(456, si.getIconResourceId()); + } + + public void testShortcutInfoParcel_resId() { + setCaller(CALLING_PACKAGE_1, USER_10); + ShortcutInfo si; + + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + + si = new ShortcutInfo.Builder(getTestContext()) + .setId("id") + .setActivityComponent(new ComponentName("a", "b")) + .setIcon(Icon.createWithResource(mClientContext, 123)) + .setTitleResId(10) + .setTextResId(11) + .setDisabledMessageResId(12) + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) + .setRank(123) + .setExtras(pb) + .build(); + si.addFlags(ShortcutInfo.FLAG_PINNED); + si.setBitmapPath("abc"); + si.setIconResourceId(456); + + si = parceled(si); + + assertEquals(getTestContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); + assertEquals(123, si.getIcon().getResId()); + assertEquals(10, si.getTitleResId()); + assertEquals(11, si.getTextResId()); + assertEquals(12, si.getDisabledMessageResId()); + assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); @@ -6012,9 +6135,10 @@ public class ShortcutManagerTest extends InstrumentationTestCase { .setIcon(Icon.createWithResource(mClientContext, 123)) .setTitle("title") .setText("text") + .setDisabledMessage("dismes") .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) - .setWeight(123) + .setRank(123) .setExtras(pb) .build(); sorig.addFlags(ShortcutInfo.FLAG_PINNED); @@ -6031,10 +6155,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertEquals(123, si.getIcon().getResId()); assertEquals("title", si.getTitle()); assertEquals("text", si.getText()); + assertEquals("dismes", si.getDisabledMessage()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); - assertEquals(123, si.getWeight()); + assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); @@ -6049,10 +6174,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertEquals(null, si.getIcon()); assertEquals("title", si.getTitle()); assertEquals("text", si.getText()); + assertEquals("dismes", si.getDisabledMessage()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); - assertEquals(123, si.getWeight()); + assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); @@ -6068,9 +6194,10 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertEquals(null, si.getIcon()); assertEquals("title", si.getTitle()); assertEquals("text", si.getText()); + assertEquals("dismes", si.getDisabledMessage()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals(null, si.getIntent()); - assertEquals(123, si.getWeight()); + assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); @@ -6086,9 +6213,111 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertEquals(null, si.getIcon()); assertEquals(null, si.getTitle()); assertEquals(null, si.getText()); + assertEquals(null, si.getDisabledMessage()); assertEquals(null, si.getCategories()); assertEquals(null, si.getIntent()); - assertEquals(0, si.getWeight()); + assertEquals(0, si.getRank()); + assertEquals(null, si.getExtras()); + + assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags()); + assertEquals(null, si.getBitmapPath()); + + assertEquals(456, si.getIconResourceId()); + } + + public void testShortcutInfoClone_resId() { + setCaller(CALLING_PACKAGE_1, USER_11); + + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) + .setId("id") + .setActivityComponent(new ComponentName("a", "b")) + .setIcon(Icon.createWithResource(mClientContext, 123)) + .setTitleResId(10) + .setTextResId(11) + .setDisabledMessageResId(12) + .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setRank(123) + .setExtras(pb) + .build(); + sorig.addFlags(ShortcutInfo.FLAG_PINNED); + sorig.setBitmapPath("abc"); + sorig.setIconResourceId(456); + + ShortcutInfo si = sorig.clone(/* clone flags*/ 0); + + assertEquals(USER_11, si.getUserId()); + assertEquals(HANDLE_USER_11, si.getUserHandle()); + assertEquals(mClientContext.getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); + assertEquals(123, si.getIcon().getResId()); + assertEquals(10, si.getTitleResId()); + assertEquals(11, si.getTextResId()); + assertEquals(12, si.getDisabledMessageResId()); + assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getRank()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals("abc", si.getBitmapPath()); + assertEquals(456, si.getIconResourceId()); + + si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); + + assertEquals(mClientContext.getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); + assertEquals(null, si.getIcon()); + assertEquals(10, si.getTitleResId()); + assertEquals(11, si.getTextResId()); + assertEquals(12, si.getDisabledMessageResId()); + assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getRank()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals(null, si.getBitmapPath()); + + assertEquals(456, si.getIconResourceId()); + + si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); + + assertEquals(mClientContext.getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); + assertEquals(null, si.getIcon()); + assertEquals(10, si.getTitleResId()); + assertEquals(11, si.getTextResId()); + assertEquals(12, si.getDisabledMessageResId()); + assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); + assertEquals(null, si.getIntent()); + assertEquals(123, si.getRank()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals(null, si.getBitmapPath()); + + assertEquals(456, si.getIconResourceId()); + + si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO); + + assertEquals(mClientContext.getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(null, si.getActivityComponent()); + assertEquals(null, si.getIcon()); + assertEquals(0, si.getTitleResId()); + assertEquals(0, si.getTextResId()); + assertEquals(0, si.getDisabledMessageResId()); + assertEquals(null, si.getCategories()); + assertEquals(null, si.getIntent()); + assertEquals(0, si.getRank()); assertEquals(null, si.getExtras()); assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags()); @@ -6147,9 +6376,10 @@ public class ShortcutManagerTest extends InstrumentationTestCase { .setIcon(Icon.createWithResource(mClientContext, 123)) .setTitle("title") .setText("text") + .setDisabledMessage("dismes") .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) - .setWeight(123) + .setRank(123) .setExtras(pb) .build(); sorig.addFlags(ShortcutInfo.FLAG_PINNED); @@ -6175,12 +6405,42 @@ public class ShortcutManagerTest extends InstrumentationTestCase { .setTitle("xyz").build()); assertEquals("text", si.getText()); assertEquals("xyz", si.getTitle()); + assertEquals(0, si.getTitleResId()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setTitleResId(123).build()); + assertEquals("text", si.getText()); + assertEquals(null, si.getTitle()); + assertEquals(123, si.getTitleResId()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") .setText("xxx").build()); - assertEquals(123, si.getWeight()); + assertEquals(123, si.getRank()); assertEquals("xxx", si.getText()); + assertEquals(0, si.getTextResId()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setTextResId(1111).build()); + assertEquals(123, si.getRank()); + assertEquals(null, si.getText()); + assertEquals(1111, si.getTextResId()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setDisabledMessage("xxx").build()); + assertEquals(123, si.getRank()); + assertEquals("xxx", si.getDisabledMessage()); + assertEquals(0, si.getDisabledMessageResId()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setDisabledMessageResId(11111).build()); + assertEquals(123, si.getRank()); + assertEquals(null, si.getDisabledMessage()); + assertEquals(11111, si.getDisabledMessageResId()); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") @@ -6210,9 +6470,9 @@ public class ShortcutManagerTest extends InstrumentationTestCase { si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") - .setWeight(999).build()); + .setRank(999).build()); assertEquals("text", si.getText()); - assertEquals(999, si.getWeight()); + assertEquals(999, si.getRank()); PersistableBundle pb2 = new PersistableBundle(); @@ -6235,6 +6495,134 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertTrue(si.getLastChangedTimestamp() > timestamp); } + public void testShortcutInfoCopyNonNullFieldsFrom_resId() throws InterruptedException { + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext()) + .setId("id") + .setActivityComponent(new ComponentName("a", "b")) + .setIcon(Icon.createWithResource(mClientContext, 123)) + .setTitleResId(10) + .setTextResId(11) + .setDisabledMessageResId(12) + .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setRank(123) + .setExtras(pb) + .build(); + sorig.addFlags(ShortcutInfo.FLAG_PINNED); + sorig.setBitmapPath("abc"); + sorig.setIconResourceId(456); + + ShortcutInfo si; + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setActivityComponent(new ComponentName("x", "y")).build()); + assertEquals(11, si.getTextResId()); + assertEquals(new ComponentName("x", "y"), si.getActivityComponent()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setIcon(Icon.createWithResource(mClientContext, 456)).build()); + assertEquals(11, si.getTextResId()); + assertEquals(456, si.getIcon().getResId()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setTitle("xyz").build()); + assertEquals(11, si.getTextResId()); + assertEquals("xyz", si.getTitle()); + assertEquals(0, si.getTitleResId()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setTitleResId(123).build()); + assertEquals(11, si.getTextResId()); + assertEquals(null, si.getTitle()); + assertEquals(123, si.getTitleResId()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setText("xxx").build()); + assertEquals(123, si.getRank()); + assertEquals("xxx", si.getText()); + assertEquals(0, si.getTextResId()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setTextResId(1111).build()); + assertEquals(123, si.getRank()); + assertEquals(null, si.getText()); + assertEquals(1111, si.getTextResId()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setDisabledMessage("xxx").build()); + assertEquals(123, si.getRank()); + assertEquals("xxx", si.getDisabledMessage()); + assertEquals(0, si.getDisabledMessageResId()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setDisabledMessageResId(11111).build()); + assertEquals(123, si.getRank()); + assertEquals(null, si.getDisabledMessage()); + assertEquals(11111, si.getDisabledMessageResId()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setCategories(set()).build()); + assertEquals(11, si.getTextResId()); + assertEquals(set(), si.getCategories()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setCategories(set("x")).build()); + assertEquals(11, si.getTextResId()); + assertEquals(set("x"), si.getCategories()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setIntent(makeIntent("action2", ShortcutActivity.class)).build()); + assertEquals(11, si.getTextResId()); + assertEquals("action2", si.getIntent().getAction()); + assertEquals(null, si.getIntent().getStringExtra("key")); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setIntent(makeIntent("action3", ShortcutActivity.class, "key", "x")).build()); + assertEquals(11, si.getTextResId()); + assertEquals("action3", si.getIntent().getAction()); + assertEquals("x", si.getIntent().getStringExtra("key")); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setRank(999).build()); + assertEquals(11, si.getTextResId()); + assertEquals(999, si.getRank()); + + + PersistableBundle pb2 = new PersistableBundle(); + pb2.putInt("x", 99); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setExtras(pb2).build()); + assertEquals(11, si.getTextResId()); + assertEquals(99, si.getExtras().getInt("x")); + + // Make sure the timestamp gets updated too. + + final long timestamp = si.getLastChangedTimestamp(); + Thread.sleep(2); + + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setTitle("xyz").build()); + + assertTrue(si.getLastChangedTimestamp() > timestamp); + } + public void testShortcutInfoSaveAndLoad() throws InterruptedException { setCaller(CALLING_PACKAGE_1, USER_10); @@ -6249,9 +6637,10 @@ public class ShortcutManagerTest extends InstrumentationTestCase { .setIcon(bmp32x32) .setTitle("title") .setText("text") + .setDisabledMessage("dismes") .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) - .setWeight(123) + .setRank(123) .setExtras(pb) .build(); @@ -6276,10 +6665,66 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertEquals(null, si.getIcon()); assertEquals("title", si.getTitle()); assertEquals("text", si.getText()); + assertEquals("dismes", si.getDisabledMessage()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); - assertEquals(123, si.getWeight()); + assertEquals(123, si.getRank()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE, si.getFlags()); + assertNotNull(si.getBitmapPath()); // Something should be set. + assertEquals(0, si.getIconResourceId()); + assertTrue(si.getLastChangedTimestamp() < now); + } + + public void testShortcutInfoSaveAndLoad_resId() throws InterruptedException { + setCaller(CALLING_PACKAGE_1, USER_10); + + final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource( + getTestContext().getResources(), R.drawable.black_32x32)); + + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) + .setId("id") + .setActivityComponent(new ComponentName(mClientContext, ShortcutActivity2.class)) + .setIcon(bmp32x32) + .setTitleResId(10) + .setTextResId(11) + .setDisabledMessageResId(12) + .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setRank(123) + .setExtras(pb) + .build(); + + mManager.addDynamicShortcuts(list(sorig)); + + Thread.sleep(2); + final long now = System.currentTimeMillis(); + + // Save and load. + mService.saveDirtyInfo(); + initService(); + mService.handleUnlockUser(USER_10); + + ShortcutInfo si; + si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10); + + assertEquals(USER_10, si.getUserId()); + assertEquals(HANDLE_USER_10, si.getUserHandle()); + assertEquals(CALLING_PACKAGE_1, si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName()); + assertEquals(null, si.getIcon()); + assertEquals(10, si.getTitleResId()); + assertEquals(11, si.getTextResId()); + assertEquals(12, si.getDisabledMessageResId()); + assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE, si.getFlags()); @@ -6302,9 +6747,10 @@ public class ShortcutManagerTest extends InstrumentationTestCase { .setIcon(bmp32x32) .setTitle("title") .setText("text") + .setDisabledMessage("dismes") .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) - .setWeight(123) + .setRank(123) .setExtras(pb) .build(); @@ -6328,10 +6774,64 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertEquals(null, si.getIcon()); assertEquals("title", si.getTitle()); assertEquals("text", si.getText()); + assertEquals("dismes", si.getDisabledMessage()); assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); - assertEquals(123, si.getWeight()); + assertEquals(123, si.getRank()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertNull(si.getBitmapPath()); // No icon. + assertEquals(0, si.getIconResourceId()); + } + + public void testShortcutInfoSaveAndLoad_forBackup_resId() { + setCaller(CALLING_PACKAGE_1, USER_0); + + final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource( + getTestContext().getResources(), R.drawable.black_32x32)); + + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) + .setId("id") + .setActivityComponent(new ComponentName(mClientContext, ShortcutActivity2.class)) + .setIcon(bmp32x32) + .setTitleResId(10) + .setTextResId(11) + .setDisabledMessageResId(12) + .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setRank(123) + .setExtras(pb) + .build(); + + mManager.addDynamicShortcuts(list(sorig)); + + // Dynamic shortcuts won't be backed up, so we need to pin it. + setCaller(LAUNCHER_1, USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("id"), HANDLE_USER_0); + + // Do backup & restore. + backupAndRestore(); + + mService.handleUnlockUser(USER_0); // Load user-0. + + ShortcutInfo si; + si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0); + + assertEquals(CALLING_PACKAGE_1, si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName()); + assertEquals(null, si.getIcon()); + assertEquals(10, si.getTitleResId()); + assertEquals(11, si.getTextResId()); + assertEquals(12, si.getDisabledMessageResId()); + assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java index ad49c2ff78e8f..c23f9e65a1fa4 100644 --- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java +++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java @@ -383,6 +383,14 @@ public class ShortcutManagerTestUtils { return actualShortcuts; } + public static List assertAllStringsResolved( + List actualShortcuts) { + for (ShortcutInfo s : actualShortcuts) { + assertTrue("ID " + s.getId(), s.hasStringResourcesResolved()); + } + return actualShortcuts; + } + public static void assertDynamicOnly(ShortcutInfo si) { assertTrue(si.isDynamic()); assertFalse(si.isPinned());