Merge "Adds capability related setters in ShortcutInfo.Builder"

This commit is contained in:
Pinyao Ting
2022-01-20 19:34:14 +00:00
committed by Android (Google) Code Review
7 changed files with 165 additions and 7 deletions

View File

@@ -13475,6 +13475,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();
@@ -13489,6 +13490,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();
@@ -13513,6 +13515,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>);

View File

@@ -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();

View File

@@ -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;
/**
@@ -1400,6 +1417,53 @@ public final class ShortcutInfo implements Parcelable {
return this;
}
/**
* 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.
*
@@ -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;
}
}

View File

@@ -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)

View File

@@ -459,7 +459,8 @@ public class ShortcutParser {
disabledReason,
null /* persons */,
null /* locusId */,
splashScreenThemeResName);
splashScreenThemeResName,
null /* capabilityBindings */);
}
private static String parseCategory(ShortcutService service, AttributeSet attrs) {

View File

@@ -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);

View File

@@ -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;
}