diff options
| author | 2021-12-16 00:30:52 +0000 | |
|---|---|---|
| committer | 2022-01-19 05:06:10 +0000 | |
| commit | 8c9f13f1e1724282e16523654c7be73c7c770dc5 (patch) | |
| tree | c1a7c22c3026fb5154e0f35c9e9ff8690c372b92 | |
| parent | 2e6243d9000e0e9ce47b1036a51e796a5f81240c (diff) | |
Adds capability related setters in ShortcutInfo.Builder
Bug: 202872648
Test: atest ShortcutManagerTest2
Change-Id: Idbb79779564b9fce704be19a13695b6e08cb1088
7 files changed, 165 insertions, 7 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 7f0ff7f9955e..836968df3fd1 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -13410,6 +13410,7 @@ package android.content.pm { public final class ShortcutInfo implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.content.ComponentName getActivity(); + method @NonNull public java.util.List<java.lang.String> getCapabilityParameterValues(@NonNull String, @NonNull String); method @Nullable public java.util.Set<java.lang.String> getCategories(); method @Nullable public CharSequence getDisabledMessage(); method public int getDisabledReason(); @@ -13424,6 +13425,7 @@ package android.content.pm { method public int getRank(); method @Nullable public CharSequence getShortLabel(); method public android.os.UserHandle getUserHandle(); + method public boolean hasCapability(@NonNull String); method public boolean hasKeyFieldsOnly(); method public boolean isCached(); method public boolean isDeclaredInManifest(); @@ -13448,6 +13450,7 @@ package android.content.pm { public static class ShortcutInfo.Builder { ctor public ShortcutInfo.Builder(android.content.Context, String); + method @NonNull public android.content.pm.ShortcutInfo.Builder addCapabilityBinding(@NonNull String, @Nullable String, @Nullable java.util.List<java.lang.String>); method @NonNull public android.content.pm.ShortcutInfo build(); method @NonNull public android.content.pm.ShortcutInfo.Builder setActivity(@NonNull android.content.ComponentName); method @NonNull public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>); diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java index 806091e2158d..8d9ef8530bfc 100644 --- a/core/java/android/content/pm/AppSearchShortcutInfo.java +++ b/core/java/android/content/pm/AppSearchShortcutInfo.java @@ -423,7 +423,7 @@ public class AppSearchShortcutInfo extends GenericDocument { shortLabelResName, longLabel, longLabelResId, longLabelResName, disabledMessage, disabledMessageResId, disabledMessageResName, categoriesSet, intents, rank, extras, getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri, - disabledReason, persons, locusId, null); + disabledReason, persons, locusId, null, null); si.setImplicitRank(implicitRank); if ((implicitRank & ShortcutInfo.RANK_CHANGED_BIT) != 0) { si.setRankChanged(); diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 7d4f7ecef29c..ab827aacbdc1 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -41,6 +41,7 @@ import android.os.Parcelable; import android.os.PersistableBundle; import android.os.UserHandle; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.view.contentcapture.ContentCaptureContext; @@ -50,9 +51,15 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Represents a shortcut that can be published via {@link ShortcutManager}. @@ -463,6 +470,9 @@ public final class ShortcutInfo implements Parcelable { private int mExcludedSurfaces; + @Nullable + private Map<String, Map<String, List<String>>> mCapabilityBindings; + private ShortcutInfo(Builder b) { mUserId = b.mContext.getUserId(); @@ -490,7 +500,7 @@ public final class ShortcutInfo implements Parcelable { mRank = b.mRank; mExtras = b.mExtras; mLocusId = b.mLocusId; - + mCapabilityBindings = b.mCapabilityBindings; mStartingThemeResName = b.mStartingThemeResId != 0 ? b.mContext.getResources().getResourceName(b.mStartingThemeResId) : null; updateTimestamp(); @@ -602,7 +612,7 @@ public final class ShortcutInfo implements Parcelable { mLocusId = source.mLocusId; mExcludedSurfaces = source.mExcludedSurfaces; - // Just always keep it since it's cheep. + // Just always keep it since it's cheap. mIconResId = source.mIconResId; if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) { @@ -641,6 +651,7 @@ public final class ShortcutInfo implements Parcelable { // Set this bit. mFlags |= FLAG_KEY_FIELDS_ONLY; } + mCapabilityBindings = source.mCapabilityBindings; mStartingThemeResName = source.mStartingThemeResName; } @@ -968,6 +979,9 @@ public final class ShortcutInfo implements Parcelable { if (source.mStartingThemeResName != null && !source.mStartingThemeResName.isEmpty()) { mStartingThemeResName = source.mStartingThemeResName; } + if (source.mCapabilityBindings != null) { + mCapabilityBindings = source.mCapabilityBindings; + } } /** @@ -1039,6 +1053,9 @@ public final class ShortcutInfo implements Parcelable { private int mStartingThemeResId; + @Nullable + private Map<String, Map<String, List<String>>> mCapabilityBindings; + private int mExcludedSurfaces; /** @@ -1401,6 +1418,53 @@ public final class ShortcutInfo implements Parcelable { } /** + * Associates a shortcut with a capability, and a parameter of that capability. Used when + * the shortcut is an instance of a capability. + * + * <P>This method can be called multiple times to add multiple parameters to the same + * capability. + * + * @param capability capability associated with the shortcut. e.g. actions.intent + * .START_EXERCISE. + * @param parameterName name of the parameter associated with given capability. + * e.g. exercise.name. + * @param parameterValues a list of values for that parameters. The first value will be + * the primary name, while the rest will be alternative names. If + * the values are empty, then the parameter will not be saved in + * the shortcut. + */ + @NonNull + public Builder addCapabilityBinding(@NonNull String capability, + @Nullable String parameterName, @Nullable List<String> parameterValues) { + Objects.requireNonNull(capability); + if (capability.contains("/")) { + throw new IllegalArgumentException("Illegal character '/' is found in capability"); + } + if (mCapabilityBindings == null) { + mCapabilityBindings = new ArrayMap<>(1); + } + if (!mCapabilityBindings.containsKey(capability)) { + mCapabilityBindings.put(capability, new ArrayMap<>(0)); + } + if (parameterName == null || parameterValues == null || parameterValues.isEmpty()) { + return this; + } + if (parameterName.contains("/")) { + throw new IllegalArgumentException( + "Illegal character '/' is found in parameter name"); + } + final Map<String, List<String>> params = mCapabilityBindings.get(capability); + if (!params.containsKey(parameterName)) { + params.put(parameterName, parameterValues); + return this; + } + params.put(parameterName, + Stream.of(params.get(parameterName), parameterValues) + .flatMap(Collection::stream).collect(Collectors.toList())); + return this; + } + + /** * Sets which surfaces a shortcut will be excluded from. * * If the shortcut is set to be excluded from {@link #SURFACE_LAUNCHER}, shortcuts will be @@ -2176,6 +2240,44 @@ public final class ShortcutInfo implements Parcelable { return (mExcludedSurfaces & surface) == 0; } + /** + * @hide + */ + public Map<String, Map<String, List<String>>> getCapabilityBindings() { + return mCapabilityBindings; + } + + /** + * Return true if the shortcut is or can be used in specified capability. + */ + public boolean hasCapability(@NonNull String capability) { + Objects.requireNonNull(capability); + return mCapabilityBindings != null && mCapabilityBindings.containsKey(capability); + } + + /** + * Returns the values of specified parameter in associated with given capability. + * + * @param capability capability associated with the shortcut. e.g. actions.intent + * .START_EXERCISE. + * @param parameterName name of the parameter associated with given capability. + * e.g. exercise.name. + */ + @NonNull + public List<String> getCapabilityParameterValues( + @NonNull String capability, @NonNull String parameterName) { + Objects.requireNonNull(capability); + Objects.requireNonNull(parameterName); + if (mCapabilityBindings == null) { + return Collections.emptyList(); + } + final Map<String, List<String>> param = mCapabilityBindings.get(capability); + if (param == null || !param.containsKey(parameterName)) { + return Collections.emptyList(); + } + return param.get(parameterName); + } + private ShortcutInfo(Parcel source) { final ClassLoader cl = getClass().getClassLoader(); @@ -2225,6 +2327,15 @@ public final class ShortcutInfo implements Parcelable { mIconUri = source.readString8(); mStartingThemeResName = source.readString8(); mExcludedSurfaces = source.readInt(); + + final Map<String, Map> rawCapabilityBindings = source.readHashMap( + /*loader*/ null, /*clazzKey*/ String.class, /*clazzValue*/ HashMap.class); + if (rawCapabilityBindings != null && !rawCapabilityBindings.isEmpty()) { + final Map<String, Map<String, List<String>>> capabilityBindings = + new ArrayMap<>(rawCapabilityBindings.size()); + rawCapabilityBindings.forEach(capabilityBindings::put); + mCapabilityBindings = capabilityBindings; + } } @Override @@ -2278,6 +2389,7 @@ public final class ShortcutInfo implements Parcelable { dest.writeString8(mIconUri); dest.writeString8(mStartingThemeResName); dest.writeInt(mExcludedSurfaces); + dest.writeMap(mCapabilityBindings); } public static final @NonNull Creator<ShortcutInfo> CREATOR = @@ -2529,7 +2641,8 @@ public final class ShortcutInfo implements Parcelable { long lastChangedTimestamp, int flags, int iconResId, String iconResName, String bitmapPath, String iconUri, int disabledReason, Person[] persons, LocusId locusId, - @Nullable String startingThemeResName) { + @Nullable String startingThemeResName, + @Nullable Map<String, Map<String, List<String>>> capabilityBindings) { mUserId = userId; mId = id; mPackageName = packageName; @@ -2559,5 +2672,6 @@ public final class ShortcutInfo implements Parcelable { mPersons = persons; mLocusId = locusId; mStartingThemeResName = startingThemeResName; + mCapabilityBindings = capabilityBindings; } } diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index bf7ef1b24776..15e64dffe892 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -146,8 +146,10 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String ATTR_PERSON_IS_IMPORTANT = "is-important"; private static final String NAME_CATEGORIES = "categories"; + private static final String NAME_CAPABILITY = "capability"; private static final String TAG_STRING_ARRAY_XMLUTILS = "string-array"; + private static final String TAG_MAP_XMLUTILS = "map"; private static final String ATTR_NAME_XMLUTILS = "name"; private static final String KEY_DYNAMIC = "dynamic"; @@ -1829,6 +1831,12 @@ class ShortcutPackage extends ShortcutPackageItem { } ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras()); + + final Map<String, Map<String, List<String>>> capabilityBindings = + si.getCapabilityBindings(); + if (capabilityBindings != null && !capabilityBindings.isEmpty()) { + XmlUtils.writeMapXml(si.getCapabilityBindings(), NAME_CAPABILITY, out); + } } out.endTag(null, TAG_SHORTCUT); @@ -1961,6 +1969,7 @@ class ShortcutPackage extends ShortcutPackageItem { int backupVersionCode; ArraySet<String> categories = null; ArrayList<Person> persons = new ArrayList<>(); + Map<String, Map<String, List<String>>> capabilityBindings = null; id = ShortcutService.parseStringAttribute(parser, ATTR_ID); activityComponent = ShortcutService.parseComponentNameAttribute(parser, @@ -2029,6 +2038,13 @@ class ShortcutPackage extends ShortcutPackageItem { } } continue; + case TAG_MAP_XMLUTILS: + if (NAME_CAPABILITY.equals(ShortcutService.parseStringAttribute(parser, + ATTR_NAME_XMLUTILS))) { + capabilityBindings = (Map<String, Map<String, List<String>>>) + XmlUtils.readValueXml(parser, new String[1]); + } + continue; } throw ShortcutService.throwForInvalidTag(depth, tag); } @@ -2064,7 +2080,7 @@ class ShortcutPackage extends ShortcutPackageItem { rank, extras, lastChangedTimestamp, flags, iconResId, iconResName, bitmapPath, iconUri, disabledReason, persons.toArray(new Person[persons.size()]), locusId, - splashScreenThemeResName); + splashScreenThemeResName, capabilityBindings); } private static Intent parseIntent(TypedXmlPullParser parser) diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java index b86c50b23687..63f1f2d518f7 100644 --- a/services/core/java/com/android/server/pm/ShortcutParser.java +++ b/services/core/java/com/android/server/pm/ShortcutParser.java @@ -459,7 +459,8 @@ public class ShortcutParser { disabledReason, null /* persons */, null /* locusId */, - splashScreenThemeResName); + splashScreenThemeResName, + null /* capabilityBindings */); } private static String parseCategory(ShortcutService service, AttributeSet attrs) { diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index 408d2c525f70..99edecfeed30 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -16,6 +16,7 @@ package com.android.server.pm; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundlesEqual; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertEmpty; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list; @@ -257,6 +258,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setLongLived(true) .setExtras(pb) .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen) + .addCapabilityBinding("action.intent.START_EXERCISE", + "exercise.type", list("running", "jogging")) + .addCapabilityBinding("action.intent.START_EXERCISE", + "exercise.duration", list("10m")) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); si.setBitmapPath("abc"); @@ -294,6 +299,13 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(null, si.getDisabledMessageResName()); assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen", si.getStartingThemeResName()); + assertTrue(si.hasCapability("action.intent.START_EXERCISE")); + assertFalse(si.hasCapability("")); + assertFalse(si.hasCapability("random")); + assertEquals(list("running", "jogging"), si.getCapabilityParameterValues( + "action.intent.START_EXERCISE", "exercise.type")); + assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "")); + assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "random")); } public void testShortcutInfoParcel_resId() { @@ -947,6 +959,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setRank(123) .setExtras(pb) .setLocusId(new LocusId("1.2.3.4.5")) + .addCapabilityBinding("action.intent.START_EXERCISE", + "exercise.type", list("running", "jogging")) + .addCapabilityBinding("action.intent.START_EXERCISE", + "exercise.duration", list("10m")) .build(); sorig.setTimestamp(mInjectedCurrentTimeMillis); @@ -1008,6 +1024,14 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertNull(si.getIconUri()); assertTrue(si.getLastChangedTimestamp() < now); + assertTrue(si.hasCapability("action.intent.START_EXERCISE")); + assertFalse(si.hasCapability("")); + assertFalse(si.hasCapability("random")); + assertEquals(list("running", "jogging"), si.getCapabilityParameterValues( + "action.intent.START_EXERCISE", "exercise.type")); + assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "")); + assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "random")); + // Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts // to test it. si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index a834e2b6cc5a..12cd834d1d66 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -394,7 +394,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { "disabledMessage", 0, "disabledMessageResName", null, null, 0, null, 0, 0, 0, "iconResName", "bitmapPath", null, 0, - null, null, null); + null, null, null, null); return si; } |