Update manifest shortcut XML schema
Use the standard <intent> tag instead of custom tags. - Also fix setDynamicShortcuts(), which was broken in the previous CL. - Also tolerate runtime exceptions while parsing XMLs - Also fix b/29422018 while I'm at it Bug 29390156 Bug 29077932 Bug 29422018 Change-Id: I2756c9d66c6d7b2962a982d9e57a7d84a5755b28
This commit is contained in:
@@ -420,9 +420,9 @@ package android {
|
||||
field public static final int contentInsetStart = 16843859; // 0x1010453
|
||||
field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
|
||||
field public static final int contextClickable = 16844007; // 0x10104e7
|
||||
field public static final int contextDescription = 16844082; // 0x1010532
|
||||
field public static final int contextDescription = 16844078; // 0x101052e
|
||||
field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
|
||||
field public static final int contextUri = 16844081; // 0x1010531
|
||||
field public static final int contextUri = 16844077; // 0x101052d
|
||||
field public static final int controlX1 = 16843772; // 0x10103fc
|
||||
field public static final int controlX2 = 16843774; // 0x10103fe
|
||||
field public static final int controlY1 = 16843773; // 0x10103fd
|
||||
@@ -1041,7 +1041,7 @@ package android {
|
||||
field public static final int rotation = 16843558; // 0x1010326
|
||||
field public static final int rotationX = 16843559; // 0x1010327
|
||||
field public static final int rotationY = 16843560; // 0x1010328
|
||||
field public static final int roundIcon = 16844080; // 0x1010530
|
||||
field public static final int roundIcon = 16844076; // 0x101052c
|
||||
field public static final int rowCount = 16843637; // 0x1010375
|
||||
field public static final int rowDelay = 16843216; // 0x10101d0
|
||||
field public static final int rowEdgeFlags = 16843329; // 0x1010241
|
||||
@@ -1109,20 +1109,16 @@ package android {
|
||||
field public static final int shareInterpolator = 16843195; // 0x10101bb
|
||||
field public static final int sharedUserId = 16842763; // 0x101000b
|
||||
field public static final int sharedUserLabel = 16843361; // 0x1010261
|
||||
field public static final int shortcutCategories = 16844077; // 0x101052d
|
||||
field public static final int shortcutDisabledMessage = 16844076; // 0x101052c
|
||||
field public static final int shortcutIcon = 16844073; // 0x1010529
|
||||
field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
|
||||
field public static final int shortcutId = 16844072; // 0x1010528
|
||||
field public static final int shortcutIntentAction = 16844078; // 0x101052e
|
||||
field public static final int shortcutIntentData = 16844079; // 0x101052f
|
||||
field public static final int shortcutLongLabel = 16844075; // 0x101052b
|
||||
field public static final int shortcutShortLabel = 16844074; // 0x101052a
|
||||
field public static final int shortcutLongLabel = 16844074; // 0x101052a
|
||||
field public static final int shortcutShortLabel = 16844073; // 0x1010529
|
||||
field public static final int shouldDisableView = 16843246; // 0x10101ee
|
||||
field public static final int showAsAction = 16843481; // 0x10102d9
|
||||
field public static final int showDefault = 16843258; // 0x10101fa
|
||||
field public static final int showDividers = 16843561; // 0x1010329
|
||||
field public static final int showForAllUsers = 16844015; // 0x10104ef
|
||||
field public static final int showMetadataInPreview = 16844083; // 0x1010533
|
||||
field public static final int showMetadataInPreview = 16844079; // 0x101052f
|
||||
field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9
|
||||
field public static final int showSilent = 16843259; // 0x10101fb
|
||||
field public static final int showText = 16843949; // 0x10104ad
|
||||
@@ -10099,14 +10095,14 @@ package android.content.pm {
|
||||
method public android.content.pm.ShortcutInfo build();
|
||||
method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
|
||||
method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
|
||||
method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.String);
|
||||
method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
|
||||
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 setLongLabel(java.lang.String);
|
||||
method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
|
||||
method public android.content.pm.ShortcutInfo.Builder setRank(int);
|
||||
method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.String);
|
||||
method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence);
|
||||
}
|
||||
|
||||
public class ShortcutManager {
|
||||
|
||||
@@ -527,9 +527,9 @@ package android {
|
||||
field public static final int contentInsetStart = 16843859; // 0x1010453
|
||||
field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
|
||||
field public static final int contextClickable = 16844007; // 0x10104e7
|
||||
field public static final int contextDescription = 16844082; // 0x1010532
|
||||
field public static final int contextDescription = 16844078; // 0x101052e
|
||||
field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
|
||||
field public static final int contextUri = 16844081; // 0x1010531
|
||||
field public static final int contextUri = 16844077; // 0x101052d
|
||||
field public static final int controlX1 = 16843772; // 0x10103fc
|
||||
field public static final int controlX2 = 16843774; // 0x10103fe
|
||||
field public static final int controlY1 = 16843773; // 0x10103fd
|
||||
@@ -1148,7 +1148,7 @@ package android {
|
||||
field public static final int rotation = 16843558; // 0x1010326
|
||||
field public static final int rotationX = 16843559; // 0x1010327
|
||||
field public static final int rotationY = 16843560; // 0x1010328
|
||||
field public static final int roundIcon = 16844080; // 0x1010530
|
||||
field public static final int roundIcon = 16844076; // 0x101052c
|
||||
field public static final int rowCount = 16843637; // 0x1010375
|
||||
field public static final int rowDelay = 16843216; // 0x10101d0
|
||||
field public static final int rowEdgeFlags = 16843329; // 0x1010241
|
||||
@@ -1220,20 +1220,16 @@ package android {
|
||||
field public static final int shareInterpolator = 16843195; // 0x10101bb
|
||||
field public static final int sharedUserId = 16842763; // 0x101000b
|
||||
field public static final int sharedUserLabel = 16843361; // 0x1010261
|
||||
field public static final int shortcutCategories = 16844077; // 0x101052d
|
||||
field public static final int shortcutDisabledMessage = 16844076; // 0x101052c
|
||||
field public static final int shortcutIcon = 16844073; // 0x1010529
|
||||
field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
|
||||
field public static final int shortcutId = 16844072; // 0x1010528
|
||||
field public static final int shortcutIntentAction = 16844078; // 0x101052e
|
||||
field public static final int shortcutIntentData = 16844079; // 0x101052f
|
||||
field public static final int shortcutLongLabel = 16844075; // 0x101052b
|
||||
field public static final int shortcutShortLabel = 16844074; // 0x101052a
|
||||
field public static final int shortcutLongLabel = 16844074; // 0x101052a
|
||||
field public static final int shortcutShortLabel = 16844073; // 0x1010529
|
||||
field public static final int shouldDisableView = 16843246; // 0x10101ee
|
||||
field public static final int showAsAction = 16843481; // 0x10102d9
|
||||
field public static final int showDefault = 16843258; // 0x10101fa
|
||||
field public static final int showDividers = 16843561; // 0x1010329
|
||||
field public static final int showForAllUsers = 16844015; // 0x10104ef
|
||||
field public static final int showMetadataInPreview = 16844083; // 0x1010533
|
||||
field public static final int showMetadataInPreview = 16844079; // 0x101052f
|
||||
field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9
|
||||
field public static final int showSilent = 16843259; // 0x10101fb
|
||||
field public static final int showText = 16843949; // 0x10104ad
|
||||
@@ -10523,14 +10519,14 @@ package android.content.pm {
|
||||
method public android.content.pm.ShortcutInfo build();
|
||||
method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
|
||||
method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
|
||||
method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.String);
|
||||
method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
|
||||
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 setLongLabel(java.lang.String);
|
||||
method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
|
||||
method public android.content.pm.ShortcutInfo.Builder setRank(int);
|
||||
method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.String);
|
||||
method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence);
|
||||
}
|
||||
|
||||
public class ShortcutManager {
|
||||
|
||||
@@ -420,9 +420,9 @@ package android {
|
||||
field public static final int contentInsetStart = 16843859; // 0x1010453
|
||||
field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
|
||||
field public static final int contextClickable = 16844007; // 0x10104e7
|
||||
field public static final int contextDescription = 16844082; // 0x1010532
|
||||
field public static final int contextDescription = 16844078; // 0x101052e
|
||||
field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
|
||||
field public static final int contextUri = 16844081; // 0x1010531
|
||||
field public static final int contextUri = 16844077; // 0x101052d
|
||||
field public static final int controlX1 = 16843772; // 0x10103fc
|
||||
field public static final int controlX2 = 16843774; // 0x10103fe
|
||||
field public static final int controlY1 = 16843773; // 0x10103fd
|
||||
@@ -1041,7 +1041,7 @@ package android {
|
||||
field public static final int rotation = 16843558; // 0x1010326
|
||||
field public static final int rotationX = 16843559; // 0x1010327
|
||||
field public static final int rotationY = 16843560; // 0x1010328
|
||||
field public static final int roundIcon = 16844080; // 0x1010530
|
||||
field public static final int roundIcon = 16844076; // 0x101052c
|
||||
field public static final int rowCount = 16843637; // 0x1010375
|
||||
field public static final int rowDelay = 16843216; // 0x10101d0
|
||||
field public static final int rowEdgeFlags = 16843329; // 0x1010241
|
||||
@@ -1109,20 +1109,16 @@ package android {
|
||||
field public static final int shareInterpolator = 16843195; // 0x10101bb
|
||||
field public static final int sharedUserId = 16842763; // 0x101000b
|
||||
field public static final int sharedUserLabel = 16843361; // 0x1010261
|
||||
field public static final int shortcutCategories = 16844077; // 0x101052d
|
||||
field public static final int shortcutDisabledMessage = 16844076; // 0x101052c
|
||||
field public static final int shortcutIcon = 16844073; // 0x1010529
|
||||
field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
|
||||
field public static final int shortcutId = 16844072; // 0x1010528
|
||||
field public static final int shortcutIntentAction = 16844078; // 0x101052e
|
||||
field public static final int shortcutIntentData = 16844079; // 0x101052f
|
||||
field public static final int shortcutLongLabel = 16844075; // 0x101052b
|
||||
field public static final int shortcutShortLabel = 16844074; // 0x101052a
|
||||
field public static final int shortcutLongLabel = 16844074; // 0x101052a
|
||||
field public static final int shortcutShortLabel = 16844073; // 0x1010529
|
||||
field public static final int shouldDisableView = 16843246; // 0x10101ee
|
||||
field public static final int showAsAction = 16843481; // 0x10102d9
|
||||
field public static final int showDefault = 16843258; // 0x10101fa
|
||||
field public static final int showDividers = 16843561; // 0x1010329
|
||||
field public static final int showForAllUsers = 16844015; // 0x10104ef
|
||||
field public static final int showMetadataInPreview = 16844083; // 0x1010533
|
||||
field public static final int showMetadataInPreview = 16844079; // 0x101052f
|
||||
field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9
|
||||
field public static final int showSilent = 16843259; // 0x10101fb
|
||||
field public static final int showText = 16843949; // 0x10104ad
|
||||
@@ -10112,14 +10108,14 @@ package android.content.pm {
|
||||
method public android.content.pm.ShortcutInfo build();
|
||||
method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
|
||||
method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
|
||||
method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.String);
|
||||
method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
|
||||
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 setLongLabel(java.lang.String);
|
||||
method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
|
||||
method public android.content.pm.ShortcutInfo.Builder setRank(int);
|
||||
method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.String);
|
||||
method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence);
|
||||
}
|
||||
|
||||
public class ShortcutManager {
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -245,7 +246,7 @@ public final class ShortcutInfo implements Parcelable {
|
||||
mTextResId = b.mTextResId;
|
||||
mDisabledMessage = b.mDisabledMessage;
|
||||
mDisabledMessageResId = b.mDisabledMessageResId;
|
||||
mCategories = clone(b.mCategories);
|
||||
mCategories = cloneCategories(b.mCategories);
|
||||
mIntent = b.mIntent;
|
||||
if (mIntent != null) {
|
||||
final Bundle intentExtras = mIntent.getExtras();
|
||||
@@ -259,8 +260,17 @@ public final class ShortcutInfo implements Parcelable {
|
||||
updateTimestamp();
|
||||
}
|
||||
|
||||
private <T> ArraySet<T> clone(Set<T> source) {
|
||||
return (source == null) ? null : new ArraySet<>(source);
|
||||
private ArraySet<String> cloneCategories(Set<String> source) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
final ArraySet<String> ret = new ArraySet<>(source.size());
|
||||
for (CharSequence s : source) {
|
||||
if (!TextUtils.isEmpty(s)) {
|
||||
ret.add(s.toString().intern());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,7 +314,7 @@ public final class ShortcutInfo implements Parcelable {
|
||||
mTextResId = source.mTextResId;
|
||||
mDisabledMessage = source.mDisabledMessage;
|
||||
mDisabledMessageResId = source.mDisabledMessageResId;
|
||||
mCategories = clone(source.mCategories);
|
||||
mCategories = cloneCategories(source.mCategories);
|
||||
if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
|
||||
mIntent = source.mIntent;
|
||||
mIntentPersistableExtras = source.mIntentPersistableExtras;
|
||||
@@ -614,7 +624,7 @@ public final class ShortcutInfo implements Parcelable {
|
||||
mDisabledMessageResName = null;
|
||||
}
|
||||
if (source.mCategories != null) {
|
||||
mCategories = clone(source.mCategories);
|
||||
mCategories = cloneCategories(source.mCategories);
|
||||
}
|
||||
if (source.mIntent != null) {
|
||||
mIntent = source.mIntent;
|
||||
@@ -752,7 +762,7 @@ public final class ShortcutInfo implements Parcelable {
|
||||
* an icon. The recommend max length is 10 characters.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setShortLabel(@NonNull String shortLabel) {
|
||||
public Builder setShortLabel(@NonNull CharSequence shortLabel) {
|
||||
Preconditions.checkState(mTitleResId == 0, "shortLabelResId already set");
|
||||
mTitle = Preconditions.checkStringNotEmpty(shortLabel, "shortLabel");
|
||||
return this;
|
||||
@@ -776,14 +786,14 @@ public final class ShortcutInfo implements Parcelable {
|
||||
* The recommend max length is 25 characters.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setLongLabel(@NonNull String longLabel) {
|
||||
public Builder setLongLabel(@NonNull CharSequence longLabel) {
|
||||
Preconditions.checkState(mTextResId == 0, "longLabelResId already set");
|
||||
mText = Preconditions.checkStringNotEmpty(longLabel, "longLabel");
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @hide -- old signature, the internal code still uses it. */
|
||||
public Builder setTitle(@NonNull String value) {
|
||||
public Builder setTitle(@NonNull CharSequence value) {
|
||||
return setShortLabel(value);
|
||||
}
|
||||
|
||||
@@ -793,7 +803,7 @@ public final class ShortcutInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/** @hide -- old signature, the internal code still uses it. */
|
||||
public Builder setText(@NonNull String value) {
|
||||
public Builder setText(@NonNull CharSequence value) {
|
||||
return setLongLabel(value);
|
||||
}
|
||||
|
||||
@@ -813,7 +823,7 @@ public final class ShortcutInfo implements Parcelable {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder setDisabledMessage(@NonNull String disabledMessage) {
|
||||
public Builder setDisabledMessage(@NonNull CharSequence disabledMessage) {
|
||||
Preconditions.checkState(
|
||||
mDisabledMessageResId == 0, "disabledMessageResId already set");
|
||||
mDisabledMessage =
|
||||
@@ -1355,6 +1365,37 @@ public final class ShortcutInfo implements Parcelable {
|
||||
mIconResName = iconResName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the intent
|
||||
*
|
||||
* @throws IllegalArgumentException when extra is not compatible with {@link PersistableBundle}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setIntent(Intent intent) throws IllegalArgumentException {
|
||||
Preconditions.checkNotNull(intent);
|
||||
|
||||
final Bundle intentExtras = intent.getExtras();
|
||||
|
||||
mIntent = intent;
|
||||
|
||||
if (intentExtras != null) {
|
||||
intent.replaceExtras((Bundle) null);
|
||||
mIntentPersistableExtras = new PersistableBundle(intentExtras);
|
||||
} else {
|
||||
mIntentPersistableExtras = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the categories.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setCategories(Set<String> categories) {
|
||||
mCategories = cloneCategories(categories);
|
||||
}
|
||||
|
||||
private ShortcutInfo(Parcel source) {
|
||||
final ClassLoader cl = getClass().getClassLoader();
|
||||
|
||||
@@ -1591,7 +1632,7 @@ public final class ShortcutInfo implements Parcelable {
|
||||
mDisabledMessage = disabledMessage;
|
||||
mDisabledMessageResId = disabledMessageResId;
|
||||
mDisabledMessageResName = disabledMessageResName;
|
||||
mCategories = clone(categories);
|
||||
mCategories = cloneCategories(categories);
|
||||
mIntent = intent;
|
||||
mIntentPersistableExtras = intentPersistableExtras;
|
||||
mRank = rank;
|
||||
|
||||
@@ -8251,12 +8251,13 @@ i
|
||||
<declare-styleable name="Shortcut">
|
||||
<attr name="shortcutId" format="string" />
|
||||
<attr name="enabled" />
|
||||
<attr name="shortcutIcon" format="reference" />
|
||||
<attr name="icon" />
|
||||
<attr name="shortcutShortLabel" format="reference" />
|
||||
<attr name="shortcutLongLabel" format="reference" />
|
||||
<attr name="shortcutDisabledMessage" format="reference" />
|
||||
<attr name="shortcutCategories" format="string" />
|
||||
<attr name="shortcutIntentAction" format="string" />
|
||||
<attr name="shortcutIntentData" format="string" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ShortcutCategories">
|
||||
<attr name="name" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
||||
@@ -2733,13 +2733,9 @@
|
||||
=============================================================== -->
|
||||
<eat-comment />
|
||||
<public type="attr" name="shortcutId" />
|
||||
<public type="attr" name="shortcutIcon" />
|
||||
<public type="attr" name="shortcutShortLabel" />
|
||||
<public type="attr" name="shortcutLongLabel" />
|
||||
<public type="attr" name="shortcutDisabledMessage" />
|
||||
<public type="attr" name="shortcutCategories" />
|
||||
<public type="attr" name="shortcutIntentAction" />
|
||||
<public type="attr" name="shortcutIntentData" />
|
||||
<public type="attr" name="roundIcon" />
|
||||
|
||||
<public type="attr" name="contextUri" />
|
||||
|
||||
@@ -682,6 +682,8 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
changed |= pushOutExcessShortcuts();
|
||||
}
|
||||
|
||||
s.verifyStates();
|
||||
|
||||
if (changed) {
|
||||
// This will send a notification to the launcher, and also save .
|
||||
s.packageShortcutsChanged(getPackageName(), getPackageUserId());
|
||||
@@ -774,6 +776,7 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
}
|
||||
removeOrphans();
|
||||
}
|
||||
adjustRanks();
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -810,7 +813,6 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
deleteDynamicWithId(shortcut.getId());
|
||||
}
|
||||
}
|
||||
service.verifyStates();
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.util.Xml;
|
||||
|
||||
@@ -48,10 +48,12 @@ public class ShortcutParser {
|
||||
private static final boolean DEBUG = ShortcutService.DEBUG || false; // DO NOT SUBMIT WITH TRUE
|
||||
|
||||
@VisibleForTesting
|
||||
static final String METADATA_KEY = "android.pm.Shortcuts";
|
||||
static final String METADATA_KEY = "android.app.shortcuts";
|
||||
|
||||
private static final String TAG_SHORTCUTS = "shortcuts";
|
||||
private static final String TAG_SHORTCUT = "shortcut";
|
||||
private static final String TAG_INTENT = "intent";
|
||||
private static final String TAG_CATEGORIES = "categories";
|
||||
|
||||
@Nullable
|
||||
public static List<ShortcutInfo> parseShortcuts(ShortcutService service,
|
||||
@@ -60,10 +62,17 @@ public class ShortcutParser {
|
||||
|
||||
List<ShortcutInfo> result = null;
|
||||
|
||||
if (pi != null && pi.activities != null) {
|
||||
for (ActivityInfo activityInfo : pi.activities) {
|
||||
result = parseShortcutsOneFile(service, activityInfo, packageName, userId, result);
|
||||
try {
|
||||
if (pi != null && pi.activities != null) {
|
||||
for (ActivityInfo activityInfo : pi.activities) {
|
||||
result = parseShortcutsOneFile(service, activityInfo, packageName, userId, result);
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
// Resource ID mismatch may cause various runtime exceptions when parsing XMLs.
|
||||
service.wtf(
|
||||
"Exception caught while parsing shortcut XML for package=" + packageName, e);
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -89,14 +98,58 @@ public class ShortcutParser {
|
||||
final int maxShortcuts = service.getMaxActivityShortcuts();
|
||||
int numShortcuts = 0;
|
||||
|
||||
// We instantiate ShortcutInfo at <shortcut>, but we add it to the list at </shortcut>,
|
||||
// after parsing <intent>. We keep the current one in here.
|
||||
ShortcutInfo currentShortcut = null;
|
||||
|
||||
Set<String> categories = null;
|
||||
|
||||
outer:
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& (type != XmlPullParser.END_TAG || parser.getDepth() > 0)) {
|
||||
final int depth = parser.getDepth();
|
||||
final String tag = parser.getName();
|
||||
|
||||
// When a shortcut tag is closing, publish.
|
||||
if ((type == XmlPullParser.END_TAG) && (depth == 2) && (TAG_SHORTCUT.equals(tag))) {
|
||||
if (currentShortcut == null) {
|
||||
// Shortcut was invalid.
|
||||
continue;
|
||||
}
|
||||
final ShortcutInfo si = currentShortcut;
|
||||
currentShortcut = null; // Make sure to null out for the next iteration.
|
||||
|
||||
if (si.getIntent() == null) {
|
||||
Log.e(TAG, "Shortcut " + si.getId() + " has no intent. Skipping it.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (numShortcuts >= maxShortcuts) {
|
||||
Log.e(TAG, "More than " + maxShortcuts + " shortcuts found for "
|
||||
+ activityInfo.getComponentName() + ". Skipping the rest.");
|
||||
return result;
|
||||
}
|
||||
if (categories != null) {
|
||||
si.setCategories(categories);
|
||||
categories = null;
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
result = new ArrayList<>();
|
||||
}
|
||||
result.add(si);
|
||||
numShortcuts++;
|
||||
rank++;
|
||||
if (ShortcutService.DEBUG) {
|
||||
Slog.d(TAG, "Shortcut added: " + si.toInsecureString());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, just look at start tags.
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
final int depth = parser.getDepth();
|
||||
final String tag = parser.getName();
|
||||
|
||||
if (depth == 1 && TAG_SHORTCUTS.equals(tag)) {
|
||||
continue; // Root tag.
|
||||
@@ -104,35 +157,73 @@ public class ShortcutParser {
|
||||
if (depth == 2 && TAG_SHORTCUT.equals(tag)) {
|
||||
final ShortcutInfo si = parseShortcutAttributes(
|
||||
service, attrs, packageName, activity, userId, rank);
|
||||
if (si == null) {
|
||||
// Shortcut was invalid.
|
||||
continue;
|
||||
}
|
||||
if (ShortcutService.DEBUG) {
|
||||
Slog.d(TAG, "Shortcut=" + si);
|
||||
Slog.d(TAG, "Shortcut found: " + si.toInsecureString());
|
||||
}
|
||||
if (result != null) {
|
||||
for (int i = result.size() - 1; i >= 0; i--) {
|
||||
if (si.getId().equals(result.get(i).getId())) {
|
||||
Slog.w(TAG, "Duplicate shortcut ID detected, skipping.");
|
||||
Log.e(TAG, "Duplicate shortcut ID detected. Skipping it.");
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!si.isEnabled()) {
|
||||
// Just set the default intent to disabled shortcuts.
|
||||
si.setIntent(new Intent(Intent.ACTION_VIEW));
|
||||
}
|
||||
currentShortcut = si;
|
||||
categories = null;
|
||||
continue;
|
||||
}
|
||||
if (depth == 3 && TAG_INTENT.equals(tag)) {
|
||||
if ((currentShortcut == null)
|
||||
|| (currentShortcut.getIntentNoExtras() != null)
|
||||
|| !currentShortcut.isEnabled()) {
|
||||
Log.e(TAG, "Ignoring excessive intent tag.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (si != null) {
|
||||
if (numShortcuts >= maxShortcuts) {
|
||||
Slog.w(TAG, "More than " + maxShortcuts + " shortcuts found for "
|
||||
+ activityInfo.getComponentName() + ", ignoring the rest.");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
result = new ArrayList<>();
|
||||
}
|
||||
result.add(si);
|
||||
numShortcuts++;
|
||||
rank++;
|
||||
final Intent intent = Intent.parseIntent(service.mContext.getResources(),
|
||||
parser, attrs);
|
||||
if (TextUtils.isEmpty(intent.getAction())) {
|
||||
Log.e(TAG, "Shortcut intent action must be provided. activity=" + activity);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
currentShortcut.setIntent(intent);
|
||||
} catch (RuntimeException e) {
|
||||
// This shouldn't happen because intents in XML can't have complicated
|
||||
// extras, but just in case Intent.parseIntent() supports such a thing one
|
||||
// day.
|
||||
Log.e(TAG, "Shortcut's extras contain un-persistable values. Skipping it.");
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Slog.w(TAG, "Unknown tag " + tag);
|
||||
if (depth == 3 && TAG_CATEGORIES.equals(tag)) {
|
||||
if ((currentShortcut == null)
|
||||
|| (currentShortcut.getCategories() != null)) {
|
||||
continue;
|
||||
}
|
||||
final String name = parseCategories(service, attrs);
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
Log.e(TAG, "Empty category found. activity=" + activity);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (categories == null) {
|
||||
categories = new ArraySet<>();
|
||||
}
|
||||
categories.add(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
Log.w(TAG, "Unknown tag " + tag + " at depth " + depth);
|
||||
}
|
||||
} finally {
|
||||
if (parser != null) {
|
||||
@@ -142,6 +233,16 @@ public class ShortcutParser {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String parseCategories(ShortcutService service, AttributeSet attrs) {
|
||||
final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
|
||||
R.styleable.ShortcutCategories);
|
||||
try {
|
||||
return sa.getString(R.styleable.ShortcutCategories_name);
|
||||
} finally {
|
||||
sa.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
private static ShortcutInfo parseShortcutAttributes(ShortcutService service,
|
||||
AttributeSet attrs, String packageName, ComponentName activity,
|
||||
@UserIdInt int userId, int rank) {
|
||||
@@ -150,14 +251,11 @@ public class ShortcutParser {
|
||||
try {
|
||||
final String id = sa.getString(R.styleable.Shortcut_shortcutId);
|
||||
final boolean enabled = sa.getBoolean(R.styleable.Shortcut_enabled, true);
|
||||
final int iconResId = sa.getResourceId(R.styleable.Shortcut_shortcutIcon, 0);
|
||||
final int iconResId = sa.getResourceId(R.styleable.Shortcut_icon, 0);
|
||||
final int titleResId = sa.getResourceId(R.styleable.Shortcut_shortcutShortLabel, 0);
|
||||
final int textResId = sa.getResourceId(R.styleable.Shortcut_shortcutLongLabel, 0);
|
||||
final int disabledMessageResId = sa.getResourceId(
|
||||
R.styleable.Shortcut_shortcutDisabledMessage, 0);
|
||||
final String categories = sa.getString(R.styleable.Shortcut_shortcutCategories);
|
||||
String intentAction = sa.getString(R.styleable.Shortcut_shortcutIntentAction);
|
||||
final String intentData = sa.getString(R.styleable.Shortcut_shortcutIntentData);
|
||||
|
||||
if (TextUtils.isEmpty(id)) {
|
||||
Slog.w(TAG, "Shortcut ID must be provided. activity=" + activity);
|
||||
@@ -167,31 +265,6 @@ public class ShortcutParser {
|
||||
Slog.w(TAG, "Shortcut title must be provided. activity=" + activity);
|
||||
return null;
|
||||
}
|
||||
if (TextUtils.isEmpty(intentAction)) {
|
||||
if (enabled) {
|
||||
Slog.w(TAG, "Shortcut intent action must be provided. activity=" + activity);
|
||||
return null;
|
||||
} else {
|
||||
// Disabled shortcut doesn't have to have an action, but just set VIEW as the
|
||||
// default.
|
||||
intentAction = Intent.ACTION_VIEW;
|
||||
}
|
||||
}
|
||||
|
||||
final ArraySet<String> categoriesSet;
|
||||
if (categories == null) {
|
||||
categoriesSet = null;
|
||||
} else {
|
||||
final String[] arr = categories.split(":");
|
||||
categoriesSet = new ArraySet<>(arr.length);
|
||||
for (String v : arr) {
|
||||
categoriesSet.add(v);
|
||||
}
|
||||
}
|
||||
final Intent intent = new Intent(intentAction);
|
||||
if (!TextUtils.isEmpty(intentData)) {
|
||||
intent.setData(Uri.parse(intentData));
|
||||
}
|
||||
|
||||
return createShortcutFromManifest(
|
||||
service,
|
||||
@@ -202,8 +275,6 @@ public class ShortcutParser {
|
||||
titleResId,
|
||||
textResId,
|
||||
disabledMessageResId,
|
||||
categoriesSet,
|
||||
intent,
|
||||
rank,
|
||||
iconResId,
|
||||
enabled);
|
||||
@@ -214,8 +285,8 @@ public class ShortcutParser {
|
||||
|
||||
private static ShortcutInfo createShortcutFromManifest(ShortcutService service,
|
||||
@UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
|
||||
int titleResId, int textResId, int disabledMessageResId, Set<String> categories,
|
||||
Intent intent, int rank, int iconResId, boolean enabled) {
|
||||
int titleResId, int textResId, int disabledMessageResId,
|
||||
int rank, int iconResId, boolean enabled) {
|
||||
|
||||
final int flags =
|
||||
(enabled ? ShortcutInfo.FLAG_MANIFEST : ShortcutInfo.FLAG_DISABLED)
|
||||
@@ -239,8 +310,8 @@ public class ShortcutParser {
|
||||
null, // disabled message string
|
||||
disabledMessageResId,
|
||||
null, // disabled message res name
|
||||
categories,
|
||||
intent,
|
||||
null, // categories
|
||||
null, // intent
|
||||
null, // intent extras
|
||||
rank,
|
||||
null, // extras
|
||||
|
||||
@@ -1510,6 +1510,10 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
ps.clearAllImplicitRanks();
|
||||
assignImplicitRanks(newShortcuts);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
|
||||
}
|
||||
|
||||
// First, remove all un-pinned; dynamic shortcuts
|
||||
ps.deleteAllDynamicShortcuts();
|
||||
|
||||
|
||||
BIN
services/tests/servicestests/res/drawable/icon3.png
Normal file
BIN
services/tests/servicestests/res/drawable/icon3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -2,12 +2,17 @@
|
||||
<shortcut
|
||||
android:shortcutId="ms1"
|
||||
android:enabled="true"
|
||||
android:shortcutIcon="@drawable/icon1"
|
||||
android:icon="@drawable/icon1"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutLongLabel="@string/shortcut_text1"
|
||||
android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
|
||||
android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
|
||||
android:shortcutIntentAction="action1"
|
||||
android:shortcutIntentData="data1"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action1"
|
||||
android:data="data1"
|
||||
>
|
||||
</intent>
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
<categories android:name="android.shortcut.media" />
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
|
||||
@@ -17,12 +17,17 @@
|
||||
<shortcut
|
||||
android:shortcutId="ms1-alt"
|
||||
android:enabled="true"
|
||||
android:shortcutIcon="@drawable/icon1"
|
||||
android:icon="@drawable/icon1"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutLongLabel="@string/shortcut_text1"
|
||||
android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
|
||||
android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
|
||||
android:shortcutIntentAction="action1"
|
||||
android:shortcutIntentData="data1"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action1"
|
||||
android:data="data1"
|
||||
>
|
||||
</intent>
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
<categories android:name="android.shortcut.media" />
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<shortcut
|
||||
android:shortcutId="ms1"
|
||||
android:enabled="false"
|
||||
android:shortcutIcon="@drawable/icon2"
|
||||
android:icon="@drawable/icon2"
|
||||
android:shortcutShortLabel="@string/shortcut_title2"
|
||||
android:shortcutLongLabel="@string/shortcut_text2"
|
||||
android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
|
||||
|
||||
@@ -17,23 +17,32 @@
|
||||
<shortcut
|
||||
android:shortcutId="ms1"
|
||||
android:enabled="true"
|
||||
android:shortcutIcon="@drawable/icon1"
|
||||
android:icon="@drawable/icon1"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutLongLabel="@string/shortcut_text1"
|
||||
android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
|
||||
android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
|
||||
android:shortcutIntentAction="action1"
|
||||
android:shortcutIntentData="http://a.b.c/"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action1"
|
||||
android:data="http://a.b.c/"
|
||||
>
|
||||
</intent>
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
<categories android:name="android.shortcut.media" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms2"
|
||||
android:enabled="true"
|
||||
android:shortcutIcon="@drawable/icon2"
|
||||
android:icon="@drawable/icon2"
|
||||
android:shortcutShortLabel="@string/shortcut_title2"
|
||||
android:shortcutLongLabel="@string/shortcut_text2"
|
||||
android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
|
||||
android:shortcutCategories="android.shortcut.conversation"
|
||||
android:shortcutIntentAction="action2"
|
||||
android:shortcutIntentData="http://a.b.c/2"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action2"
|
||||
android:data="http://a.b.c/2"
|
||||
>
|
||||
</intent>
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
|
||||
@@ -17,11 +17,19 @@
|
||||
<shortcut
|
||||
android:shortcutId="ms1"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="action1"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action1"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms1"
|
||||
android:shortcutShortLabel="@string/shortcut_title2"
|
||||
android:shortcutIntentAction="action2"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action2"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
|
||||
@@ -17,37 +17,69 @@
|
||||
<shortcut
|
||||
android:shortcutId="ms1"
|
||||
android:enabled="true"
|
||||
android:shortcutIcon="@drawable/icon1"
|
||||
android:icon="@drawable/icon1"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutLongLabel="@string/shortcut_text1"
|
||||
android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
|
||||
android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
|
||||
android:shortcutIntentAction="action1"
|
||||
android:shortcutIntentData="http://a.b.c/1"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action1"
|
||||
android:data="http://a.b.c/1"
|
||||
>
|
||||
</intent>
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
<categories android:name="android.shortcut.media" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms2"
|
||||
android:enabled="true"
|
||||
android:shortcutIcon="@drawable/icon2"
|
||||
android:icon="@drawable/icon2"
|
||||
android:shortcutShortLabel="@string/shortcut_title2"
|
||||
android:shortcutLongLabel="@string/shortcut_text2"
|
||||
android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
|
||||
android:shortcutCategories="android.shortcut.conversation"
|
||||
android:shortcutIntentAction="action2"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action2"
|
||||
>
|
||||
</intent>
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms3"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms4"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
android:shortcutShortLabel="@string/shortcut_title2"
|
||||
>
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW2"
|
||||
>
|
||||
</intent>
|
||||
<categories />
|
||||
<categories android:name="" />
|
||||
<categories android:name="cat" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms5"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action"
|
||||
android:data="http://www/"
|
||||
android:targetPackage="abc"
|
||||
android:targetClass=".xyz"
|
||||
android:mimeType="foo/bar"
|
||||
>
|
||||
<categories android:name="cat1" />
|
||||
<categories android:name="cat2" />
|
||||
<extra android:name="key1" android:value="value1" />
|
||||
<extra android:name="key2" android:value="value2" />
|
||||
</intent>
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
|
||||
@@ -17,37 +17,58 @@
|
||||
<shortcut
|
||||
android:shortcutId="ms1_alt"
|
||||
android:enabled="true"
|
||||
android:shortcutIcon="@drawable/icon1"
|
||||
android:icon="@drawable/icon1"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutLongLabel="@string/shortcut_text1"
|
||||
android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
|
||||
android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
|
||||
android:shortcutIntentAction="action1"
|
||||
android:shortcutIntentData="http://a.b.c/1"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action1"
|
||||
android:data="http://a.b.c/1"
|
||||
>
|
||||
</intent>
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
<categories android:name="android.shortcut.media" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms2_alt"
|
||||
android:enabled="true"
|
||||
android:shortcutIcon="@drawable/icon2"
|
||||
android:icon="@drawable/icon2"
|
||||
android:shortcutShortLabel="@string/shortcut_title2"
|
||||
android:shortcutLongLabel="@string/shortcut_text2"
|
||||
android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
|
||||
android:shortcutCategories="android.shortcut.conversation"
|
||||
android:shortcutIntentAction="action2"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action2"
|
||||
>
|
||||
</intent>
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms3_alt"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms4_alt"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms5_alt"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
|
||||
@@ -17,37 +17,62 @@
|
||||
<shortcut
|
||||
android:shortcutId="ms5"
|
||||
android:enabled="true"
|
||||
android:shortcutIcon="@drawable/icon1"
|
||||
android:icon="@drawable/icon1"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutLongLabel="@string/shortcut_text1"
|
||||
android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
|
||||
android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
|
||||
android:shortcutIntentAction="action1"
|
||||
android:shortcutIntentData="http://a.b.c/1"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action1"
|
||||
android:data="http://a.b.c/1"
|
||||
>
|
||||
</intent>
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
<categories android:name="android.shortcut.media" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms4"
|
||||
android:enabled="true"
|
||||
android:shortcutIcon="@drawable/icon2"
|
||||
android:icon="@drawable/icon2"
|
||||
android:shortcutShortLabel="@string/shortcut_title2"
|
||||
android:shortcutLongLabel="@string/shortcut_text2"
|
||||
android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
|
||||
android:shortcutCategories="android.shortcut.conversation"
|
||||
android:shortcutIntentAction="action2"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action2"
|
||||
>
|
||||
</intent>
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms3"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms2"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="ms1"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="action"
|
||||
>
|
||||
<categories android:name="cat1" />
|
||||
<categories android:name="cat2" />
|
||||
<extra android:name="key1" android:value="value1" />
|
||||
<extra android:name="key2" android:value="value2" />
|
||||
</intent>
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
|
||||
@@ -16,11 +16,19 @@
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<shortcut
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="x1"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
|
||||
@@ -16,11 +16,19 @@
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<shortcut
|
||||
android:shortcutId="manifest-shortcut-3"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="x2"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<shortcut
|
||||
android:shortcutId="x3"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||
/>
|
||||
>
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
>
|
||||
</intent>
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
|
||||
63
services/tests/servicestests/res/xml/shortcut_error_4.xml
Normal file
63
services/tests/servicestests/res/xml/shortcut_error_4.xml
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<!-- This is valid -->
|
||||
<shortcut
|
||||
android:shortcutId="ms1"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
>
|
||||
<intent android:action="action1" />
|
||||
</shortcut>
|
||||
|
||||
<!-- Invalid: no intent -->
|
||||
<shortcut
|
||||
android:shortcutId="ms_ignored1"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
/>
|
||||
|
||||
<!-- Valid: more than one intent; first one will be picked. -->
|
||||
<shortcut
|
||||
android:shortcutId="ms2"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
>
|
||||
<intent android:action="action2_1" />
|
||||
<intent android:action="action2_2" />
|
||||
</shortcut>
|
||||
|
||||
<!-- Valid: disabled shortcut doesn't need an intent -->
|
||||
<shortcut
|
||||
android:shortcutId="ms3"
|
||||
android:enabled="false"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
/>
|
||||
|
||||
<!-- Valid, but disabled shortcut's intent will be ignored. -->
|
||||
<shortcut
|
||||
android:shortcutId="ms4"
|
||||
android:enabled="false"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
>
|
||||
<intent android:action="action4" />
|
||||
</shortcut>
|
||||
|
||||
<!-- Invalid, no intent action -->
|
||||
<shortcut
|
||||
android:shortcutId="ms_ignored2"
|
||||
android:shortcutShortLabel="@string/shortcut_title1"
|
||||
>
|
||||
<intent android:data="x"/>
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
@@ -4884,6 +4884,117 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
|
||||
});
|
||||
}
|
||||
|
||||
public void testManifestShortcuts_intentDefinitions() {
|
||||
addManifestShortcutResource(
|
||||
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
|
||||
R.xml.shortcut_error_4);
|
||||
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||
mService.mPackageMonitor.onReceive(getTestContext(),
|
||||
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
// Make sure invalid ones are not published.
|
||||
// Note that at this point disabled ones don't show up because they weren't pinned.
|
||||
assertWith(getCallerShortcuts())
|
||||
.haveIds("ms1", "ms2")
|
||||
.areAllManifest()
|
||||
.areAllNotDynamic()
|
||||
.areAllNotPinned()
|
||||
.areAllImmutable()
|
||||
.areAllEnabled()
|
||||
.forShortcutWithId("ms1", si -> {
|
||||
assertTrue(si.isEnabled());
|
||||
assertEquals("action1", si.getIntent().getAction());
|
||||
})
|
||||
.forShortcutWithId("ms2", si -> {
|
||||
assertTrue(si.isEnabled());
|
||||
assertEquals("action2_1", si.getIntent().getAction());
|
||||
});
|
||||
});
|
||||
|
||||
// Publish 5 enabled to pin some, so we can later test disabled manfiest shortcuts..
|
||||
addManifestShortcutResource(
|
||||
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
|
||||
R.xml.shortcut_5);
|
||||
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||
mService.mPackageMonitor.onReceive(getTestContext(),
|
||||
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
// Make sure 5 manifest shortcuts are published.
|
||||
assertWith(getCallerShortcuts())
|
||||
.haveIds("ms1", "ms2", "ms3", "ms4", "ms5")
|
||||
.areAllManifest()
|
||||
.areAllNotDynamic()
|
||||
.areAllNotPinned()
|
||||
.areAllImmutable()
|
||||
.areAllEnabled();
|
||||
});
|
||||
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
|
||||
list("ms3", "ms4", "ms5"), HANDLE_USER_0);
|
||||
});
|
||||
|
||||
// Make sure they're pinned.
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertWith(getCallerShortcuts())
|
||||
.haveIds("ms1", "ms2", "ms3", "ms4", "ms5")
|
||||
.selectByIds("ms1", "ms2")
|
||||
.areAllNotPinned()
|
||||
.areAllEnabled()
|
||||
|
||||
.revertToOriginalList()
|
||||
.selectByIds("ms3", "ms4", "ms5")
|
||||
.areAllPinned()
|
||||
.areAllEnabled();
|
||||
});
|
||||
|
||||
// Update the app.
|
||||
addManifestShortcutResource(
|
||||
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
|
||||
R.xml.shortcut_error_4);
|
||||
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||
mService.mPackageMonitor.onReceive(getTestContext(),
|
||||
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
|
||||
|
||||
// Make sure 3, 4 and 5 still exist but disabled.
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertWith(getCallerShortcuts())
|
||||
.haveIds("ms1", "ms2", "ms3", "ms4", "ms5")
|
||||
.areAllNotDynamic()
|
||||
.areAllImmutable()
|
||||
|
||||
.selectByIds("ms1", "ms2")
|
||||
.areAllManifest()
|
||||
.areAllNotPinned()
|
||||
.areAllEnabled()
|
||||
|
||||
.revertToOriginalList()
|
||||
.selectByIds("ms3", "ms4", "ms5")
|
||||
.areAllNotManifest()
|
||||
.areAllPinned()
|
||||
.areAllDisabled()
|
||||
|
||||
.revertToOriginalList()
|
||||
.forShortcutWithId("ms1", si -> {
|
||||
assertEquals(si.getId(), "action1", si.getIntent().getAction());
|
||||
})
|
||||
.forShortcutWithId("ms2", si -> {
|
||||
assertEquals(si.getId(), "action2_1", si.getIntent().getAction());
|
||||
})
|
||||
.forShortcutWithId("ms3", si -> {
|
||||
assertEquals(si.getId(), Intent.ACTION_VIEW, si.getIntent().getAction());
|
||||
})
|
||||
.forShortcutWithId("ms4", si -> {
|
||||
assertEquals(si.getId(), Intent.ACTION_VIEW, si.getIntent().getAction());
|
||||
})
|
||||
.forShortcutWithId("ms5", si -> {
|
||||
assertEquals(si.getId(), "action", si.getIntent().getAction());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void testManifestShortcuts_checkAllFields() {
|
||||
mService.handleUnlockUser(USER_0);
|
||||
|
||||
@@ -4897,63 +5008,95 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
|
||||
|
||||
// Only the valid one is published.
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
|
||||
mManager.getManifestShortcuts()))),
|
||||
"ms1", "ms2", "ms3", "ms4", "ms5");
|
||||
assertWith(getCallerShortcuts())
|
||||
.haveIds("ms1", "ms2", "ms3", "ms4", "ms5")
|
||||
.areAllManifest()
|
||||
.areAllImmutable()
|
||||
.areAllEnabled()
|
||||
.areAllNotPinned()
|
||||
.areAllNotDynamic()
|
||||
|
||||
// check first shortcut.
|
||||
ShortcutInfo si = getCallerShortcut("ms1");
|
||||
.forShortcutWithId("ms1", si -> {
|
||||
assertEquals(R.drawable.icon1, si.getIconResourceId());
|
||||
assertEquals(new ComponentName(CALLING_PACKAGE_1,
|
||||
ShortcutActivity.class.getName()),
|
||||
si.getActivity());
|
||||
|
||||
assertEquals("ms1", si.getId());
|
||||
assertEquals(R.drawable.icon1, si.getIconResourceId());
|
||||
assertEquals(new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
|
||||
si.getActivity());
|
||||
assertEquals(R.string.shortcut_title1, si.getTitleResId());
|
||||
assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
|
||||
assertEquals(R.string.shortcut_text1, si.getTextResId());
|
||||
assertEquals("r" + R.string.shortcut_text1, si.getTextResName());
|
||||
assertEquals(R.string.shortcut_disabled_message1,
|
||||
si.getDisabledMessageResourceId());
|
||||
assertEquals("r" + R.string.shortcut_disabled_message1,
|
||||
si.getDisabledMessageResName());
|
||||
|
||||
assertEquals(R.string.shortcut_title1, si.getTitleResId());
|
||||
assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
|
||||
assertEquals(R.string.shortcut_text1, si.getTextResId());
|
||||
assertEquals("r" + R.string.shortcut_text1, si.getTextResName());
|
||||
assertEquals(R.string.shortcut_disabled_message1, si.getDisabledMessageResourceId());
|
||||
assertEquals("r" + R.string.shortcut_disabled_message1, si.getDisabledMessageResName());
|
||||
assertEquals(set("android.shortcut.conversation", "android.shortcut.media"),
|
||||
si.getCategories());
|
||||
assertEquals("action1", si.getIntent().getAction());
|
||||
assertEquals(Uri.parse("http://a.b.c/1"), si.getIntent().getData());
|
||||
})
|
||||
|
||||
assertEquals(set("android.shortcut.conversation", "android.shortcut.media"),
|
||||
si.getCategories());
|
||||
assertEquals("action1", si.getIntent().getAction());
|
||||
assertEquals(Uri.parse("http://a.b.c/1"), si.getIntent().getData());
|
||||
.forShortcutWithId("ms2", si -> {
|
||||
assertEquals("ms2", si.getId());
|
||||
assertEquals(R.drawable.icon2, si.getIconResourceId());
|
||||
|
||||
// check another
|
||||
si = getCallerShortcut("ms2");
|
||||
assertEquals(R.string.shortcut_title2, si.getTitleResId());
|
||||
assertEquals("r" + R.string.shortcut_title2, si.getTitleResName());
|
||||
assertEquals(R.string.shortcut_text2, si.getTextResId());
|
||||
assertEquals("r" + R.string.shortcut_text2, si.getTextResName());
|
||||
assertEquals(R.string.shortcut_disabled_message2,
|
||||
si.getDisabledMessageResourceId());
|
||||
assertEquals("r" + R.string.shortcut_disabled_message2,
|
||||
si.getDisabledMessageResName());
|
||||
|
||||
assertEquals("ms2", si.getId());
|
||||
assertEquals(R.drawable.icon2, si.getIconResourceId());
|
||||
assertEquals(set("android.shortcut.conversation"), si.getCategories());
|
||||
assertEquals("action2", si.getIntent().getAction());
|
||||
assertEquals(null, si.getIntent().getData());
|
||||
})
|
||||
|
||||
assertEquals(R.string.shortcut_title2, si.getTitleResId());
|
||||
assertEquals("r" + R.string.shortcut_title2, si.getTitleResName());
|
||||
assertEquals(R.string.shortcut_text2, si.getTextResId());
|
||||
assertEquals("r" + R.string.shortcut_text2, si.getTextResName());
|
||||
assertEquals(R.string.shortcut_disabled_message2, si.getDisabledMessageResourceId());
|
||||
assertEquals("r" + R.string.shortcut_disabled_message2, si.getDisabledMessageResName());
|
||||
.forShortcutWithId("ms3", si -> {
|
||||
assertEquals(0, si.getIconResourceId());
|
||||
assertEquals(R.string.shortcut_title1, si.getTitleResId());
|
||||
assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
|
||||
|
||||
assertEquals(set("android.shortcut.conversation"), si.getCategories());
|
||||
assertEquals("action2", si.getIntent().getAction());
|
||||
assertEquals(null, si.getIntent().getData());
|
||||
assertEquals(0, si.getTextResId());
|
||||
assertEquals(null, si.getTextResName());
|
||||
assertEquals(0, si.getDisabledMessageResourceId());
|
||||
assertEquals(null, si.getDisabledMessageResName());
|
||||
|
||||
// check another
|
||||
si = getCallerShortcut("ms3");
|
||||
assertEmpty(si.getCategories());
|
||||
assertEquals("android.intent.action.VIEW", si.getIntent().getAction());
|
||||
assertEquals(null, si.getIntent().getData());
|
||||
})
|
||||
|
||||
assertEquals("ms3", si.getId());
|
||||
assertEquals(0, si.getIconResourceId());
|
||||
assertEquals(R.string.shortcut_title1, si.getTitleResId());
|
||||
assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
|
||||
.forShortcutWithId("ms4", si -> {
|
||||
assertEquals(0, si.getIconResourceId());
|
||||
assertEquals(R.string.shortcut_title2, si.getTitleResId());
|
||||
assertEquals("r" + R.string.shortcut_title2, si.getTitleResName());
|
||||
|
||||
assertEquals(0, si.getTextResId());
|
||||
assertEquals(null, si.getTextResName());
|
||||
assertEquals(0, si.getDisabledMessageResourceId());
|
||||
assertEquals(null, si.getDisabledMessageResName());
|
||||
assertEquals(0, si.getTextResId());
|
||||
assertEquals(null, si.getTextResName());
|
||||
assertEquals(0, si.getDisabledMessageResourceId());
|
||||
assertEquals(null, si.getDisabledMessageResName());
|
||||
|
||||
assertEquals(null, si.getCategories());
|
||||
assertEquals("android.intent.action.VIEW", si.getIntent().getAction());
|
||||
assertEquals(null, si.getIntent().getData());
|
||||
assertEquals(set("cat"), si.getCategories());
|
||||
assertEquals("android.intent.action.VIEW2", si.getIntent().getAction());
|
||||
assertEquals(null, si.getIntent().getData());
|
||||
})
|
||||
|
||||
.forShortcutWithId("ms5", si -> {
|
||||
si = getCallerShortcut("ms5");
|
||||
assertEquals("action", si.getIntent().getAction());
|
||||
assertEquals("http://www/", si.getIntent().getData().toString());
|
||||
assertEquals("foo/bar", si.getIntent().getType());
|
||||
assertEquals(
|
||||
new ComponentName("abc", ".xyz"), si.getIntent().getComponent());
|
||||
|
||||
assertEquals(set("cat1", "cat2"), si.getIntent().getCategories());
|
||||
assertEquals("value1", si.getIntent().getStringExtra("key1"));
|
||||
assertEquals("value2", si.getIntent().getStringExtra("key2"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -43,9 +43,11 @@ import android.os.Parcel;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.UserHandle;
|
||||
import android.test.MoreAsserts;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
@@ -66,6 +68,7 @@ import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -259,9 +262,12 @@ public class ShortcutManagerTestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> List<T> assertEmpty(List<T> list) {
|
||||
assertEquals(0, list.size());
|
||||
return list;
|
||||
public static <T extends Collection<?>> T assertEmpty(T collection) {
|
||||
if (collection == null) {
|
||||
return collection; // okay.
|
||||
}
|
||||
assertEquals(0, collection.size());
|
||||
return collection;
|
||||
}
|
||||
|
||||
public static List<ShortcutInfo> filter(List<ShortcutInfo> list, Predicate<ShortcutInfo> p) {
|
||||
@@ -653,42 +659,73 @@ public class ShortcutManagerTestUtils {
|
||||
* New style assertion that allows chained calls.
|
||||
*/
|
||||
public static class ShortcutListAsserter {
|
||||
private final ShortcutListAsserter mOriginal;
|
||||
private final List<ShortcutInfo> mList;
|
||||
|
||||
ShortcutListAsserter(List<ShortcutInfo> list) {
|
||||
this(null, list);
|
||||
}
|
||||
|
||||
private ShortcutListAsserter(ShortcutListAsserter original, List<ShortcutInfo> list) {
|
||||
mOriginal = original == null ? this : original;
|
||||
mList = new ArrayList<>(list);
|
||||
}
|
||||
|
||||
public ShortcutListAsserter revertToOriginalList() {
|
||||
return mOriginal;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter selectDynamic() {
|
||||
return new ShortcutListAsserter(
|
||||
return new ShortcutListAsserter(this,
|
||||
filter(mList, ShortcutInfo::isDynamic));
|
||||
}
|
||||
|
||||
public ShortcutListAsserter selectManifest() {
|
||||
return new ShortcutListAsserter(
|
||||
return new ShortcutListAsserter(this,
|
||||
filter(mList, ShortcutInfo::isManifestShortcut));
|
||||
}
|
||||
|
||||
public ShortcutListAsserter selectPinned() {
|
||||
return new ShortcutListAsserter(
|
||||
return new ShortcutListAsserter(this,
|
||||
filter(mList, ShortcutInfo::isPinned));
|
||||
}
|
||||
|
||||
public ShortcutListAsserter selectByActivity(ComponentName activity) {
|
||||
return new ShortcutListAsserter(
|
||||
return new ShortcutListAsserter(this,
|
||||
ShortcutManagerTestUtils.filterByActivity(mList, activity));
|
||||
}
|
||||
|
||||
public ShortcutListAsserter selectByChangedSince(long time) {
|
||||
return new ShortcutListAsserter(
|
||||
return new ShortcutListAsserter(this,
|
||||
ShortcutManagerTestUtils.changedSince(mList, time));
|
||||
}
|
||||
|
||||
public ShortcutListAsserter selectByIds(String... ids) {
|
||||
final Set<String> idSet = set(ids);
|
||||
final ArrayList<ShortcutInfo> selected = new ArrayList<>();
|
||||
for (ShortcutInfo si : mList) {
|
||||
if (idSet.contains(si.getId())) {
|
||||
selected.add(si);
|
||||
idSet.remove(si.getId());
|
||||
}
|
||||
}
|
||||
if (idSet.size() > 0) {
|
||||
fail("Shortcuts not found for IDs=" + idSet);
|
||||
}
|
||||
|
||||
return new ShortcutListAsserter(this, selected);
|
||||
}
|
||||
|
||||
public ShortcutListAsserter toSortByRank() {
|
||||
return new ShortcutListAsserter(
|
||||
return new ShortcutListAsserter(this,
|
||||
ShortcutManagerTestUtils.sortedByRank(mList));
|
||||
}
|
||||
|
||||
public ShortcutListAsserter call(Consumer<List<ShortcutInfo>> c) {
|
||||
c.accept(mList);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter haveIds(String... expectedIds) {
|
||||
assertShortcutIds(mList, expectedIds);
|
||||
return this;
|
||||
@@ -701,7 +738,8 @@ public class ShortcutManagerTestUtils {
|
||||
|
||||
private ShortcutListAsserter haveSequentialRanks() {
|
||||
for (int i = 0; i < mList.size(); i++) {
|
||||
assertEquals("Rank not sequential", i, mList.get(i).getRank());
|
||||
final ShortcutInfo si = mList.get(i);
|
||||
assertEquals("Rank not sequential: id=" + si.getId(), i, si.getRank());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -717,5 +755,87 @@ public class ShortcutManagerTestUtils {
|
||||
assertEquals(0, mList.size());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter areAllDynamic() {
|
||||
forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isDynamic()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter areAllNotDynamic() {
|
||||
forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isDynamic()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter areAllPinned() {
|
||||
forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isPinned()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter areAllNotPinned() {
|
||||
forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isPinned()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter areAllManifest() {
|
||||
forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isManifestShortcut()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter areAllNotManifest() {
|
||||
forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isManifestShortcut()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter areAllImmutable() {
|
||||
forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isImmutable()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter areAllMutable() {
|
||||
forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isImmutable()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter areAllEnabled() {
|
||||
forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isEnabled()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter areAllDisabled() {
|
||||
forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isEnabled()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter forAllShortcuts(Consumer<ShortcutInfo> sa) {
|
||||
for (int i = 0; i < mList.size(); i++) {
|
||||
final ShortcutInfo si = mList.get(i);
|
||||
sa.accept(si);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter forShortcut(Predicate<ShortcutInfo> p,
|
||||
Consumer<ShortcutInfo> sa) {
|
||||
boolean found = false;
|
||||
for (int i = 0; i < mList.size(); i++) {
|
||||
final ShortcutInfo si = mList.get(i);
|
||||
if (p.test(si)) {
|
||||
found = true;
|
||||
try {
|
||||
sa.accept(si);
|
||||
} catch (Throwable e) {
|
||||
throw new AssertionError("Assertion failed for shortcut " + si.getId(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
assertTrue("Shortcut with the given condition not found.", found);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShortcutListAsserter forShortcutWithId(String id, Consumer<ShortcutInfo> sa) {
|
||||
forShortcut(si -> si.getId().equals(id), sa);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user