summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Pinyao Ting <pinyaoting@google.com> 2021-12-16 00:30:52 +0000
committer Pinyao Ting <pinyaoting@google.com> 2022-01-19 05:06:10 +0000
commit8c9f13f1e1724282e16523654c7be73c7c770dc5 (patch)
treec1a7c22c3026fb5154e0f35c9e9ff8690c372b92
parent2e6243d9000e0e9ce47b1036a51e796a5f81240c (diff)
Adds capability related setters in ShortcutInfo.Builder
Bug: 202872648 Test: atest ShortcutManagerTest2 Change-Id: Idbb79779564b9fce704be19a13695b6e08cb1088
-rw-r--r--core/api/current.txt3
-rw-r--r--core/java/android/content/pm/AppSearchShortcutInfo.java2
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java120
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java18
-rw-r--r--services/core/java/com/android/server/pm/ShortcutParser.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java24
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java2
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;
}