Merge "ShortcutManager: Support secondary text field." into nyc-dev

This commit is contained in:
Makoto Onuki
2016-03-30 15:30:16 +00:00
committed by Android (Google) Code Review
8 changed files with 280 additions and 13 deletions

View File

@@ -10001,6 +10001,7 @@ package android.content.pm {
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
method public java.lang.String getPackageName();
method public java.lang.String getText();
method public java.lang.String getTitle();
method public int getWeight();
method public boolean hasIconFile();
@@ -10028,6 +10029,7 @@ package android.content.pm {
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 setText(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setWeight(int);
}

View File

@@ -10399,6 +10399,7 @@ package android.content.pm {
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
method public java.lang.String getPackageName();
method public java.lang.String getText();
method public java.lang.String getTitle();
method public int getWeight();
method public boolean hasIconFile();
@@ -10426,6 +10427,7 @@ package android.content.pm {
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 setText(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setWeight(int);
}

View File

@@ -10011,6 +10011,7 @@ package android.content.pm {
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
method public java.lang.String getPackageName();
method public java.lang.String getText();
method public java.lang.String getTitle();
method public int getWeight();
method public boolean hasIconFile();
@@ -10038,6 +10039,7 @@ package android.content.pm {
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 setText(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setWeight(int);
}

View File

@@ -118,6 +118,9 @@ public class ShortcutInfo implements Parcelable {
@NonNull
private String mTitle;
@Nullable
private String mText;
/**
* Intent *with extras removed*.
*/
@@ -157,6 +160,7 @@ public class ShortcutInfo implements Parcelable {
mActivityComponent = b.mActivityComponent;
mIcon = b.mIcon;
mTitle = b.mTitle;
mText = b.mText;
mIntent = b.mIntent;
if (mIntent != null) {
final Bundle intentExtras = mIntent.getExtras();
@@ -176,6 +180,7 @@ public class ShortcutInfo implements Parcelable {
* @hide
*/
public void enforceMandatoryFields() {
Preconditions.checkStringNotEmpty(mId, "Shortcut ID must be provided");
Preconditions.checkStringNotEmpty(mTitle, "Shortcut title must be provided");
Preconditions.checkNotNull(mIntent, "Shortcut Intent must be provided");
}
@@ -195,16 +200,17 @@ public class ShortcutInfo implements Parcelable {
if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
mIcon = source.mIcon;
mBitmapPath = source.mBitmapPath;
mIconResourceId = source.mIconResourceId;
}
mTitle = source.mTitle;
mText = source.mText;
if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
mIntent = source.mIntent;
mIntentPersistableExtras = source.mIntentPersistableExtras;
}
mWeight = source.mWeight;
mExtras = source.mExtras;
mIconResourceId = source.mIconResourceId;
} else {
// Set this bit.
mFlags |= FLAG_KEY_FIELDS_ONLY;
@@ -244,6 +250,9 @@ public class ShortcutInfo implements Parcelable {
if (source.mTitle != null) {
mTitle = source.mTitle;
}
if (source.mText != null) {
mText = source.mText;
}
if (source.mIntent != null) {
mIntent = source.mIntent;
mIntentPersistableExtras = source.mIntentPersistableExtras;
@@ -305,6 +314,8 @@ public class ShortcutInfo implements Parcelable {
private String mTitle;
private String mText;
private Intent mIntent;
private int mWeight;
@@ -367,6 +378,15 @@ public class ShortcutInfo implements Parcelable {
return this;
}
/**
* Sets the text of a shortcut. This is an optional field.
*/
@NonNull
public Builder setText(@NonNull String text) {
mText = Preconditions.checkStringNotEmpty(text, "text");
return this;
}
/**
* Sets the intent of a shortcut. This is a mandatory field. The extras must only contain
* persistable information. (See {@link PersistableBundle}).
@@ -456,6 +476,14 @@ public class ShortcutInfo implements Parcelable {
return mTitle;
}
/**
* Return the shortcut text.
*/
@Nullable
public String getText() {
return mText;
}
/**
* Return the intent.
*
@@ -630,6 +658,7 @@ public class ShortcutInfo implements Parcelable {
mActivityComponent = source.readParcelable(cl);
mIcon = source.readParcelable(cl);
mTitle = source.readString();
mText = source.readString();
mIntent = source.readParcelable(cl);
mIntentPersistableExtras = source.readParcelable(cl);
mWeight = source.readInt();
@@ -647,6 +676,7 @@ public class ShortcutInfo implements Parcelable {
dest.writeParcelable(mActivityComponent, flags);
dest.writeParcelable(mIcon, flags);
dest.writeString(mTitle);
dest.writeString(mText);
dest.writeParcelable(mIntent, flags);
dest.writeParcelable(mIntentPersistableExtras, flags);
dest.writeInt(mWeight);
@@ -708,6 +738,9 @@ public class ShortcutInfo implements Parcelable {
sb.append(", title=");
sb.append(secure ? "***" : mTitle);
sb.append(", text=");
sb.append(secure ? "***" : mText);
sb.append(", icon=");
sb.append(mIcon);
@@ -744,7 +777,8 @@ public class ShortcutInfo implements Parcelable {
/** @hide */
public ShortcutInfo(String id, String packageName, ComponentName activityComponent,
Icon icon, String title, Intent intent, PersistableBundle intentPersistableExtras,
Icon icon, String title, String text, Intent intent,
PersistableBundle intentPersistableExtras,
int weight, PersistableBundle extras, long lastChangedTimestamp,
int flags, int iconResId, String bitmapPath) {
mId = id;
@@ -752,6 +786,7 @@ public class ShortcutInfo implements Parcelable {
mActivityComponent = activityComponent;
mIcon = icon;
mTitle = title;
mText = text;
mIntent = intent;
mIntentPersistableExtras = intentPersistableExtras;
mWeight = weight;

View File

@@ -55,6 +55,7 @@ class ShortcutPackage extends ShortcutPackageItem {
private static final String ATTR_ID = "id";
private static final String ATTR_ACTIVITY = "activity";
private static final String ATTR_TITLE = "title";
private static final String ATTR_TEXT = "text";
private static final String ATTR_INTENT = "intent";
private static final String ATTR_WEIGHT = "weight";
private static final String ATTR_TIMESTAMP = "timestamp";
@@ -439,6 +440,7 @@ class ShortcutPackage extends ShortcutPackageItem {
ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivityComponent());
// writeAttr(out, "icon", si.getIcon()); // We don't save it.
ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
ShortcutService.writeAttr(out, ATTR_TEXT, si.getText());
ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras());
ShortcutService.writeAttr(out, ATTR_WEIGHT, si.getWeight());
ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
@@ -515,6 +517,7 @@ class ShortcutPackage extends ShortcutPackageItem {
ComponentName activityComponent;
// Icon icon;
String title;
String text;
Intent intent;
PersistableBundle intentPersistableExtras = null;
int weight;
@@ -528,6 +531,7 @@ class ShortcutPackage extends ShortcutPackageItem {
activityComponent = ShortcutService.parseComponentNameAttribute(parser,
ATTR_ACTIVITY);
title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT);
intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT);
weight = (int) ShortcutService.parseLongAttribute(parser, ATTR_WEIGHT);
lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP);
@@ -559,7 +563,7 @@ class ShortcutPackage extends ShortcutPackageItem {
throw ShortcutService.throwForInvalidTag(depth, tag);
}
return new ShortcutInfo(
id, packageName, activityComponent, /* icon =*/ null, title, intent,
id, packageName, activityComponent, /* icon =*/ null, title, text, intent,
intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
iconRes, bitmapPath);
}

View File

@@ -16,9 +16,16 @@
*/
package com.android.server.pm;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.test.AndroidTestCase;
import com.android.internal.util.Preconditions;
import com.android.server.testutis.TestUtils;
/**
@@ -33,12 +40,232 @@ import com.android.server.testutis.TestUtils;
*/
public class ShortcutInfoTest extends AndroidTestCase {
public void testNoId() {
public void testMissingMandatoryFields() {
TestUtils.assertExpectException(
IllegalArgumentException.class,
"ID must be provided",
() -> new ShortcutInfo.Builder(mContext).build());
TestUtils.assertExpectException(
IllegalArgumentException.class,
"title must be provided",
() -> new ShortcutInfo.Builder(mContext).setId("id").build()
.enforceMandatoryFields());
TestUtils.assertExpectException(
NullPointerException.class,
"Intent must be provided",
() -> new ShortcutInfo.Builder(mContext).setId("id").setTitle("x").build()
.enforceMandatoryFields());
}
// TODO Add more tests.
private ShortcutInfo parceled(ShortcutInfo si) {
Parcel p = Parcel.obtain();
p.writeParcelable(si, 0);
p.setDataPosition(0);
ShortcutInfo si2 = p.readParcelable(getClass().getClassLoader());
p.recycle();
return si2;
}
private Intent makeIntent(String action, Object... bundleKeysAndValues) {
final Intent intent = new Intent(action);
intent.replaceExtras(ShortcutManagerTest.makeBundle(bundleKeysAndValues));
return intent;
}
public void testParcel() {
ShortcutInfo si = parceled(new ShortcutInfo.Builder(getContext())
.setId("id")
.setTitle("title")
.setIntent(makeIntent("action"))
.build());
assertEquals(getContext().getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals("title", si.getTitle());
assertEquals("action", si.getIntent().getAction());
PersistableBundle pb = new PersistableBundle();
pb.putInt("k", 1);
si = new ShortcutInfo.Builder(getContext())
.setId("id")
.setActivityComponent(new ComponentName("a", "b"))
.setIcon(Icon.createWithContentUri("content://a.b.c/"))
.setTitle("title")
.setText("text")
.setIntent(makeIntent("action", "key", "val"))
.setWeight(123)
.setExtras(pb)
.build();
si.addFlags(ShortcutInfo.FLAG_PINNED);
si.setBitmapPath("abc");
si.setIconResourceId(456);
si = parceled(si);
assertEquals(getContext().getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals("content://a.b.c/", si.getIcon().getUriString());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
assertEquals("action", si.getIntent().getAction());
assertEquals("val", si.getIntent().getStringExtra("key"));
assertEquals(123, si.getWeight());
assertEquals(1, si.getExtras().getInt("k"));
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
assertEquals("abc", si.getBitmapPath());
assertEquals(456, si.getIconResourceId());
}
public void testClone() {
PersistableBundle pb = new PersistableBundle();
pb.putInt("k", 1);
ShortcutInfo sorig = new ShortcutInfo.Builder(getContext())
.setId("id")
.setActivityComponent(new ComponentName("a", "b"))
.setIcon(Icon.createWithContentUri("content://a.b.c/"))
.setTitle("title")
.setText("text")
.setIntent(makeIntent("action", "key", "val"))
.setWeight(123)
.setExtras(pb)
.build();
sorig.addFlags(ShortcutInfo.FLAG_PINNED);
sorig.setBitmapPath("abc");
sorig.setIconResourceId(456);
ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
assertEquals(getContext().getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals("content://a.b.c/", si.getIcon().getUriString());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
assertEquals("action", si.getIntent().getAction());
assertEquals("val", si.getIntent().getStringExtra("key"));
assertEquals(123, si.getWeight());
assertEquals(1, si.getExtras().getInt("k"));
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
assertEquals("abc", si.getBitmapPath());
assertEquals(456, si.getIconResourceId());
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
assertEquals(getContext().getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals(null, si.getIcon());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
assertEquals("action", si.getIntent().getAction());
assertEquals("val", si.getIntent().getStringExtra("key"));
assertEquals(123, si.getWeight());
assertEquals(1, si.getExtras().getInt("k"));
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
assertEquals(null, si.getBitmapPath());
assertEquals(0, si.getIconResourceId());
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
assertEquals(getContext().getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals(null, si.getIcon());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
assertEquals(null, si.getIntent());
assertEquals(123, si.getWeight());
assertEquals(1, si.getExtras().getInt("k"));
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
assertEquals(null, si.getBitmapPath());
assertEquals(0, si.getIconResourceId());
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
assertEquals(getContext().getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(null, si.getActivityComponent());
assertEquals(null, si.getIcon());
assertEquals(null, si.getTitle());
assertEquals(null, si.getText());
assertEquals(null, si.getIntent());
assertEquals(0, si.getWeight());
assertEquals(null, si.getExtras());
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags());
assertEquals(null, si.getBitmapPath());
assertEquals(0, si.getIconResourceId());
}
public void testCopyNonNullFieldsFrom() {
PersistableBundle pb = new PersistableBundle();
pb.putInt("k", 1);
ShortcutInfo sorig = new ShortcutInfo.Builder(getContext())
.setId("id")
.setActivityComponent(new ComponentName("a", "b"))
.setIcon(Icon.createWithContentUri("content://a.b.c/"))
.setTitle("title")
.setText("text")
.setIntent(makeIntent("action", "key", "val"))
.setWeight(123)
.setExtras(pb)
.build();
sorig.addFlags(ShortcutInfo.FLAG_PINNED);
sorig.setBitmapPath("abc");
sorig.setIconResourceId(456);
ShortcutInfo si;
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
.setActivityComponent(new ComponentName("x", "y")).build());
assertEquals(new ComponentName("x", "y"), si.getActivityComponent());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
.setIcon(Icon.createWithContentUri("content://x.y.z/")).build());
assertEquals("content://x.y.z/", si.getIcon().getUriString());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
.setTitle("xyz").build());
assertEquals("xyz", si.getTitle());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
.setText("xxx").build());
assertEquals("xxx", si.getText());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
.setIntent(makeIntent("action2")).build());
assertEquals("action2", si.getIntent().getAction());
assertEquals(null, si.getIntent().getStringExtra("key"));
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
.setIntent(makeIntent("action3", "key", "x")).build());
assertEquals("action3", si.getIntent().getAction());
assertEquals("x", si.getIntent().getStringExtra("key"));
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
.setWeight(999).build());
assertEquals(999, si.getWeight());
PersistableBundle pb2 = new PersistableBundle();
pb2.putInt("x", 99);
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
.setExtras(pb2).build());
assertEquals(99, si.getExtras().getInt("x"));
}
}

View File

@@ -646,7 +646,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
runTestOnUiThread(() -> {});
}
private static Bundle makeBundle(Object... keysAndValues) {
public static Bundle makeBundle(Object... keysAndValues) {
Preconditions.checkState((keysAndValues.length % 2) == 0);
if (keysAndValues.length == 0) {

View File

@@ -23,20 +23,15 @@ public class TestUtils {
private TestUtils() {
}
public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
Runnable r) {
assertExpectException(expectedExceptionType, null, r);
}
public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
String expectedExceptionMessageRegex, Runnable r) {
try {
r.run();
Assert.fail("Expected exception type " + expectedExceptionType.getClass().getName()
Assert.fail("Expected exception type " + expectedExceptionType.getName()
+ " was not thrown");
} catch (Throwable e) {
Assert.assertTrue(
"Expected exception type was " + expectedExceptionType.getClass().getName()
"Expected exception type was " + expectedExceptionType.getName()
+ " but caught " + e.getClass().getName(),
expectedExceptionType.isAssignableFrom(e.getClass()));
if (expectedExceptionMessageRegex != null) {