Merge \\"ShortcutManager: deal with changing resource IDs on app update\\" into nyc-mr1-dev am: 3bb4366006
am: a3a56ab9d1
Change-Id: Id5e39a35db803223a89100a8e23a7b3309cd9dd3
This commit is contained in:
@@ -10093,19 +10093,7 @@ package android.content.pm {
|
||||
method public boolean isManifestShortcut();
|
||||
method public boolean isPinned();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
|
||||
field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3
|
||||
field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4
|
||||
field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
|
||||
field public static final int FLAG_DISABLED = 64; // 0x40
|
||||
field public static final int FLAG_DYNAMIC = 1; // 0x1
|
||||
field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
|
||||
field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
|
||||
field public static final int FLAG_IMMUTABLE = 256; // 0x100
|
||||
field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
|
||||
field public static final int FLAG_MANIFEST = 32; // 0x20
|
||||
field public static final int FLAG_PINNED = 2; // 0x2
|
||||
field public static final int FLAG_STRINGS_RESOLVED = 128; // 0x80
|
||||
field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
|
||||
}
|
||||
|
||||
|
||||
@@ -10516,19 +10516,7 @@ package android.content.pm {
|
||||
method public boolean isManifestShortcut();
|
||||
method public boolean isPinned();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
|
||||
field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3
|
||||
field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4
|
||||
field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
|
||||
field public static final int FLAG_DISABLED = 64; // 0x40
|
||||
field public static final int FLAG_DYNAMIC = 1; // 0x1
|
||||
field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
|
||||
field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
|
||||
field public static final int FLAG_IMMUTABLE = 256; // 0x100
|
||||
field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
|
||||
field public static final int FLAG_MANIFEST = 32; // 0x20
|
||||
field public static final int FLAG_PINNED = 2; // 0x2
|
||||
field public static final int FLAG_STRINGS_RESOLVED = 128; // 0x80
|
||||
field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
|
||||
}
|
||||
|
||||
|
||||
@@ -10106,19 +10106,7 @@ package android.content.pm {
|
||||
method public boolean isManifestShortcut();
|
||||
method public boolean isPinned();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
|
||||
field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3
|
||||
field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4
|
||||
field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
|
||||
field public static final int FLAG_DISABLED = 64; // 0x40
|
||||
field public static final int FLAG_DYNAMIC = 1; // 0x1
|
||||
field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
|
||||
field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
|
||||
field public static final int FLAG_IMMUTABLE = 256; // 0x100
|
||||
field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
|
||||
field public static final int FLAG_MANIFEST = 32; // 0x20
|
||||
field public static final int FLAG_PINNED = 2; // 0x2
|
||||
field public static final int FLAG_STRINGS_RESOLVED = 128; // 0x80
|
||||
field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ import android.annotation.UserIdInt;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.Resources.NotFoundException;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
@@ -31,7 +31,9 @@ import android.os.Parcelable;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -54,31 +56,37 @@ import java.util.Set;
|
||||
* @see {@link ShortcutManager}.
|
||||
*/
|
||||
public final class ShortcutInfo implements Parcelable {
|
||||
/* @hide */
|
||||
static final String TAG = "Shortcut";
|
||||
|
||||
private static final String RES_TYPE_STRING = "string";
|
||||
|
||||
private static final String ANDROID_PACKAGE_NAME = "android";
|
||||
|
||||
/** @hide */
|
||||
public static final int FLAG_DYNAMIC = 1 << 0;
|
||||
|
||||
/* @hide */
|
||||
/** @hide */
|
||||
public static final int FLAG_PINNED = 1 << 1;
|
||||
|
||||
/* @hide */
|
||||
/** @hide */
|
||||
public static final int FLAG_HAS_ICON_RES = 1 << 2;
|
||||
|
||||
/* @hide */
|
||||
/** @hide */
|
||||
public static final int FLAG_HAS_ICON_FILE = 1 << 3;
|
||||
|
||||
/* @hide */
|
||||
/** @hide */
|
||||
public static final int FLAG_KEY_FIELDS_ONLY = 1 << 4;
|
||||
|
||||
/* @hide */
|
||||
/** @hide */
|
||||
public static final int FLAG_MANIFEST = 1 << 5;
|
||||
|
||||
/* @hide */
|
||||
/** @hide */
|
||||
public static final int FLAG_DISABLED = 1 << 6;
|
||||
|
||||
/* @hide */
|
||||
/** @hide */
|
||||
public static final int FLAG_STRINGS_RESOLVED = 1 << 7;
|
||||
|
||||
/* @hide */
|
||||
/** @hide */
|
||||
public static final int FLAG_IMMUTABLE = 1 << 8;
|
||||
|
||||
/** @hide */
|
||||
@@ -99,20 +107,24 @@ public final class ShortcutInfo implements Parcelable {
|
||||
|
||||
// Cloning options.
|
||||
|
||||
/* @hide */
|
||||
/** @hide */
|
||||
private static final int CLONE_REMOVE_ICON = 1 << 0;
|
||||
|
||||
/* @hide */
|
||||
/** @hide */
|
||||
private static final int CLONE_REMOVE_INTENT = 1 << 1;
|
||||
|
||||
/* @hide */
|
||||
/** @hide */
|
||||
public static final int CLONE_REMOVE_NON_KEY_INFO = 1 << 2;
|
||||
|
||||
/* @hide */
|
||||
public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON;
|
||||
/** @hide */
|
||||
public static final int CLONE_REMOVE_RES_NAMES = 1 << 3;
|
||||
|
||||
/* @hide */
|
||||
public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT;
|
||||
/** @hide */
|
||||
public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON | CLONE_REMOVE_RES_NAMES;
|
||||
|
||||
/** @hide */
|
||||
public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT
|
||||
| CLONE_REMOVE_RES_NAMES;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(flag = true,
|
||||
@@ -120,6 +132,7 @@ public final class ShortcutInfo implements Parcelable {
|
||||
CLONE_REMOVE_ICON,
|
||||
CLONE_REMOVE_INTENT,
|
||||
CLONE_REMOVE_NON_KEY_INFO,
|
||||
CLONE_REMOVE_RES_NAMES,
|
||||
CLONE_REMOVE_FOR_CREATOR,
|
||||
CLONE_REMOVE_FOR_LAUNCHER
|
||||
})
|
||||
@@ -144,16 +157,22 @@ public final class ShortcutInfo implements Parcelable {
|
||||
|
||||
private int mTitleResId;
|
||||
|
||||
private String mTitleResName;
|
||||
|
||||
@Nullable
|
||||
private CharSequence mTitle;
|
||||
|
||||
private int mTextResId;
|
||||
|
||||
private String mTextResName;
|
||||
|
||||
@Nullable
|
||||
private CharSequence mText;
|
||||
|
||||
private int mDisabledMessageResId;
|
||||
|
||||
private String mDisabledMessageResName;
|
||||
|
||||
@Nullable
|
||||
private CharSequence mDisabledMessage;
|
||||
|
||||
@@ -184,7 +203,9 @@ public final class ShortcutInfo implements Parcelable {
|
||||
private int mFlags;
|
||||
|
||||
// Internal use only.
|
||||
private int mIconResourceId;
|
||||
private int mIconResId;
|
||||
|
||||
private String mIconResName;
|
||||
|
||||
// Internal use only.
|
||||
@Nullable
|
||||
@@ -251,7 +272,7 @@ public final class ShortcutInfo implements Parcelable {
|
||||
mLastChangedTimestamp = source.mLastChangedTimestamp;
|
||||
|
||||
// Just always keep it since it's cheep.
|
||||
mIconResourceId = source.mIconResourceId;
|
||||
mIconResId = source.mIconResId;
|
||||
|
||||
if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
|
||||
mActivity = source.mActivity;
|
||||
@@ -274,36 +295,234 @@ public final class ShortcutInfo implements Parcelable {
|
||||
}
|
||||
mRank = source.mRank;
|
||||
mExtras = source.mExtras;
|
||||
|
||||
if ((cloneFlags & CLONE_REMOVE_RES_NAMES) == 0) {
|
||||
mTitleResName = source.mTitleResName;
|
||||
mTextResName = source.mTextResName;
|
||||
mDisabledMessageResName = source.mDisabledMessageResName;
|
||||
mIconResName = source.mIconResName;
|
||||
}
|
||||
} else {
|
||||
// Set this bit.
|
||||
mFlags |= FLAG_KEY_FIELDS_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void resolveStringsRequiringCrossUser(Context context) throws NameNotFoundException {
|
||||
/**
|
||||
* Load a string resource from the publisher app.
|
||||
*
|
||||
* @param resId resource ID
|
||||
* @param defValue default value to be returned when the specified resource isn't found.
|
||||
*/
|
||||
private CharSequence getResourceString(Resources res, int resId, CharSequence defValue) {
|
||||
try {
|
||||
return res.getString(resId);
|
||||
} catch (NotFoundException e) {
|
||||
Log.e(TAG, "Resource for ID=" + resId + " not found in package " + mPackageName);
|
||||
return defValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the string resources for the text fields and set them to the actual value fields.
|
||||
* This will set {@link #FLAG_STRINGS_RESOLVED}.
|
||||
*
|
||||
* @param res {@link Resources} for the publisher. Must have been loaded with
|
||||
* {@link PackageManager#getResourcesForApplicationAsUser}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void resolveResourceStrings(@NonNull Resources res) {
|
||||
mFlags |= FLAG_STRINGS_RESOLVED;
|
||||
|
||||
if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)) {
|
||||
return; // Bail early.
|
||||
}
|
||||
final Resources res = context.getPackageManager().getResourcesForApplicationAsUser(
|
||||
mPackageName, mUserId);
|
||||
|
||||
if (mTitleResId != 0) {
|
||||
mTitle = res.getString(mTitleResId);
|
||||
mTitleResId = 0;
|
||||
mTitle = getResourceString(res, mTitleResId, mTitle);
|
||||
}
|
||||
if (mTextResId != 0) {
|
||||
mText = res.getString(mTextResId);
|
||||
mTextResId = 0;
|
||||
mText = getResourceString(res, mTextResId, mText);
|
||||
}
|
||||
if (mDisabledMessageResId != 0) {
|
||||
mDisabledMessage = res.getString(mDisabledMessageResId);
|
||||
mDisabledMessageResId = 0;
|
||||
mDisabledMessage = getResourceString(res, mDisabledMessageResId, mDisabledMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up resource name for a given resource ID.
|
||||
*
|
||||
* @return a simple resource name (e.g. "text_1") when {@code withType} is false, or with the
|
||||
* type (e.g. "string/text_1").
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static String lookUpResourceName(@NonNull Resources res, int resId, boolean withType,
|
||||
@NonNull String packageName) {
|
||||
if (resId == 0) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final String fullName = res.getResourceName(resId);
|
||||
|
||||
if (ANDROID_PACKAGE_NAME.equals(getResourcePackageName(fullName))) {
|
||||
// If it's a framework resource, the value won't change, so just return the ID
|
||||
// value as a string.
|
||||
return String.valueOf(resId);
|
||||
}
|
||||
return withType ? getResourceTypeAndEntryName(fullName)
|
||||
: getResourceEntryName(fullName);
|
||||
} catch (NotFoundException e) {
|
||||
Log.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName
|
||||
+ ". Resource IDs may change when the application is upgraded, and the system"
|
||||
+ " may not be able to find the correct resource.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the package name from a fully-donated resource name.
|
||||
* e.g. "com.android.app1:drawable/icon1" -> "com.android.app1"
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static String getResourcePackageName(@NonNull String fullResourceName) {
|
||||
final int p1 = fullResourceName.indexOf(':');
|
||||
if (p1 < 0) {
|
||||
return null;
|
||||
}
|
||||
return fullResourceName.substring(0, p1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the type name from a fully-donated resource name.
|
||||
* e.g. "com.android.app1:drawable/icon1" -> "drawable"
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static String getResourceTypeName(@NonNull String fullResourceName) {
|
||||
final int p1 = fullResourceName.indexOf(':');
|
||||
if (p1 < 0) {
|
||||
return null;
|
||||
}
|
||||
final int p2 = fullResourceName.indexOf('/', p1 + 1);
|
||||
if (p2 < 0) {
|
||||
return null;
|
||||
}
|
||||
return fullResourceName.substring(p1 + 1, p2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the type name + the entry name from a fully-donated resource name.
|
||||
* e.g. "com.android.app1:drawable/icon1" -> "drawable/icon1"
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static String getResourceTypeAndEntryName(@NonNull String fullResourceName) {
|
||||
final int p1 = fullResourceName.indexOf(':');
|
||||
if (p1 < 0) {
|
||||
return null;
|
||||
}
|
||||
return fullResourceName.substring(p1 + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the entry name from a fully-donated resource name.
|
||||
* e.g. "com.android.app1:drawable/icon1" -> "icon1"
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static String getResourceEntryName(@NonNull String fullResourceName) {
|
||||
final int p1 = fullResourceName.indexOf('/');
|
||||
if (p1 < 0) {
|
||||
return null;
|
||||
}
|
||||
return fullResourceName.substring(p1 + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resource ID for a given resource ID.
|
||||
*
|
||||
* Basically its' a wrapper over {@link Resources#getIdentifier(String, String, String)}, except
|
||||
* if {@code resourceName} is an integer then it'll just return its value. (Which also the
|
||||
* aforementioned method would do internally, but not documented, so doing here explicitly.)
|
||||
*
|
||||
* @param res {@link Resources} for the publisher. Must have been loaded with
|
||||
* {@link PackageManager#getResourcesForApplicationAsUser}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static int lookUpResourceId(@NonNull Resources res, @Nullable String resourceName,
|
||||
@Nullable String resourceType, String packageName) {
|
||||
if (resourceName == null) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
try {
|
||||
// It the name can be parsed as an integer, just use it.
|
||||
return Integer.parseInt(resourceName);
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
|
||||
return res.getIdentifier(resourceName, resourceType, packageName);
|
||||
} catch (NotFoundException e) {
|
||||
Log.e(TAG, "Resource ID for name=" + resourceName + " not found in package "
|
||||
+ packageName);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up resource names from the resource IDs for the icon res and the text fields, and fill
|
||||
* in the resource name fields.
|
||||
*
|
||||
* @param res {@link Resources} for the publisher. Must have been loaded with
|
||||
* {@link PackageManager#getResourcesForApplicationAsUser}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void lookupAndFillInResourceNames(@NonNull Resources res) {
|
||||
if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)
|
||||
&& (mIconResId == 0)) {
|
||||
return; // Bail early.
|
||||
}
|
||||
|
||||
// We don't need types for strings because their types are always "string".
|
||||
mTitleResName = lookUpResourceName(res, mTitleResId, /*withType=*/ false, mPackageName);
|
||||
mTextResName = lookUpResourceName(res, mTextResId, /*withType=*/ false, mPackageName);
|
||||
mDisabledMessageResName = lookUpResourceName(res, mDisabledMessageResId,
|
||||
/*withType=*/ false, mPackageName);
|
||||
|
||||
// But icons have multiple possible types, so include the type.
|
||||
mIconResName = lookUpResourceName(res, mIconResId, /*withType=*/ true, mPackageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up resource IDs from the resource names for the icon res and the text fields, and fill
|
||||
* in the resource ID fields.
|
||||
*
|
||||
* This is called when an app is updated.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void lookupAndFillInResourceIds(@NonNull Resources res) {
|
||||
if ((mTitleResName == null) && (mTextResName == null) && (mDisabledMessageResName == null)
|
||||
&& (mIconResName == null)) {
|
||||
return; // Bail early.
|
||||
}
|
||||
|
||||
mTitleResId = lookUpResourceId(res, mTitleResName, RES_TYPE_STRING, mPackageName);
|
||||
mTextResId = lookUpResourceId(res, mTextResName, RES_TYPE_STRING, mPackageName);
|
||||
mDisabledMessageResId = lookUpResourceId(res, mDisabledMessageResName, RES_TYPE_STRING,
|
||||
mPackageName);
|
||||
|
||||
// mIconResName already contains the type, so the third argument is not needed.
|
||||
mIconResId = lookUpResourceId(res, mIconResName, null, mPackageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a {@link ShortcutInfo}, optionally removing fields.
|
||||
* @hide
|
||||
@@ -344,27 +563,38 @@ public final class ShortcutInfo implements Parcelable {
|
||||
|
||||
if (source.mIcon != null) {
|
||||
mIcon = source.mIcon;
|
||||
|
||||
mIconResId = 0;
|
||||
mIconResName = null;
|
||||
mBitmapPath = null;
|
||||
}
|
||||
if (source.mTitle != null) {
|
||||
mTitle = source.mTitle;
|
||||
mTitleResId = 0;
|
||||
mTitleResName = null;
|
||||
} else if (source.mTitleResId != 0) {
|
||||
mTitle = null;
|
||||
mTitleResId = source.mTitleResId;
|
||||
mTitleResName = null;
|
||||
}
|
||||
|
||||
if (source.mText != null) {
|
||||
mText = source.mText;
|
||||
mTextResId = 0;
|
||||
mTextResName = null;
|
||||
} else if (source.mTextResId != 0) {
|
||||
mText = null;
|
||||
mTextResId = source.mTextResId;
|
||||
mTextResName = null;
|
||||
}
|
||||
if (source.mDisabledMessage != null) {
|
||||
mDisabledMessage = source.mDisabledMessage;
|
||||
mDisabledMessageResId = 0;
|
||||
mDisabledMessageResName = null;
|
||||
} else if (source.mDisabledMessageResId != 0) {
|
||||
mDisabledMessage = null;
|
||||
mDisabledMessageResId = source.mDisabledMessageResId;
|
||||
mDisabledMessageResName = null;
|
||||
}
|
||||
if (source.mCategories != null) {
|
||||
mCategories = clone(source.mCategories);
|
||||
@@ -983,14 +1213,17 @@ public final class ShortcutInfo implements Parcelable {
|
||||
|
||||
/** @hide */
|
||||
public void setIconResourceId(int iconResourceId) {
|
||||
mIconResourceId = iconResourceId;
|
||||
if (mIconResId != iconResourceId) {
|
||||
mIconResName = null;
|
||||
}
|
||||
mIconResId = iconResourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource ID for the icon, valid only when {@link #hasIconResource()} } is true.
|
||||
*/
|
||||
public int getIconResourceId() {
|
||||
return mIconResourceId;
|
||||
return mIconResId;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@@ -1005,6 +1238,9 @@ public final class ShortcutInfo implements Parcelable {
|
||||
|
||||
/** @hide */
|
||||
public void setDisabledMessageResId(int disabledMessageResId) {
|
||||
if (mDisabledMessageResId != disabledMessageResId) {
|
||||
mDisabledMessageResName = null;
|
||||
}
|
||||
mDisabledMessageResId = disabledMessageResId;
|
||||
mDisabledMessage = null;
|
||||
}
|
||||
@@ -1013,6 +1249,47 @@ public final class ShortcutInfo implements Parcelable {
|
||||
public void setDisabledMessage(String disabledMessage) {
|
||||
mDisabledMessage = disabledMessage;
|
||||
mDisabledMessageResId = 0;
|
||||
mDisabledMessageResName = null;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public String getTitleResName() {
|
||||
return mTitleResName;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setTitleResName(String titleResName) {
|
||||
mTitleResName = titleResName;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public String getTextResName() {
|
||||
return mTextResName;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setTextResName(String textResName) {
|
||||
mTextResName = textResName;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public String getDisabledMessageResName() {
|
||||
return mDisabledMessageResName;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setDisabledMessageResName(String disabledMessageResName) {
|
||||
mDisabledMessageResName = disabledMessageResName;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public String getIconResName() {
|
||||
return mIconResName;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setIconResName(String iconResName) {
|
||||
mIconResName = iconResName;
|
||||
}
|
||||
|
||||
private ShortcutInfo(Parcel source) {
|
||||
@@ -1035,9 +1312,14 @@ public final class ShortcutInfo implements Parcelable {
|
||||
mExtras = source.readParcelable(cl);
|
||||
mLastChangedTimestamp = source.readLong();
|
||||
mFlags = source.readInt();
|
||||
mIconResourceId = source.readInt();
|
||||
mIconResId = source.readInt();
|
||||
mBitmapPath = source.readString();
|
||||
|
||||
mIconResName = source.readString();
|
||||
mTitleResName = source.readString();
|
||||
mTextResName = source.readString();
|
||||
mDisabledMessageResName = source.readString();
|
||||
|
||||
int N = source.readInt();
|
||||
if (N == 0) {
|
||||
mCategories = null;
|
||||
@@ -1069,9 +1351,14 @@ public final class ShortcutInfo implements Parcelable {
|
||||
dest.writeParcelable(mExtras, flags);
|
||||
dest.writeLong(mLastChangedTimestamp);
|
||||
dest.writeInt(mFlags);
|
||||
dest.writeInt(mIconResourceId);
|
||||
dest.writeInt(mIconResId);
|
||||
dest.writeString(mBitmapPath);
|
||||
|
||||
dest.writeString(mIconResName);
|
||||
dest.writeString(mTitleResName);
|
||||
dest.writeString(mTextResName);
|
||||
dest.writeString(mDisabledMessageResName);
|
||||
|
||||
if (mCategories != null) {
|
||||
final int N = mCategories.size();
|
||||
dest.writeInt(N);
|
||||
@@ -1160,16 +1447,25 @@ public final class ShortcutInfo implements Parcelable {
|
||||
sb.append(secure ? "***" : mTitle);
|
||||
sb.append(", resId=");
|
||||
sb.append(mTitleResId);
|
||||
sb.append("[");
|
||||
sb.append(mTitleResName);
|
||||
sb.append("]");
|
||||
|
||||
sb.append(", longLabel=");
|
||||
sb.append(secure ? "***" : mText);
|
||||
sb.append(", resId=");
|
||||
sb.append(mTextResId);
|
||||
sb.append("[");
|
||||
sb.append(mTextResName);
|
||||
sb.append("]");
|
||||
|
||||
sb.append(", disabledMessage=");
|
||||
sb.append(secure ? "***" : mDisabledMessage);
|
||||
sb.append(", resId=");
|
||||
sb.append(mDisabledMessageResId);
|
||||
sb.append("[");
|
||||
sb.append(mDisabledMessageResName);
|
||||
sb.append("]");
|
||||
|
||||
sb.append(", categories=");
|
||||
sb.append(mCategories);
|
||||
@@ -1195,7 +1491,10 @@ public final class ShortcutInfo implements Parcelable {
|
||||
if (includeInternalData) {
|
||||
|
||||
sb.append(", iconRes=");
|
||||
sb.append(mIconResourceId);
|
||||
sb.append(mIconResId);
|
||||
sb.append("[");
|
||||
sb.append(mIconResName);
|
||||
sb.append("]");
|
||||
|
||||
sb.append(", bitmapPath=");
|
||||
sb.append(mBitmapPath);
|
||||
@@ -1208,11 +1507,13 @@ public final class ShortcutInfo implements Parcelable {
|
||||
/** @hide */
|
||||
public ShortcutInfo(
|
||||
@UserIdInt int userId, String id, String packageName, ComponentName activity,
|
||||
Icon icon, CharSequence title, int titleResId, CharSequence text, int textResId,
|
||||
CharSequence disabledMessage, int disabledMessageResId, Set<String> categories,
|
||||
Icon icon, CharSequence title, int titleResId, String titleResName,
|
||||
CharSequence text, int textResId, String textResName,
|
||||
CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
|
||||
Set<String> categories,
|
||||
Intent intent, PersistableBundle intentPersistableExtras,
|
||||
int rank, PersistableBundle extras, long lastChangedTimestamp,
|
||||
int flags, int iconResId, String bitmapPath) {
|
||||
int flags, int iconResId, String iconResName, String bitmapPath) {
|
||||
mUserId = userId;
|
||||
mId = id;
|
||||
mPackageName = packageName;
|
||||
@@ -1220,10 +1521,13 @@ public final class ShortcutInfo implements Parcelable {
|
||||
mIcon = icon;
|
||||
mTitle = title;
|
||||
mTitleResId = titleResId;
|
||||
mTitleResName = titleResName;
|
||||
mText = text;
|
||||
mTextResId = textResId;
|
||||
mTextResName = textResName;
|
||||
mDisabledMessage = disabledMessage;
|
||||
mDisabledMessageResId = disabledMessageResId;
|
||||
mDisabledMessageResName = disabledMessageResName;
|
||||
mCategories = clone(categories);
|
||||
mIntent = intent;
|
||||
mIntentPersistableExtras = intentPersistableExtras;
|
||||
@@ -1231,7 +1535,8 @@ public final class ShortcutInfo implements Parcelable {
|
||||
mExtras = extras;
|
||||
mLastChangedTimestamp = lastChangedTimestamp;
|
||||
mFlags = flags;
|
||||
mIconResourceId = iconResId;
|
||||
mIconResId = iconResId;
|
||||
mIconResName = iconResName;
|
||||
mBitmapPath = bitmapPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,12 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.os.PersistableBundle;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.ArrayMap;
|
||||
@@ -48,6 +51,8 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import sun.misc.Resource;
|
||||
|
||||
/**
|
||||
* Package information used by {@link ShortcutService}.
|
||||
* User information used by {@link ShortcutService}.
|
||||
@@ -72,15 +77,19 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
private static final String ATTR_ACTIVITY = "activity";
|
||||
private static final String ATTR_TITLE = "title";
|
||||
private static final String ATTR_TITLE_RES_ID = "titleid";
|
||||
private static final String ATTR_TITLE_RES_NAME = "titlename";
|
||||
private static final String ATTR_TEXT = "text";
|
||||
private static final String ATTR_TEXT_RES_ID = "textid";
|
||||
private static final String ATTR_TEXT_RES_NAME = "textname";
|
||||
private static final String ATTR_DISABLED_MESSAGE = "dmessage";
|
||||
private static final String ATTR_DISABLED_MESSAGE_RES_ID = "dmessageid";
|
||||
private static final String ATTR_DISABLED_MESSAGE_RES_NAME = "dmessagename";
|
||||
private static final String ATTR_INTENT = "intent";
|
||||
private static final String ATTR_RANK = "rank";
|
||||
private static final String ATTR_TIMESTAMP = "timestamp";
|
||||
private static final String ATTR_FLAGS = "flags";
|
||||
private static final String ATTR_ICON_RES = "icon-res";
|
||||
private static final String ATTR_ICON_RES_ID = "icon-res";
|
||||
private static final String ATTR_ICON_RES_NAME = "icon-resname";
|
||||
private static final String ATTR_BITMAP_PATH = "bitmap-path";
|
||||
|
||||
private static final String NAME_CATEGORIES = "categories";
|
||||
@@ -153,6 +162,12 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Resources getPackageResources() {
|
||||
return mShortcutUser.mService.injectGetResourcesForApplicationAsUser(
|
||||
getPackageName(), getPackageUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreBlocked() {
|
||||
// Can't restore due to version/signature mismatch. Remove all shortcuts.
|
||||
@@ -209,8 +224,13 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
}
|
||||
|
||||
private void addShortcutInner(@NonNull ShortcutInfo newShortcut) {
|
||||
final ShortcutService s = mShortcutUser.mService;
|
||||
|
||||
deleteShortcutInner(newShortcut.getId());
|
||||
mShortcutUser.mService.saveIconAndFixUpShortcut(getPackageUserId(), newShortcut);
|
||||
|
||||
// Extract Icon and update the icon res ID and the bitmap path.
|
||||
s.saveIconAndFixUpShortcut(getPackageUserId(), newShortcut);
|
||||
s.fixUpShortcutResourceNamesAndValues(newShortcut);
|
||||
mShortcuts.put(newShortcut.getId(), newShortcut);
|
||||
}
|
||||
|
||||
@@ -248,7 +268,7 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
// TODO Check max dynamic count.
|
||||
// mShortcutUser.mService.enforceMaxDynamicShortcuts(newDynamicCount);
|
||||
|
||||
// Okay, make it dynamic and add.
|
||||
// If it was originally pinned, the new one should be pinned too.
|
||||
if (wasPinned) {
|
||||
newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
|
||||
}
|
||||
@@ -318,6 +338,8 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
disabled.setDisabledMessage(disabledMessage);
|
||||
} else if (disabledMessageResId != 0) {
|
||||
disabled.setDisabledMessageResId(disabledMessageResId);
|
||||
|
||||
mShortcutUser.mService.fixUpShortcutResourceNamesAndValues(disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -622,10 +644,25 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
|
||||
// For existing shortcuts, update timestamps if they have any resources.
|
||||
if (!isNewApp) {
|
||||
Resources publisherRes = null;
|
||||
|
||||
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
|
||||
final ShortcutInfo si = mShortcuts.valueAt(i);
|
||||
|
||||
if (si.hasAnyResources()) {
|
||||
if (!si.isOriginallyFromManifest()) {
|
||||
if (publisherRes == null) {
|
||||
publisherRes = getPackageResources();
|
||||
if (publisherRes == null) {
|
||||
break; // Resources couldn't be loaded.
|
||||
}
|
||||
}
|
||||
|
||||
// If this shortcut is not from a manifest, then update all resource IDs
|
||||
// from resource names. (We don't allow resource strings for
|
||||
// non-manifest at the moment, but icons can still be resources.)
|
||||
si.lookupAndFillInResourceIds(publisherRes);
|
||||
}
|
||||
changed = true;
|
||||
si.setTimestamp(s.injectCurrentTimeMillis());
|
||||
}
|
||||
@@ -869,7 +906,9 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
final ComponentName newActivity = newShortcut.getActivity();
|
||||
if (newActivity == null) {
|
||||
if (operation != ShortcutService.OPERATION_UPDATE) {
|
||||
service.wtf("null Activity found for non-update");
|
||||
// This method may be called before validating shortcuts, so this may happen,
|
||||
// and is a caller side error.
|
||||
throw new NullPointerException("activity must be provided");
|
||||
}
|
||||
continue; // Activity can be null for update.
|
||||
}
|
||||
@@ -907,6 +946,36 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For all the text fields, refresh the string values if they're from resources.
|
||||
*/
|
||||
public void resolveResourceStrings() {
|
||||
final ShortcutService s = mShortcutUser.mService;
|
||||
boolean changed = false;
|
||||
|
||||
Resources publisherRes = null;
|
||||
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
|
||||
final ShortcutInfo si = mShortcuts.valueAt(i);
|
||||
|
||||
if (si.hasStringResources()) {
|
||||
changed = true;
|
||||
|
||||
if (publisherRes == null) {
|
||||
publisherRes = getPackageResources();
|
||||
if (publisherRes == null) {
|
||||
break; // Resources couldn't be loaded.
|
||||
}
|
||||
}
|
||||
|
||||
si.resolveResourceStrings(publisherRes);
|
||||
si.setTimestamp(s.injectCurrentTimeMillis());
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
s.scheduleSaveUser(getPackageUserId());
|
||||
}
|
||||
}
|
||||
|
||||
public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
|
||||
pw.println();
|
||||
|
||||
@@ -1008,11 +1077,15 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
// writeAttr(out, "icon", si.getIcon()); // We don't save it.
|
||||
ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
|
||||
ShortcutService.writeAttr(out, ATTR_TITLE_RES_ID, si.getTitleResId());
|
||||
ShortcutService.writeAttr(out, ATTR_TITLE_RES_NAME, si.getTitleResName());
|
||||
ShortcutService.writeAttr(out, ATTR_TEXT, si.getText());
|
||||
ShortcutService.writeAttr(out, ATTR_TEXT_RES_ID, si.getTextResId());
|
||||
ShortcutService.writeAttr(out, ATTR_TEXT_RES_NAME, si.getTextResName());
|
||||
ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE, si.getDisabledMessage());
|
||||
ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_ID,
|
||||
si.getDisabledMessageResourceId());
|
||||
ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_NAME,
|
||||
si.getDisabledMessageResName());
|
||||
ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras());
|
||||
ShortcutService.writeAttr(out, ATTR_RANK, si.getRank());
|
||||
ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
|
||||
@@ -1025,7 +1098,8 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
| ShortcutInfo.FLAG_DYNAMIC));
|
||||
} else {
|
||||
ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags());
|
||||
ShortcutService.writeAttr(out, ATTR_ICON_RES, si.getIconResourceId());
|
||||
ShortcutService.writeAttr(out, ATTR_ICON_RES_ID, si.getIconResourceId());
|
||||
ShortcutService.writeAttr(out, ATTR_ICON_RES_NAME, si.getIconResName());
|
||||
ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
|
||||
}
|
||||
|
||||
@@ -1096,17 +1170,21 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
// Icon icon;
|
||||
String title;
|
||||
int titleResId;
|
||||
String titleResName;
|
||||
String text;
|
||||
int textResId;
|
||||
String textResName;
|
||||
String disabledMessage;
|
||||
int disabledMessageResId;
|
||||
String disabledMessageResName;
|
||||
Intent intent;
|
||||
PersistableBundle intentPersistableExtras = null;
|
||||
int rank;
|
||||
PersistableBundle extras = null;
|
||||
long lastChangedTimestamp;
|
||||
int flags;
|
||||
int iconRes;
|
||||
int iconResId;
|
||||
String iconResName;
|
||||
String bitmapPath;
|
||||
ArraySet<String> categories = null;
|
||||
|
||||
@@ -1115,16 +1193,21 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
ATTR_ACTIVITY);
|
||||
title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
|
||||
titleResId = ShortcutService.parseIntAttribute(parser, ATTR_TITLE_RES_ID);
|
||||
titleResName = ShortcutService.parseStringAttribute(parser, ATTR_TITLE_RES_NAME);
|
||||
text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT);
|
||||
textResId = ShortcutService.parseIntAttribute(parser, ATTR_TEXT_RES_ID);
|
||||
textResName = ShortcutService.parseStringAttribute(parser, ATTR_TEXT_RES_NAME);
|
||||
disabledMessage = ShortcutService.parseStringAttribute(parser, ATTR_DISABLED_MESSAGE);
|
||||
disabledMessageResId = ShortcutService.parseIntAttribute(parser,
|
||||
ATTR_DISABLED_MESSAGE_RES_ID);
|
||||
disabledMessageResName = ShortcutService.parseStringAttribute(parser,
|
||||
ATTR_DISABLED_MESSAGE_RES_NAME);
|
||||
intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT);
|
||||
rank = (int) ShortcutService.parseLongAttribute(parser, ATTR_RANK);
|
||||
lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP);
|
||||
flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS);
|
||||
iconRes = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES);
|
||||
iconResId = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES_ID);
|
||||
iconResName = ShortcutService.parseStringAttribute(parser, ATTR_ICON_RES_NAME);
|
||||
bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH);
|
||||
|
||||
final int outerDepth = parser.getDepth();
|
||||
@@ -1167,10 +1250,11 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
|
||||
return new ShortcutInfo(
|
||||
userId, id, packageName, activityComponent, /* icon =*/ null,
|
||||
title, titleResId, text, textResId, disabledMessage, disabledMessageResId,
|
||||
title, titleResId, titleResName, text, textResId, textResName,
|
||||
disabledMessage, disabledMessageResId, disabledMessageResName,
|
||||
categories, intent,
|
||||
intentPersistableExtras, rank, extras, lastChangedTimestamp, flags,
|
||||
iconRes, bitmapPath);
|
||||
iconResId, iconResName, bitmapPath);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -221,6 +221,8 @@ public class ShortcutParser {
|
||||
| ShortcutInfo.FLAG_IMMUTABLE
|
||||
| ((iconResId != 0) ? ShortcutInfo.FLAG_HAS_ICON_RES : 0);
|
||||
|
||||
// Note we don't need to set resource names here yet. They'll be set when they're about
|
||||
// to be published.
|
||||
return new ShortcutInfo(
|
||||
userId,
|
||||
id,
|
||||
@@ -229,10 +231,13 @@ public class ShortcutParser {
|
||||
null, // icon
|
||||
null, // title string
|
||||
titleResId,
|
||||
null, // title res name
|
||||
null, // text string
|
||||
textResId,
|
||||
null, // text res name
|
||||
null, // disabled message string
|
||||
disabledMessageResId,
|
||||
null, // disabled message res name
|
||||
categories,
|
||||
intent,
|
||||
null, // intent extras
|
||||
@@ -241,6 +246,7 @@ public class ShortcutParser {
|
||||
service.injectCurrentTimeMillis(),
|
||||
flags,
|
||||
iconResId,
|
||||
null, // icon res name
|
||||
null); // bitmap path
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutServiceInternal;
|
||||
import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
@@ -120,6 +121,9 @@ import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* - Deal with the async nature of PACKAGE_ADD. Basically when a publisher does anything after
|
||||
* it's upgraded, the manager should make sure the upgrade process has been executed.
|
||||
*
|
||||
* - HandleUnlockUser needs to be async. Wait on it in onCleanupUser.
|
||||
*
|
||||
* - Implement reportShortcutUsed().
|
||||
@@ -319,8 +323,10 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
int GET_ACTIVITIES_WITH_METADATA = 6;
|
||||
int GET_INSTALLED_PACKAGES = 7;
|
||||
int CHECK_PACKAGE_CHANGES = 8;
|
||||
int GET_APPLICATION_RESOURCES = 9;
|
||||
int RESOURCE_NAME_LOOKUP = 10;
|
||||
|
||||
int COUNT = CHECK_PACKAGE_CHANGES + 1;
|
||||
int COUNT = RESOURCE_NAME_LOOKUP + 1;
|
||||
}
|
||||
|
||||
final Object mStatLock = new Object();
|
||||
@@ -1266,6 +1272,24 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
return scaledBitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a shortcut, update all resource names from resource IDs, and also update all
|
||||
* resource-based strings.
|
||||
*/
|
||||
void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) {
|
||||
final Resources publisherRes = injectGetResourcesForApplicationAsUser(
|
||||
si.getPackage(), si.getUserId());
|
||||
if (publisherRes != null) {
|
||||
final long start = injectElapsedRealtime();
|
||||
try {
|
||||
si.lookupAndFillInResourceNames(publisherRes);
|
||||
} finally {
|
||||
logDurationStat(Stats.RESOURCE_NAME_LOOKUP, start);
|
||||
}
|
||||
si.resolveResourceStrings(publisherRes);
|
||||
}
|
||||
}
|
||||
|
||||
// === Caller validation ===
|
||||
|
||||
private boolean isCallerSystem() {
|
||||
@@ -1328,7 +1352,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
throw new SecurityException("Calling package name mismatch");
|
||||
}
|
||||
|
||||
void postToHandler(Runnable r) {
|
||||
// Overridden in unit tests to execute r synchronously.
|
||||
void injectPostToHandler(Runnable r) {
|
||||
mHandler.post(r);
|
||||
}
|
||||
|
||||
@@ -1371,7 +1396,7 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
} finally {
|
||||
injectRestoreCallingIdentity(token);
|
||||
}
|
||||
postToHandler(() -> {
|
||||
injectPostToHandler(() -> {
|
||||
final ArrayList<ShortcutChangeListener> copy;
|
||||
synchronized (mLock) {
|
||||
copy = new ArrayList<>(mListeners);
|
||||
@@ -1538,12 +1563,18 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
// TODO When activity is changing, check the dynamic count.
|
||||
}
|
||||
|
||||
// Note copyNonNullFieldsFrom() does the "udpatable with?" check too.
|
||||
// Note copyNonNullFieldsFrom() does the "updatable with?" check too.
|
||||
target.copyNonNullFieldsFrom(source);
|
||||
|
||||
if (replacingIcon) {
|
||||
saveIconAndFixUpShortcut(userId, target);
|
||||
}
|
||||
|
||||
// When we're updating any resource related fields, re-extract the res names and
|
||||
// the values.
|
||||
if (replacingIcon || source.hasStringResources()) {
|
||||
fixUpShortcutResourceNamesAndValues(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1981,21 +2012,6 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
});
|
||||
}
|
||||
}
|
||||
// Resolve all strings if needed.
|
||||
if (!cloneKeyFieldOnly) {
|
||||
final long token = injectClearCallingIdentity();
|
||||
try {
|
||||
for (int i = ret.size() - 1; i >= 0; i--) {
|
||||
try {
|
||||
ret.get(i).resolveStringsRequiringCrossUser(mContext);
|
||||
} catch (NameNotFoundException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
injectRestoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2218,11 +2234,25 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onSystemLocaleChangedNoLock: " + mLocaleChangeSequenceNumber.get());
|
||||
}
|
||||
postToHandler(() -> scheduleSaveBaseState());
|
||||
injectPostToHandler(() -> handleLocaleChanged());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleLocaleChanged() {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "handleLocaleChanged");
|
||||
}
|
||||
scheduleSaveBaseState();
|
||||
|
||||
final long token = injectClearCallingIdentity();
|
||||
try {
|
||||
forEachLoadedUserLocked(u -> u.forAllPackages(p -> p.resolveResourceStrings()));
|
||||
} finally {
|
||||
injectRestoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Package event callbacks.
|
||||
*/
|
||||
@@ -2482,10 +2512,26 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
|
||||
@Nullable
|
||||
XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
|
||||
// TODO Doesn't seem like ACROSS_USER is needed, but double check.
|
||||
return activityInfo.loadXmlMetaData(mContext.getPackageManager(), key);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Resources injectGetResourcesForApplicationAsUser(String packageName, int userId) {
|
||||
final long start = injectElapsedRealtime();
|
||||
final long token = injectClearCallingIdentity();
|
||||
try {
|
||||
return mContext.getPackageManager().getResourcesForApplicationAsUser(
|
||||
packageName, userId);
|
||||
} catch (NameNotFoundException e) {
|
||||
Slog.e(TAG, "Resources for package " + packageName + " not found");
|
||||
return null;
|
||||
} finally {
|
||||
injectRestoreCallingIdentity(token);
|
||||
|
||||
logDurationStat(Stats.GET_APPLICATION_RESOURCES, start);
|
||||
}
|
||||
}
|
||||
|
||||
// === Backup & restore ===
|
||||
|
||||
boolean shouldBackupApp(String packageName, int userId) {
|
||||
@@ -2628,6 +2674,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
dumpStatLS(pw, p, Stats.GET_ACTIVITIES_WITH_METADATA, "getActivities+metadata");
|
||||
dumpStatLS(pw, p, Stats.GET_INSTALLED_PACKAGES, "getInstalledPackages");
|
||||
dumpStatLS(pw, p, Stats.CHECK_PACKAGE_CHANGES, "checkPackageChanges");
|
||||
dumpStatLS(pw, p, Stats.GET_APPLICATION_RESOURCES, "getApplicationResources");
|
||||
dumpStatLS(pw, p, Stats.RESOURCE_NAME_LOOKUP, "resourceNameLookup");
|
||||
}
|
||||
|
||||
for (int i = 0; i < mUsers.size(); i++) {
|
||||
|
||||
@@ -22,6 +22,8 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -92,6 +94,7 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiPredicate;
|
||||
@@ -317,7 +320,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
void postToHandler(Runnable r) {
|
||||
void injectPostToHandler(Runnable r) {
|
||||
final long token = mContext.injectClearCallingIdentity();
|
||||
r.run();
|
||||
mContext.injectRestoreCallingIdentity(token);
|
||||
@@ -441,6 +444,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
|
||||
|
||||
protected boolean mInjectedIsLowRamDevice;
|
||||
|
||||
protected Locale mInjectedLocale = Locale.ENGLISH;
|
||||
|
||||
protected int mInjectedCallingUid;
|
||||
protected String mInjectedClientPackage;
|
||||
|
||||
@@ -597,6 +602,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
|
||||
// User 0 is always running.
|
||||
when(mMockUserManager.isUserRunning(eq(USER_0))).thenAnswer(new AnswerIsUserRunning(true));
|
||||
|
||||
setUpAppResources();
|
||||
|
||||
// Start the service.
|
||||
initService();
|
||||
setCaller(CALLING_PACKAGE_1);
|
||||
|
||||
@@ -624,6 +632,53 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
protected void setUpAppResources() throws Exception {
|
||||
setUpAppResources(/* offset = */ 0);
|
||||
}
|
||||
|
||||
protected void setUpAppResources(int ressIdOffset) throws Exception {
|
||||
// ressIdOffset is used to adjust resource IDs to emulate the case where an updated app
|
||||
// has resource IDs changed.
|
||||
|
||||
doAnswer(pmInvocation -> {
|
||||
assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
|
||||
|
||||
final String packageName = (String) pmInvocation.getArguments()[0];
|
||||
final int userId = (Integer) pmInvocation.getArguments()[1];
|
||||
|
||||
final Resources res = mock(Resources.class);
|
||||
|
||||
doAnswer(resInvocation -> {
|
||||
final int argResId = (Integer) resInvocation.getArguments()[0];
|
||||
|
||||
return "string-" + packageName + "-user:" + userId + "-res:" + argResId
|
||||
+ "/" + mInjectedLocale;
|
||||
}).when(res).getString(anyInt());
|
||||
|
||||
doAnswer(resInvocation -> {
|
||||
final int resId = (Integer) resInvocation.getArguments()[0];
|
||||
|
||||
// Always use the "string" resource type. The type doesn't matter during the test.
|
||||
return packageName + ":string/r" + resId;
|
||||
}).when(res).getResourceName(anyInt());
|
||||
|
||||
doAnswer(resInvocation -> {
|
||||
final String argResName = (String) resInvocation.getArguments()[0];
|
||||
final String argType = (String) resInvocation.getArguments()[1];
|
||||
final String argPackageName = (String) resInvocation.getArguments()[2];
|
||||
|
||||
// See the above code. getResourceName() will just use "r" + res ID as the entry
|
||||
// name.
|
||||
String entryName = argResName;
|
||||
if (entryName.contains("/")) {
|
||||
entryName = ShortcutInfo.getResourceEntryName(entryName);
|
||||
}
|
||||
return Integer.parseInt(entryName.substring(1)) + ressIdOffset;
|
||||
}).when(res).getIdentifier(anyString(), anyString(), anyString());
|
||||
return res;
|
||||
}).when(mMockPackageManager).getResourcesForApplicationAsUser(anyString(), anyInt());
|
||||
}
|
||||
|
||||
protected static UserInfo withProfileGroupId(UserInfo in, int groupId) {
|
||||
in.profileGroupId = groupId;
|
||||
return in;
|
||||
@@ -667,8 +722,16 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
|
||||
mLauncherApps = null;
|
||||
mLauncherAppsMap.clear();
|
||||
|
||||
// Load the setting file.
|
||||
// Send boot sequence events.
|
||||
mService.onBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);
|
||||
|
||||
// Make sure a call to onSystemLocaleChangedNoLock() before PHASE_BOOT_COMPLETED will be
|
||||
// ignored.
|
||||
final long origSequenceNumber = mService.getLocaleChangeSequenceNumber();
|
||||
mInternal.onSystemLocaleChangedNoLock();
|
||||
assertEquals(origSequenceNumber, mService.getLocaleChangeSequenceNumber());
|
||||
|
||||
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
|
||||
}
|
||||
|
||||
protected void shutdownServices() {
|
||||
@@ -891,6 +954,18 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
|
||||
setCaller(previousPackage, previousUserId);
|
||||
}
|
||||
|
||||
protected void runWithSystemUid(Runnable r) {
|
||||
final int origUid = mInjectedCallingUid;
|
||||
mInjectedCallingUid = Process.SYSTEM_UID;
|
||||
r.run();
|
||||
mInjectedCallingUid = origUid;
|
||||
}
|
||||
|
||||
protected void lookupAndFillInResourceNames(ShortcutInfo si) {
|
||||
runWithSystemUid(() -> si.lookupAndFillInResourceNames(
|
||||
mService.injectGetResourcesForApplicationAsUser(si.getPackage(), si.getUserId())));
|
||||
}
|
||||
|
||||
protected int getCallingUserId() {
|
||||
return UserHandle.getUserId(mInjectedCallingUid);
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ import org.mockito.ArgumentCaptor;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Tests for ShortcutService and ShortcutManager.
|
||||
@@ -922,6 +923,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
|
||||
ShortcutInfo s = getCallerShortcut("s2");
|
||||
assertTrue(s.hasIconResource());
|
||||
assertEquals(R.drawable.black_32x32, s.getIconResourceId());
|
||||
assertEquals("string/r" + R.drawable.black_32x32, s.getIconResName());
|
||||
assertEquals("Title-s2", s.getTitle());
|
||||
|
||||
s = getCallerShortcut("s4");
|
||||
@@ -1091,21 +1093,6 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
|
||||
}
|
||||
|
||||
public void testGetShortcuts_resolveStrings() throws Exception {
|
||||
doAnswer(pmInvocation -> {
|
||||
assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
|
||||
|
||||
final String packageName = (String) pmInvocation.getArguments()[0];
|
||||
final int userId = (Integer) pmInvocation.getArguments()[1];
|
||||
|
||||
final Resources res = mock(Resources.class);
|
||||
doAnswer(resInvocation -> {
|
||||
final int resId = (Integer) resInvocation.getArguments()[0];
|
||||
|
||||
return "string-" + packageName + "-user:" + userId + "-res:" + resId;
|
||||
}).when(res).getString(anyInt());
|
||||
return res;
|
||||
}).when(mMockPackageManager).getResourcesForApplicationAsUser(anyString(), anyInt());
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
ShortcutInfo si = new ShortcutInfo.Builder(mClientContext)
|
||||
.setId("id")
|
||||
@@ -1137,18 +1124,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
|
||||
List<ShortcutInfo> ret = assertShortcutIds(
|
||||
assertAllStringsResolved(mLauncherApps.getShortcuts(q, HANDLE_USER_0)),
|
||||
"id");
|
||||
assertEquals("string-com.android.test.1-user:0-res:10", ret.get(0).getTitle());
|
||||
assertEquals("string-com.android.test.1-user:0-res:11", ret.get(0).getText());
|
||||
assertEquals("string-com.android.test.1-user:0-res:12",
|
||||
assertEquals("string-com.android.test.1-user:0-res:10/en", ret.get(0).getTitle());
|
||||
assertEquals("string-com.android.test.1-user:0-res:11/en", ret.get(0).getText());
|
||||
assertEquals("string-com.android.test.1-user:0-res:12/en",
|
||||
ret.get(0).getDisabledMessage());
|
||||
|
||||
// USER P0
|
||||
ret = assertShortcutIds(
|
||||
assertAllStringsResolved(mLauncherApps.getShortcuts(q, HANDLE_USER_P0)),
|
||||
"id");
|
||||
assertEquals("string-com.android.test.1-user:20-res:10", ret.get(0).getTitle());
|
||||
assertEquals("string-com.android.test.1-user:20-res:11", ret.get(0).getText());
|
||||
assertEquals("string-com.android.test.1-user:20-res:12",
|
||||
assertEquals("string-com.android.test.1-user:20-res:10/en", ret.get(0).getTitle());
|
||||
assertEquals("string-com.android.test.1-user:20-res:11/en", ret.get(0).getText());
|
||||
assertEquals("string-com.android.test.1-user:20-res:12/en",
|
||||
ret.get(0).getDisabledMessage());
|
||||
});
|
||||
}
|
||||
@@ -3602,6 +3589,84 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
|
||||
findShortcut(shortcuts.getValue(), "s1").getLastChangedTimestamp());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the case where an updated app has resource IDs changed.
|
||||
*/
|
||||
public void testHandlePackageUpdate_resIdChanged() throws Exception {
|
||||
final Icon icon1 = Icon.createWithResource(getTestContext(), /* res ID */ 1000);
|
||||
final Icon icon2 = Icon.createWithResource(getTestContext(), /* res ID */ 1001);
|
||||
|
||||
// Set up shortcuts.
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
// Note resource strings are not officially supported (they're hidden), but
|
||||
// should work.
|
||||
|
||||
final ShortcutInfo s1 = new ShortcutInfo.Builder(mClientContext)
|
||||
.setId("s1")
|
||||
.setActivity(makeComponent(ShortcutActivity.class))
|
||||
.setIntent(new Intent(Intent.ACTION_VIEW))
|
||||
.setIcon(icon1)
|
||||
.setTitleResId(10000)
|
||||
.setTextResId(10001)
|
||||
.setDisabledMessageResId(10002)
|
||||
.build();
|
||||
|
||||
final ShortcutInfo s2 = new ShortcutInfo.Builder(mClientContext)
|
||||
.setId("s2")
|
||||
.setActivity(makeComponent(ShortcutActivity.class))
|
||||
.setIntent(new Intent(Intent.ACTION_VIEW))
|
||||
.setIcon(icon2)
|
||||
.setTitleResId(20000)
|
||||
.build();
|
||||
|
||||
assertTrue(mManager.setDynamicShortcuts(list(s1, s2)));
|
||||
});
|
||||
|
||||
// Verify.
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
final ShortcutInfo s1 = getCallerShortcut("s1");
|
||||
final ShortcutInfo s2 = getCallerShortcut("s2");
|
||||
|
||||
assertEquals(1000, s1.getIconResourceId());
|
||||
assertEquals(10000, s1.getTitleResId());
|
||||
assertEquals(10001, s1.getTextResId());
|
||||
assertEquals(10002, s1.getDisabledMessageResourceId());
|
||||
|
||||
assertEquals(1001, s2.getIconResourceId());
|
||||
assertEquals(20000, s2.getTitleResId());
|
||||
assertEquals(0, s2.getTextResId());
|
||||
assertEquals(0, s2.getDisabledMessageResourceId());
|
||||
});
|
||||
|
||||
mService.saveDirtyInfo();
|
||||
initService();
|
||||
|
||||
// Set up the mock resources again, with an "adjustment".
|
||||
// When the package is updated, the service will fetch the updated res-IDs with res-names,
|
||||
// and the new IDs will have this offset.
|
||||
setUpAppResources(10);
|
||||
|
||||
// Update the package.
|
||||
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||
mService.mPackageMonitor.onReceive(getTestContext(),
|
||||
genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
final ShortcutInfo s1 = getCallerShortcut("s1");
|
||||
final ShortcutInfo s2 = getCallerShortcut("s2");
|
||||
|
||||
assertEquals(1010, s1.getIconResourceId());
|
||||
assertEquals(10010, s1.getTitleResId());
|
||||
assertEquals(10011, s1.getTextResId());
|
||||
assertEquals(10012, s1.getDisabledMessageResourceId());
|
||||
|
||||
assertEquals(1011, s2.getIconResourceId());
|
||||
assertEquals(20010, s2.getTitleResId());
|
||||
assertEquals(0, s2.getTextResId());
|
||||
assertEquals(0, s2.getDisabledMessageResourceId());
|
||||
});
|
||||
}
|
||||
|
||||
protected void prepareForBackupTest() {
|
||||
|
||||
prepareCrossProfileDataSet();
|
||||
@@ -4785,27 +4850,35 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
|
||||
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(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(0, si.getRank());
|
||||
|
||||
// check another
|
||||
si = getCallerShortcut("ms2");
|
||||
|
||||
assertEquals("ms2", si.getId());
|
||||
assertEquals(R.drawable.icon2, si.getIconResourceId());
|
||||
|
||||
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(set("android.shortcut.conversation"), si.getCategories());
|
||||
assertEquals("action2", si.getIntent().getAction());
|
||||
assertEquals(null, si.getIntent().getData());
|
||||
assertEquals(1, si.getRank());
|
||||
|
||||
// check another
|
||||
si = getCallerShortcut("ms3");
|
||||
@@ -4813,12 +4886,113 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
|
||||
assertEquals("ms3", si.getId());
|
||||
assertEquals(0, si.getIconResourceId());
|
||||
assertEquals(R.string.shortcut_title1, si.getTitleResId());
|
||||
assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
|
||||
|
||||
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(2, si.getRank());
|
||||
});
|
||||
}
|
||||
|
||||
public void testManifestShortcuts_localeChange() {
|
||||
mService.handleUnlockUser(USER_0);
|
||||
|
||||
// Package 1 updated, which has one valid manifest shortcut and one invalid.
|
||||
addManifestShortcutResource(
|
||||
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
|
||||
R.xml.shortcut_2);
|
||||
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||
mService.mPackageMonitor.onReceive(getTestContext(),
|
||||
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.setDynamicShortcuts(list(makeShortcutWithTitle("s1", "title")));
|
||||
|
||||
assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
|
||||
mManager.getManifestShortcuts()))),
|
||||
"ms1", "ms2");
|
||||
|
||||
// check first shortcut.
|
||||
ShortcutInfo si = getCallerShortcut("ms1");
|
||||
|
||||
assertEquals("ms1", si.getId());
|
||||
assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_title1 + "/en",
|
||||
si.getTitle());
|
||||
assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_text1 + "/en",
|
||||
si.getText());
|
||||
assertEquals("string-com.android.test.1-user:0-res:"
|
||||
+ R.string.shortcut_disabled_message1 + "/en",
|
||||
si.getDisabledMessage());
|
||||
assertEquals(START_TIME, si.getLastChangedTimestamp());
|
||||
|
||||
// check another
|
||||
si = getCallerShortcut("ms2");
|
||||
|
||||
assertEquals("ms2", si.getId());
|
||||
assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_title2 + "/en",
|
||||
si.getTitle());
|
||||
assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_text2 + "/en",
|
||||
si.getText());
|
||||
assertEquals("string-com.android.test.1-user:0-res:"
|
||||
+ R.string.shortcut_disabled_message2 + "/en",
|
||||
si.getDisabledMessage());
|
||||
assertEquals(START_TIME, si.getLastChangedTimestamp());
|
||||
|
||||
// Check the dynamic one.
|
||||
si = getCallerShortcut("s1");
|
||||
|
||||
assertEquals("s1", si.getId());
|
||||
assertEquals("title", si.getTitle());
|
||||
assertEquals(null, si.getText());
|
||||
assertEquals(null, si.getDisabledMessage());
|
||||
assertEquals(START_TIME, si.getLastChangedTimestamp());
|
||||
});
|
||||
|
||||
mInjectedCurrentTimeMillis++;
|
||||
|
||||
mInjectedLocale = Locale.JAPANESE;
|
||||
mInternal.onSystemLocaleChangedNoLock();
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
// check first shortcut.
|
||||
ShortcutInfo si = getCallerShortcut("ms1");
|
||||
|
||||
assertEquals("ms1", si.getId());
|
||||
assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_title1 + "/ja",
|
||||
si.getTitle());
|
||||
assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_text1 + "/ja",
|
||||
si.getText());
|
||||
assertEquals("string-com.android.test.1-user:0-res:"
|
||||
+ R.string.shortcut_disabled_message1 + "/ja",
|
||||
si.getDisabledMessage());
|
||||
assertEquals(START_TIME + 1, si.getLastChangedTimestamp());
|
||||
|
||||
// check another
|
||||
si = getCallerShortcut("ms2");
|
||||
|
||||
assertEquals("ms2", si.getId());
|
||||
assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_title2 + "/ja",
|
||||
si.getTitle());
|
||||
assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_text2 + "/ja",
|
||||
si.getText());
|
||||
assertEquals("string-com.android.test.1-user:0-res:"
|
||||
+ R.string.shortcut_disabled_message2 + "/ja",
|
||||
si.getDisabledMessage());
|
||||
assertEquals(START_TIME + 1, si.getLastChangedTimestamp());
|
||||
|
||||
// Check the dynamic one. (locale change shouldn't affect.)
|
||||
si = getCallerShortcut("s1");
|
||||
|
||||
assertEquals("s1", si.getId());
|
||||
assertEquals("title", si.getTitle());
|
||||
assertEquals(null, si.getText());
|
||||
assertEquals(null, si.getDisabledMessage());
|
||||
assertEquals(START_TIME, si.getLastChangedTimestamp()); // Not changed.
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.app.ActivityManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.PersistableBundle;
|
||||
@@ -40,7 +41,6 @@ import android.test.MoreAsserts;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import com.android.frameworks.servicestests.R;
|
||||
import com.android.server.SystemService;
|
||||
|
||||
/**
|
||||
* Tests for ShortcutService and ShortcutManager.
|
||||
@@ -138,6 +138,13 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
|
||||
assertEquals("abc", si.getBitmapPath());
|
||||
assertEquals(456, si.getIconResourceId());
|
||||
|
||||
assertEquals(0, si.getTitleResId());
|
||||
assertEquals(null, si.getTitleResName());
|
||||
assertEquals(0, si.getTextResId());
|
||||
assertEquals(null, si.getTextResName());
|
||||
assertEquals(0, si.getDisabledMessageResourceId());
|
||||
assertEquals(null, si.getDisabledMessageResName());
|
||||
}
|
||||
|
||||
public void testShortcutInfoParcel_resId() {
|
||||
@@ -163,6 +170,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
si.setBitmapPath("abc");
|
||||
si.setIconResourceId(456);
|
||||
|
||||
lookupAndFillInResourceNames(si);
|
||||
|
||||
si = parceled(si);
|
||||
|
||||
assertEquals(getTestContext().getPackageName(), si.getPackage());
|
||||
@@ -170,8 +179,11 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(new ComponentName("a", "b"), si.getActivity());
|
||||
assertEquals(123, si.getIcon().getResId());
|
||||
assertEquals(10, si.getTitleResId());
|
||||
assertEquals("r10", si.getTitleResName());
|
||||
assertEquals(11, si.getTextResId());
|
||||
assertEquals("r11", si.getTextResName());
|
||||
assertEquals(12, si.getDisabledMessageResourceId());
|
||||
assertEquals("r12", si.getDisabledMessageResName());
|
||||
assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
|
||||
assertEquals("action", si.getIntent().getAction());
|
||||
assertEquals("val", si.getIntent().getStringExtra("key"));
|
||||
@@ -181,6 +193,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
|
||||
assertEquals("abc", si.getBitmapPath());
|
||||
assertEquals(456, si.getIconResourceId());
|
||||
assertEquals("string/r456", si.getIconResName());
|
||||
}
|
||||
|
||||
public void testShortcutInfoClone() {
|
||||
@@ -204,6 +217,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
sorig.setBitmapPath("abc");
|
||||
sorig.setIconResourceId(456);
|
||||
|
||||
lookupAndFillInResourceNames(sorig);
|
||||
|
||||
ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
|
||||
|
||||
assertEquals(USER_11, si.getUserId());
|
||||
@@ -224,6 +239,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
|
||||
assertEquals("abc", si.getBitmapPath());
|
||||
assertEquals(456, si.getIconResourceId());
|
||||
assertEquals("string/r456", si.getIconResName());
|
||||
|
||||
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
|
||||
|
||||
@@ -244,6 +260,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(null, si.getBitmapPath());
|
||||
|
||||
assertEquals(456, si.getIconResourceId());
|
||||
assertEquals(null, si.getIconResName());
|
||||
|
||||
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
|
||||
|
||||
@@ -263,6 +280,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(null, si.getBitmapPath());
|
||||
|
||||
assertEquals(456, si.getIconResourceId());
|
||||
assertEquals(null, si.getIconResName());
|
||||
|
||||
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
|
||||
|
||||
@@ -282,6 +300,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(null, si.getBitmapPath());
|
||||
|
||||
assertEquals(456, si.getIconResourceId());
|
||||
assertEquals(null, si.getIconResName());
|
||||
}
|
||||
|
||||
public void testShortcutInfoClone_resId() {
|
||||
@@ -305,6 +324,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
sorig.setBitmapPath("abc");
|
||||
sorig.setIconResourceId(456);
|
||||
|
||||
lookupAndFillInResourceNames(sorig);
|
||||
|
||||
ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
|
||||
|
||||
assertEquals(USER_11, si.getUserId());
|
||||
@@ -314,8 +335,11 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(new ComponentName("a", "b"), si.getActivity());
|
||||
assertEquals(123, si.getIcon().getResId());
|
||||
assertEquals(10, si.getTitleResId());
|
||||
assertEquals("r10", si.getTitleResName());
|
||||
assertEquals(11, si.getTextResId());
|
||||
assertEquals("r11", si.getTextResName());
|
||||
assertEquals(12, si.getDisabledMessageResourceId());
|
||||
assertEquals("r12", si.getDisabledMessageResName());
|
||||
assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
|
||||
assertEquals("action", si.getIntent().getAction());
|
||||
assertEquals("val", si.getIntent().getStringExtra("key"));
|
||||
@@ -325,6 +349,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
|
||||
assertEquals("abc", si.getBitmapPath());
|
||||
assertEquals(456, si.getIconResourceId());
|
||||
assertEquals("string/r456", si.getIconResName());
|
||||
|
||||
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
|
||||
|
||||
@@ -333,8 +358,11 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(new ComponentName("a", "b"), si.getActivity());
|
||||
assertEquals(null, si.getIcon());
|
||||
assertEquals(10, si.getTitleResId());
|
||||
assertEquals(null, si.getTitleResName());
|
||||
assertEquals(11, si.getTextResId());
|
||||
assertEquals(null, si.getTextResName());
|
||||
assertEquals(12, si.getDisabledMessageResourceId());
|
||||
assertEquals(null, si.getDisabledMessageResName());
|
||||
assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
|
||||
assertEquals("action", si.getIntent().getAction());
|
||||
assertEquals("val", si.getIntent().getStringExtra("key"));
|
||||
@@ -345,6 +373,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(null, si.getBitmapPath());
|
||||
|
||||
assertEquals(456, si.getIconResourceId());
|
||||
assertEquals(null, si.getIconResName());
|
||||
|
||||
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
|
||||
|
||||
@@ -353,8 +382,11 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(new ComponentName("a", "b"), si.getActivity());
|
||||
assertEquals(null, si.getIcon());
|
||||
assertEquals(10, si.getTitleResId());
|
||||
assertEquals(null, si.getTitleResName());
|
||||
assertEquals(11, si.getTextResId());
|
||||
assertEquals(null, si.getTextResName());
|
||||
assertEquals(12, si.getDisabledMessageResourceId());
|
||||
assertEquals(null, si.getDisabledMessageResName());
|
||||
assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
|
||||
assertEquals(null, si.getIntent());
|
||||
assertEquals(123, si.getRank());
|
||||
@@ -364,6 +396,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(null, si.getBitmapPath());
|
||||
|
||||
assertEquals(456, si.getIconResourceId());
|
||||
assertEquals(null, si.getIconResName());
|
||||
|
||||
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
|
||||
|
||||
@@ -372,8 +405,11 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(null, si.getActivity());
|
||||
assertEquals(null, si.getIcon());
|
||||
assertEquals(0, si.getTitleResId());
|
||||
assertEquals(null, si.getTitleResName());
|
||||
assertEquals(0, si.getTextResId());
|
||||
assertEquals(null, si.getTextResName());
|
||||
assertEquals(0, si.getDisabledMessageResourceId());
|
||||
assertEquals(null, si.getDisabledMessageResName());
|
||||
assertEquals(null, si.getCategories());
|
||||
assertEquals(null, si.getIntent());
|
||||
assertEquals(0, si.getRank());
|
||||
@@ -383,6 +419,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(null, si.getBitmapPath());
|
||||
|
||||
assertEquals(456, si.getIconResourceId());
|
||||
assertEquals(null, si.getIconResName());
|
||||
}
|
||||
|
||||
public void testShortcutInfoClone_minimum() {
|
||||
@@ -445,6 +482,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
sorig.setBitmapPath("abc");
|
||||
sorig.setIconResourceId(456);
|
||||
|
||||
lookupAndFillInResourceNames(sorig);
|
||||
|
||||
ShortcutInfo si;
|
||||
|
||||
si = sorig.clone(/* flags=*/ 0);
|
||||
@@ -458,6 +497,9 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
.setIcon(Icon.createWithResource(mClientContext, 456)).build());
|
||||
assertEquals("text", si.getText());
|
||||
assertEquals(456, si.getIcon().getResId());
|
||||
assertEquals(0, si.getIconResourceId());
|
||||
assertEquals(null, si.getIconResName());
|
||||
assertEquals(null, si.getBitmapPath());
|
||||
|
||||
si = sorig.clone(/* flags=*/ 0);
|
||||
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
|
||||
@@ -586,6 +628,9 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
.setIcon(Icon.createWithResource(mClientContext, 456)).build());
|
||||
assertEquals(11, si.getTextResId());
|
||||
assertEquals(456, si.getIcon().getResId());
|
||||
assertEquals(0, si.getIconResourceId());
|
||||
assertEquals(null, si.getIconResName());
|
||||
assertEquals(null, si.getBitmapPath());
|
||||
|
||||
si = sorig.clone(/* flags=*/ 0);
|
||||
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
|
||||
@@ -593,6 +638,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(11, si.getTextResId());
|
||||
assertEquals("xyz", si.getTitle());
|
||||
assertEquals(0, si.getTitleResId());
|
||||
assertEquals(null, si.getTitleResName());
|
||||
|
||||
si = sorig.clone(/* flags=*/ 0);
|
||||
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
|
||||
@@ -600,6 +646,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(11, si.getTextResId());
|
||||
assertEquals(null, si.getTitle());
|
||||
assertEquals(123, si.getTitleResId());
|
||||
assertEquals(null, si.getTitleResName());
|
||||
|
||||
si = sorig.clone(/* flags=*/ 0);
|
||||
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
|
||||
@@ -607,6 +654,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(123, si.getRank());
|
||||
assertEquals("xxx", si.getText());
|
||||
assertEquals(0, si.getTextResId());
|
||||
assertEquals(null, si.getTextResName());
|
||||
|
||||
si = sorig.clone(/* flags=*/ 0);
|
||||
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
|
||||
@@ -614,6 +662,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(123, si.getRank());
|
||||
assertEquals(null, si.getText());
|
||||
assertEquals(1111, si.getTextResId());
|
||||
assertEquals(null, si.getTextResName());
|
||||
|
||||
si = sorig.clone(/* flags=*/ 0);
|
||||
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
|
||||
@@ -621,6 +670,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(123, si.getRank());
|
||||
assertEquals("xxx", si.getDisabledMessage());
|
||||
assertEquals(0, si.getDisabledMessageResourceId());
|
||||
assertEquals(null, si.getDisabledMessageResName());
|
||||
|
||||
si = sorig.clone(/* flags=*/ 0);
|
||||
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
|
||||
@@ -628,6 +678,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(123, si.getRank());
|
||||
assertEquals(null, si.getDisabledMessage());
|
||||
assertEquals(11111, si.getDisabledMessageResourceId());
|
||||
assertEquals(null, si.getDisabledMessageResName());
|
||||
|
||||
si = sorig.clone(/* flags=*/ 0);
|
||||
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
|
||||
@@ -731,7 +782,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(123, si.getRank());
|
||||
assertEquals(1, si.getExtras().getInt("k"));
|
||||
|
||||
assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE, si.getFlags());
|
||||
assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE
|
||||
| ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
|
||||
assertNotNull(si.getBitmapPath()); // Something should be set.
|
||||
assertEquals(0, si.getIconResourceId());
|
||||
assertTrue(si.getLastChangedTimestamp() < now);
|
||||
@@ -740,15 +792,14 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
public void testShortcutInfoSaveAndLoad_resId() throws InterruptedException {
|
||||
setCaller(CALLING_PACKAGE_1, USER_10);
|
||||
|
||||
final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
|
||||
getTestContext().getResources(), R.drawable.black_32x32));
|
||||
final Icon res32x32 = Icon.createWithResource(mClientContext, R.drawable.black_32x32);
|
||||
|
||||
PersistableBundle pb = new PersistableBundle();
|
||||
pb.putInt("k", 1);
|
||||
ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
|
||||
.setId("id")
|
||||
.setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
|
||||
.setIcon(bmp32x32)
|
||||
.setIcon(res32x32)
|
||||
.setTitleResId(10)
|
||||
.setTextResId(11)
|
||||
.setDisabledMessageResId(12)
|
||||
@@ -778,17 +829,21 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
|
||||
assertEquals(null, si.getIcon());
|
||||
assertEquals(10, si.getTitleResId());
|
||||
assertEquals("r10", si.getTitleResName());
|
||||
assertEquals(11, si.getTextResId());
|
||||
assertEquals("r11", si.getTextResName());
|
||||
assertEquals(12, si.getDisabledMessageResourceId());
|
||||
assertEquals("r12", si.getDisabledMessageResName());
|
||||
assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
|
||||
assertEquals("action", si.getIntent().getAction());
|
||||
assertEquals("val", si.getIntent().getStringExtra("key"));
|
||||
assertEquals(123, si.getRank());
|
||||
assertEquals(1, si.getExtras().getInt("k"));
|
||||
|
||||
assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE, si.getFlags());
|
||||
assertNotNull(si.getBitmapPath()); // Something should be set.
|
||||
assertEquals(0, si.getIconResourceId());
|
||||
assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_RES
|
||||
| ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
|
||||
assertNull(si.getBitmapPath());
|
||||
assertEquals(R.drawable.black_32x32, si.getIconResourceId());
|
||||
assertTrue(si.getLastChangedTimestamp() < now);
|
||||
}
|
||||
|
||||
@@ -840,7 +895,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(123, si.getRank());
|
||||
assertEquals(1, si.getExtras().getInt("k"));
|
||||
|
||||
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
|
||||
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
|
||||
assertNull(si.getBitmapPath()); // No icon.
|
||||
assertEquals(0, si.getIconResourceId());
|
||||
}
|
||||
@@ -848,15 +903,14 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
public void testShortcutInfoSaveAndLoad_forBackup_resId() {
|
||||
setCaller(CALLING_PACKAGE_1, USER_0);
|
||||
|
||||
final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
|
||||
getTestContext().getResources(), R.drawable.black_32x32));
|
||||
final Icon res32x32 = Icon.createWithResource(mClientContext, R.drawable.black_32x32);
|
||||
|
||||
PersistableBundle pb = new PersistableBundle();
|
||||
pb.putInt("k", 1);
|
||||
ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
|
||||
.setId("id")
|
||||
.setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
|
||||
.setIcon(bmp32x32)
|
||||
.setIcon(res32x32)
|
||||
.setTitleResId(10)
|
||||
.setTextResId(11)
|
||||
.setDisabledMessageResId(12)
|
||||
@@ -885,20 +939,23 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
|
||||
assertEquals(null, si.getIcon());
|
||||
assertEquals(10, si.getTitleResId());
|
||||
assertEquals("r10", si.getTitleResName());
|
||||
assertEquals(11, si.getTextResId());
|
||||
assertEquals("r11", si.getTextResName());
|
||||
assertEquals(12, si.getDisabledMessageResourceId());
|
||||
assertEquals("r12", si.getDisabledMessageResName());
|
||||
assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
|
||||
assertEquals("action", si.getIntent().getAction());
|
||||
assertEquals("val", si.getIntent().getStringExtra("key"));
|
||||
assertEquals(123, si.getRank());
|
||||
assertEquals(1, si.getExtras().getInt("k"));
|
||||
|
||||
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
|
||||
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
|
||||
assertNull(si.getBitmapPath()); // No icon.
|
||||
assertEquals(0, si.getIconResourceId());
|
||||
assertEquals(null, si.getIconResName());
|
||||
}
|
||||
|
||||
|
||||
public void testThrottling() {
|
||||
final ShortcutInfo si1 = makeShortcut("shortcut1");
|
||||
|
||||
@@ -1086,11 +1143,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
|
||||
final long origSequenceNumber = mService.getLocaleChangeSequenceNumber();
|
||||
|
||||
// onSystemLocaleChangedNoLock before boot completed will be ignored.
|
||||
mInternal.onSystemLocaleChangedNoLock();
|
||||
assertEquals(origSequenceNumber, mService.getLocaleChangeSequenceNumber());
|
||||
|
||||
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
|
||||
mInternal.onSystemLocaleChangedNoLock();
|
||||
assertEquals(origSequenceNumber + 1, mService.getLocaleChangeSequenceNumber());
|
||||
|
||||
@@ -1467,4 +1519,103 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// Test for a ShortcutInfo method.
|
||||
public void testGetResourcePackageName() {
|
||||
assertEquals(null, ShortcutInfo.getResourcePackageName(""));
|
||||
assertEquals(null, ShortcutInfo.getResourcePackageName("abc"));
|
||||
assertEquals("p", ShortcutInfo.getResourcePackageName("p:"));
|
||||
assertEquals("p", ShortcutInfo.getResourcePackageName("p:xx"));
|
||||
assertEquals("pac", ShortcutInfo.getResourcePackageName("pac:"));
|
||||
}
|
||||
|
||||
// Test for a ShortcutInfo method.
|
||||
public void testGetResourceTypeName() {
|
||||
assertEquals(null, ShortcutInfo.getResourceTypeName(""));
|
||||
assertEquals(null, ShortcutInfo.getResourceTypeName(":"));
|
||||
assertEquals(null, ShortcutInfo.getResourceTypeName("/"));
|
||||
assertEquals(null, ShortcutInfo.getResourceTypeName("/:"));
|
||||
assertEquals("a", ShortcutInfo.getResourceTypeName(":a/"));
|
||||
assertEquals("type", ShortcutInfo.getResourceTypeName("xxx:type/yyy"));
|
||||
}
|
||||
|
||||
// Test for a ShortcutInfo method.
|
||||
public void testGetResourceTypeAndEntryName() {
|
||||
assertEquals(null, ShortcutInfo.getResourceTypeAndEntryName(""));
|
||||
assertEquals(null, ShortcutInfo.getResourceTypeAndEntryName("abc"));
|
||||
assertEquals("", ShortcutInfo.getResourceTypeAndEntryName("p:"));
|
||||
assertEquals("x", ShortcutInfo.getResourceTypeAndEntryName(":x"));
|
||||
assertEquals("x", ShortcutInfo.getResourceTypeAndEntryName("p:x"));
|
||||
assertEquals("xyz", ShortcutInfo.getResourceTypeAndEntryName("pac:xyz"));
|
||||
}
|
||||
|
||||
// Test for a ShortcutInfo method.
|
||||
public void testGetResourceEntryName() {
|
||||
assertEquals(null, ShortcutInfo.getResourceEntryName(""));
|
||||
assertEquals(null, ShortcutInfo.getResourceEntryName("ab:"));
|
||||
assertEquals("", ShortcutInfo.getResourceEntryName("/"));
|
||||
assertEquals("abc", ShortcutInfo.getResourceEntryName("/abc"));
|
||||
assertEquals("abc", ShortcutInfo.getResourceEntryName("xyz/abc"));
|
||||
}
|
||||
|
||||
// Test for a ShortcutInfo method.
|
||||
public void testLookUpResourceName_systemResources() {
|
||||
// For android system resources, lookUpResourceName will simply return the value as a
|
||||
// string, regardless of "withType".
|
||||
final Resources res = getTestContext().getResources();
|
||||
|
||||
assertEquals("" + android.R.string.cancel, ShortcutInfo.lookUpResourceName(res,
|
||||
android.R.string.cancel, true, getTestContext().getPackageName()));
|
||||
assertEquals("" + android.R.drawable.alert_dark_frame, ShortcutInfo.lookUpResourceName(res,
|
||||
android.R.drawable.alert_dark_frame, true, getTestContext().getPackageName()));
|
||||
assertEquals("" + android.R.string.cancel, ShortcutInfo.lookUpResourceName(res,
|
||||
android.R.string.cancel, false, getTestContext().getPackageName()));
|
||||
}
|
||||
|
||||
public void testLookUpResourceName_appResources() {
|
||||
final Resources res = getTestContext().getResources();
|
||||
|
||||
assertEquals("shortcut_text1", ShortcutInfo.lookUpResourceName(res,
|
||||
R.string.shortcut_text1, false, getTestContext().getPackageName()));
|
||||
assertEquals("string/shortcut_text1", ShortcutInfo.lookUpResourceName(res,
|
||||
R.string.shortcut_text1, true, getTestContext().getPackageName()));
|
||||
|
||||
assertEquals("black_16x64", ShortcutInfo.lookUpResourceName(res,
|
||||
R.drawable.black_16x64, false, getTestContext().getPackageName()));
|
||||
assertEquals("drawable/black_16x64", ShortcutInfo.lookUpResourceName(res,
|
||||
R.drawable.black_16x64, true, getTestContext().getPackageName()));
|
||||
}
|
||||
|
||||
// Test for a ShortcutInfo method.
|
||||
public void testLookUpResourceId_systemResources() {
|
||||
final Resources res = getTestContext().getResources();
|
||||
|
||||
assertEquals(android.R.string.cancel, ShortcutInfo.lookUpResourceId(res,
|
||||
"" + android.R.string.cancel, null,
|
||||
getTestContext().getPackageName()));
|
||||
assertEquals(android.R.drawable.alert_dark_frame, ShortcutInfo.lookUpResourceId(res,
|
||||
"" + android.R.drawable.alert_dark_frame, null,
|
||||
getTestContext().getPackageName()));
|
||||
}
|
||||
|
||||
// Test for a ShortcutInfo method.
|
||||
public void testLookUpResourceId_appResources() {
|
||||
final Resources res = getTestContext().getResources();
|
||||
|
||||
assertEquals(R.string.shortcut_text1,
|
||||
ShortcutInfo.lookUpResourceId(res, "shortcut_text1", "string",
|
||||
getTestContext().getPackageName()));
|
||||
|
||||
assertEquals(R.string.shortcut_text1,
|
||||
ShortcutInfo.lookUpResourceId(res, "string/shortcut_text1", null,
|
||||
getTestContext().getPackageName()));
|
||||
|
||||
assertEquals(R.drawable.black_16x64,
|
||||
ShortcutInfo.lookUpResourceId(res, "black_16x64", "drawable",
|
||||
getTestContext().getPackageName()));
|
||||
|
||||
assertEquals(R.drawable.black_16x64,
|
||||
ShortcutInfo.lookUpResourceId(res, "drawable/black_16x64", null,
|
||||
getTestContext().getPackageName()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user