Merge "ShortcutManager: add remaining APIs." into nyc-dev
am: 4a07c34ca8
* commit '4a07c34ca86fddb75332eba22a4d7b9bd48205db':
ShortcutManager: add remaining APIs.
@@ -9994,6 +9994,7 @@ package android.content.pm {
|
||||
method public int getWeight();
|
||||
method public boolean hasIconFile();
|
||||
method public boolean hasIconResource();
|
||||
method public boolean hasKeyFieldsOnly();
|
||||
method public boolean isDynamic();
|
||||
method public boolean isPinned();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
@@ -10004,6 +10005,7 @@ package android.content.pm {
|
||||
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_KEY_FIELDS_ONLY = 16; // 0x10
|
||||
field public static final int FLAG_PINNED = 2; // 0x2
|
||||
}
|
||||
|
||||
@@ -10024,6 +10026,7 @@ package android.content.pm {
|
||||
method public void deleteAllDynamicShortcuts();
|
||||
method public void deleteDynamicShortcut(java.lang.String);
|
||||
method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
|
||||
method public int getIconMaxDimensions();
|
||||
method public int getMaxDynamicShortcutCount();
|
||||
method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
|
||||
method public long getRateLimitResetTime();
|
||||
@@ -29030,7 +29033,6 @@ package android.os {
|
||||
ctor public PersistableBundle();
|
||||
ctor public PersistableBundle(int);
|
||||
ctor public PersistableBundle(android.os.PersistableBundle);
|
||||
ctor public PersistableBundle(android.os.Bundle);
|
||||
method public java.lang.Object clone();
|
||||
method public int describeContents();
|
||||
method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
|
||||
|
||||
@@ -10388,6 +10388,7 @@ package android.content.pm {
|
||||
method public int getWeight();
|
||||
method public boolean hasIconFile();
|
||||
method public boolean hasIconResource();
|
||||
method public boolean hasKeyFieldsOnly();
|
||||
method public boolean isDynamic();
|
||||
method public boolean isPinned();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
@@ -10398,6 +10399,7 @@ package android.content.pm {
|
||||
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_KEY_FIELDS_ONLY = 16; // 0x10
|
||||
field public static final int FLAG_PINNED = 2; // 0x2
|
||||
}
|
||||
|
||||
@@ -10418,6 +10420,7 @@ package android.content.pm {
|
||||
method public void deleteAllDynamicShortcuts();
|
||||
method public void deleteDynamicShortcut(java.lang.String);
|
||||
method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
|
||||
method public int getIconMaxDimensions();
|
||||
method public int getMaxDynamicShortcutCount();
|
||||
method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
|
||||
method public long getRateLimitResetTime();
|
||||
@@ -31310,7 +31313,6 @@ package android.os {
|
||||
ctor public PersistableBundle();
|
||||
ctor public PersistableBundle(int);
|
||||
ctor public PersistableBundle(android.os.PersistableBundle);
|
||||
ctor public PersistableBundle(android.os.Bundle);
|
||||
method public java.lang.Object clone();
|
||||
method public int describeContents();
|
||||
method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
|
||||
|
||||
@@ -10004,6 +10004,7 @@ package android.content.pm {
|
||||
method public int getWeight();
|
||||
method public boolean hasIconFile();
|
||||
method public boolean hasIconResource();
|
||||
method public boolean hasKeyFieldsOnly();
|
||||
method public boolean isDynamic();
|
||||
method public boolean isPinned();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
@@ -10014,6 +10015,7 @@ package android.content.pm {
|
||||
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_KEY_FIELDS_ONLY = 16; // 0x10
|
||||
field public static final int FLAG_PINNED = 2; // 0x2
|
||||
}
|
||||
|
||||
@@ -10034,6 +10036,7 @@ package android.content.pm {
|
||||
method public void deleteAllDynamicShortcuts();
|
||||
method public void deleteDynamicShortcut(java.lang.String);
|
||||
method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
|
||||
method public int getIconMaxDimensions();
|
||||
method public int getMaxDynamicShortcutCount();
|
||||
method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
|
||||
method public long getRateLimitResetTime();
|
||||
@@ -29096,7 +29099,6 @@ package android.os {
|
||||
ctor public PersistableBundle();
|
||||
ctor public PersistableBundle(int);
|
||||
ctor public PersistableBundle(android.os.PersistableBundle);
|
||||
ctor public PersistableBundle(android.os.Bundle);
|
||||
method public java.lang.Object clone();
|
||||
method public int describeContents();
|
||||
method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
|
||||
|
||||
@@ -753,12 +753,11 @@ final class SystemServiceRegistry {
|
||||
|
||||
registerService(Context.SHORTCUT_SERVICE, ShortcutManager.class,
|
||||
new CachedServiceFetcher<ShortcutManager>() {
|
||||
@Override
|
||||
public ShortcutManager createService(ContextImpl ctx) {
|
||||
IBinder b = ServiceManager.getService(Context.SHORTCUT_SERVICE);
|
||||
return new ShortcutManager(ctx,
|
||||
IShortcutService.Stub.asInterface(b));
|
||||
}});
|
||||
@Override
|
||||
public ShortcutManager createService(ContextImpl ctx) {
|
||||
IBinder b = ServiceManager.getService(Context.SHORTCUT_SERVICE);
|
||||
return new ShortcutManager(ctx, IShortcutService.Stub.asInterface(b));
|
||||
}});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,8 @@ import android.content.pm.ShortcutInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -52,4 +54,8 @@ interface ILauncherApps {
|
||||
in UserHandle user);
|
||||
boolean startShortcut(String callingPackage, String packageName, String id,
|
||||
in Rect sourceBounds, in Bundle startActivityOptions, in UserHandle user);
|
||||
|
||||
int getShortcutIconResId(String callingPackage, in ShortcutInfo shortcut, in UserHandle user);
|
||||
ParcelFileDescriptor getShortcutIconFd(String callingPackage, in ShortcutInfo shortcut,
|
||||
in UserHandle user);
|
||||
}
|
||||
|
||||
@@ -44,5 +44,7 @@ interface IShortcutService {
|
||||
|
||||
long getRateLimitResetTime(String packageName, int userId);
|
||||
|
||||
int getIconMaxDimensions(String packageName, int userId);
|
||||
|
||||
void resetThrottling(); // system only API for developer opsions
|
||||
}
|
||||
@@ -183,7 +183,8 @@ public class LauncherApps {
|
||||
public static final int FLAG_GET_PINNED = 1 << 1;
|
||||
|
||||
/**
|
||||
* Requests "key" fields only.
|
||||
* Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()} for which
|
||||
* fields are available.
|
||||
*/
|
||||
public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
|
||||
|
||||
@@ -473,7 +474,11 @@ public class LauncherApps {
|
||||
*/
|
||||
@RequiresPermission(permission.BIND_APPWIDGET)
|
||||
public int getShortcutIconResId(@NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
|
||||
throw new RuntimeException("not implemented yet");
|
||||
try {
|
||||
return mService.getShortcutIconResId(mContext.getPackageName(), shortcut, user);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -488,7 +493,11 @@ public class LauncherApps {
|
||||
@RequiresPermission(permission.BIND_APPWIDGET)
|
||||
public ParcelFileDescriptor getShortcutIconFd(
|
||||
@NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
|
||||
throw new RuntimeException("not implemented yet");
|
||||
try {
|
||||
return mService.getShortcutIconFd(mContext.getPackageName(), shortcut, user);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,9 +19,11 @@ import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.PersistableBundle;
|
||||
@@ -60,6 +62,9 @@ public class ShortcutInfo implements Parcelable {
|
||||
/* @hide */
|
||||
public static final int FLAG_HAS_ICON_FILE = 1 << 3;
|
||||
|
||||
/* @hide */
|
||||
public static final int FLAG_KEY_FIELDS_ONLY = 1 << 4;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(flag = true,
|
||||
value = {
|
||||
@@ -67,6 +72,7 @@ public class ShortcutInfo implements Parcelable {
|
||||
FLAG_PINNED,
|
||||
FLAG_HAS_ICON_RES,
|
||||
FLAG_HAS_ICON_FILE,
|
||||
FLAG_KEY_FIELDS_ONLY,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ShortcutFlags {}
|
||||
@@ -114,10 +120,15 @@ public class ShortcutInfo implements Parcelable {
|
||||
@NonNull
|
||||
private String mTitle;
|
||||
|
||||
/**
|
||||
* Intent *with extras removed*.
|
||||
*/
|
||||
@NonNull
|
||||
private Intent mIntent;
|
||||
|
||||
// Internal use only.
|
||||
/**
|
||||
* Extras for the intent.
|
||||
*/
|
||||
@NonNull
|
||||
private PersistableBundle mIntentPersistableExtras;
|
||||
|
||||
@@ -149,6 +160,11 @@ public class ShortcutInfo implements Parcelable {
|
||||
mIcon = b.mIcon;
|
||||
mTitle = b.mTitle;
|
||||
mIntent = b.mIntent;
|
||||
final Bundle intentExtras = mIntent.getExtras();
|
||||
if (intentExtras != null) {
|
||||
mIntent.replaceExtras((Bundle) null);
|
||||
mIntentPersistableExtras = new PersistableBundle(intentExtras);
|
||||
}
|
||||
mWeight = b.mWeight;
|
||||
mExtras = b.mExtras;
|
||||
updateTimestamp();
|
||||
@@ -170,11 +186,12 @@ public class ShortcutInfo implements Parcelable {
|
||||
private ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags) {
|
||||
mId = source.mId;
|
||||
mPackageName = source.mPackageName;
|
||||
mActivityComponent = source.mActivityComponent;
|
||||
mFlags = source.mFlags;
|
||||
mLastChangedTimestamp = source.mLastChangedTimestamp;
|
||||
|
||||
if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
|
||||
mActivityComponent = source.mActivityComponent;
|
||||
|
||||
if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
|
||||
mIcon = source.mIcon;
|
||||
}
|
||||
@@ -188,6 +205,10 @@ public class ShortcutInfo implements Parcelable {
|
||||
mExtras = source.mExtras;
|
||||
mIconResourceId = source.mIconResourceId;
|
||||
mBitmapPath = source.mBitmapPath;
|
||||
|
||||
} else {
|
||||
// Set this bit.
|
||||
mFlags |= FLAG_KEY_FIELDS_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +259,39 @@ public class ShortcutInfo implements Parcelable {
|
||||
updateTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static Icon validateIcon(Icon icon) {
|
||||
switch (icon.getType()) {
|
||||
case Icon.TYPE_RESOURCE:
|
||||
case Icon.TYPE_BITMAP:
|
||||
break; // OK
|
||||
case Icon.TYPE_URI:
|
||||
if (ContentResolver.SCHEME_CONTENT.equals(icon.getUri().getScheme())) {
|
||||
break;
|
||||
}
|
||||
// Note "file:" is not supported, because depending on the path, system server
|
||||
// cannot access it. // TODO Revisit "file:" icon support
|
||||
|
||||
// fall through
|
||||
default:
|
||||
throw getInvalidIconException();
|
||||
}
|
||||
if (icon.hasTint()) {
|
||||
// TODO support it
|
||||
throw new IllegalArgumentException("Icons with tints are not supported");
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static IllegalArgumentException getInvalidIconException() {
|
||||
return new IllegalArgumentException("Unsupported icon type:"
|
||||
+" only bitmap, resource and content URI are supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for {@link ShortcutInfo} objects.
|
||||
*/
|
||||
@@ -273,7 +327,9 @@ public class ShortcutInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally sets the target activity.
|
||||
* Optionally sets the target activity. If it's not set, and if the caller application
|
||||
* has multiple launcher icons, this shortcut will be shown on all those icons.
|
||||
* If it's set, this shortcut will be only shown on this activity.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setActivityComponent(@NonNull ComponentName activityComponent) {
|
||||
@@ -284,15 +340,22 @@ public class ShortcutInfo implements Parcelable {
|
||||
/**
|
||||
* Optionally sets an icon.
|
||||
*
|
||||
* - Tint is not supported TODO Either check and throw, or support it.
|
||||
* - URI icons will be converted into Bitmap icons at the registration time.
|
||||
* <ul>
|
||||
* <li>Tints are not supported.
|
||||
* <li>Bitmaps, resources and "content:" URIs are supported.
|
||||
* <li>"content:" URI will be fetched when a shortcut is registered to
|
||||
* {@link ShortcutManager}. Changing the content from the same URI later will
|
||||
* not be reflected to launcher icons.
|
||||
* </ul>
|
||||
*
|
||||
* TODO Only allow Bitmap, Resource and URI types. byte[] type can easily go over
|
||||
* binder size limit.
|
||||
* <p>For performance reasons, icons will <b>NOT</b> be available on instances
|
||||
* returned by {@link ShortcutManager} or {@link LauncherApps}. Launcher applications
|
||||
* need to use {@link LauncherApps#getShortcutIconFd(ShortcutInfo, UserHandle)}
|
||||
* and {@link LauncherApps#getShortcutIconResId(ShortcutInfo, UserHandle)}.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setIcon(Icon icon) {
|
||||
mIcon = icon;
|
||||
mIcon = validateIcon(icon);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -316,7 +379,7 @@ public class ShortcutInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally sets the weight of a shortcut, which will be used by Launcher for sorting.
|
||||
* Optionally sets the weight of a shortcut, which will be used by the launcher for sorting.
|
||||
* The larger the weight, the more "important" a shortcut is.
|
||||
*/
|
||||
@NonNull
|
||||
@@ -326,8 +389,8 @@ public class ShortcutInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional values that application can set.
|
||||
* TODO: reserve keys starting with "android."
|
||||
* Optional values that applications can set. Applications can store any meta-data of
|
||||
* shortcuts in this, and retrieve later from {@link ShortcutInfo#getExtras()}.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setExtras(@NonNull PersistableBundle extras) {
|
||||
@@ -353,7 +416,7 @@ public class ShortcutInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ID of the shortcut.
|
||||
* Return the package name of the creator application.
|
||||
*/
|
||||
@NonNull
|
||||
public String getPackageName() {
|
||||
@@ -374,7 +437,7 @@ public class ShortcutInfo implements Parcelable {
|
||||
*
|
||||
* For performance reasons, this will <b>NOT</b> be available when an instance is returned
|
||||
* by {@link ShortcutManager} or {@link LauncherApps}. A launcher application needs to use
|
||||
* other APIs in LauncherApps to fetch the bitmap. TODO Add a precondition for it.
|
||||
* other APIs in LauncherApps to fetch the bitmap.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@@ -385,22 +448,46 @@ public class ShortcutInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* Return the shortcut title.
|
||||
*
|
||||
* <p>All shortcuts must have a non-empty title, but this method will return null when
|
||||
* {@link #hasKeyFieldsOnly()} is true.
|
||||
*/
|
||||
@NonNull
|
||||
@Nullable
|
||||
public String getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the intent.
|
||||
* TODO Set mIntentPersistableExtras and before returning.
|
||||
*
|
||||
* <p>All shortcuts must have an intent, but this method will return null when
|
||||
* {@link #hasKeyFieldsOnly()} is true.
|
||||
*/
|
||||
@NonNull
|
||||
@Nullable
|
||||
public Intent getIntent() {
|
||||
if (mIntent == null) {
|
||||
return null;
|
||||
}
|
||||
final Intent intent = new Intent(mIntent);
|
||||
intent.replaceExtras(
|
||||
mIntentPersistableExtras != null ? new Bundle(mIntentPersistableExtras) : null);
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return "raw" intent, which is the original intent without the extras.
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public Intent getIntentNoExtras() {
|
||||
return mIntent;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
/**
|
||||
* The extras in the intent. We convert extras into {@link PersistableBundle} so we can
|
||||
* persist them.
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public PersistableBundle getIntentPersistableExtras() {
|
||||
return mIntentPersistableExtras;
|
||||
@@ -483,6 +570,23 @@ public class ShortcutInfo implements Parcelable {
|
||||
return hasFlags(FLAG_HAS_ICON_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether a shortcut only contains "key" information only or not. If true, only the
|
||||
* following fields are available.
|
||||
* <ul>
|
||||
* <li>{@link #getId()}
|
||||
* <li>{@link #getPackageName()}
|
||||
* <li>{@link #getLastChangedTimestamp()}
|
||||
* <li>{@link #isDynamic()}
|
||||
* <li>{@link #isPinned()}
|
||||
* <li>{@link #hasIconResource()}
|
||||
* <li>{@link #hasIconFile()}
|
||||
* </ul>
|
||||
*/
|
||||
public boolean hasKeyFieldsOnly() {
|
||||
return hasFlags(FLAG_KEY_FIELDS_ONLY);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void updateTimestamp() {
|
||||
mLastChangedTimestamp = System.currentTimeMillis();
|
||||
@@ -495,33 +599,13 @@ public class ShortcutInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setIcon(Icon icon) {
|
||||
mIcon = icon;
|
||||
public void clearIcon() {
|
||||
mIcon = null;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setTitle(String title) {
|
||||
mTitle = title;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setIntent(Intent intent) {
|
||||
mIntent = intent;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setIntentPersistableExtras(PersistableBundle intentPersistableExtras) {
|
||||
mIntentPersistableExtras = intentPersistableExtras;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setWeight(int weight) {
|
||||
mWeight = weight;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setExtras(PersistableBundle extras) {
|
||||
mExtras = extras;
|
||||
public void setIconResourceId(int iconResourceId) {
|
||||
mIconResourceId = iconResourceId;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@@ -643,9 +727,10 @@ public class ShortcutInfo implements Parcelable {
|
||||
sb.append(", extras=");
|
||||
sb.append(mExtras);
|
||||
|
||||
sb.append(", flags=");
|
||||
sb.append(mFlags);
|
||||
|
||||
if (includeInternalData) {
|
||||
sb.append(", flags=");
|
||||
sb.append(mFlags);
|
||||
|
||||
sb.append(", iconRes=");
|
||||
sb.append(mIconResourceId);
|
||||
|
||||
@@ -239,6 +239,17 @@ public class ShortcutManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the max width and height for icons, in pixels.
|
||||
*/
|
||||
public int getIconMaxDimensions() {
|
||||
try {
|
||||
return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide injection point */
|
||||
@VisibleForTesting
|
||||
protected int injectMyUserId() {
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.annotation.UserIdInt;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LauncherApps.ShortcutQuery;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -55,4 +57,10 @@ public abstract class ShortcutServiceInternal {
|
||||
@NonNull String packageName, @NonNull String shortcutId, int userId);
|
||||
|
||||
public abstract void addListener(@NonNull ShortcutChangeListener listener);
|
||||
|
||||
public abstract int getShortcutIconResId(@NonNull String callingPackage,
|
||||
@NonNull ShortcutInfo shortcut, int userId);
|
||||
|
||||
public abstract ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
|
||||
@NonNull ShortcutInfo shortcut, int userId);
|
||||
}
|
||||
|
||||
@@ -86,6 +86,8 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa
|
||||
* @param b a Bundle to be copied.
|
||||
*
|
||||
* @throws IllegalArgumentException if any element of {@code b} cannot be persisted.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public PersistableBundle(Bundle b) {
|
||||
this(b.getMap());
|
||||
|
||||
@@ -627,6 +627,11 @@ public final class Icon implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean hasTint() {
|
||||
return (mTintList != null) || (mTintMode != DEFAULT_TINT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Icon pointing to an image file specified by path.
|
||||
*
|
||||
|
||||
@@ -42,6 +42,7 @@ import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IInterface;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
@@ -328,6 +329,26 @@ public class LauncherAppsService extends SystemService {
|
||||
ids, user.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getShortcutIconResId(String callingPackage, ShortcutInfo shortcut,
|
||||
UserHandle user) {
|
||||
enforceShortcutPermission(user);
|
||||
verifyCallingPackage(callingPackage);
|
||||
|
||||
return mShortcutServiceInternal.getShortcutIconResId(callingPackage, shortcut,
|
||||
user.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor getShortcutIconFd(String callingPackage, ShortcutInfo shortcut,
|
||||
UserHandle user) {
|
||||
enforceShortcutPermission(user);
|
||||
verifyCallingPackage(callingPackage);
|
||||
|
||||
return mShortcutServiceInternal.getShortcutIconFd(callingPackage, shortcut,
|
||||
user.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
|
||||
Rect sourceBounds, Bundle startActivityOptions, UserHandle user)
|
||||
|
||||
@@ -726,9 +726,6 @@ public final class SystemServer {
|
||||
// Always start the Device Policy Manager, so that the API is compatible with
|
||||
// API8.
|
||||
mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
|
||||
|
||||
// TODO is this a good place?
|
||||
mSystemServiceManager.startService(ShortcutService.Lifecycle.class);
|
||||
}
|
||||
|
||||
if (!disableSystemUI) {
|
||||
@@ -1139,6 +1136,8 @@ public final class SystemServer {
|
||||
}
|
||||
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
|
||||
}
|
||||
// LauncherAppsService uses ShortcutService.
|
||||
mSystemServiceManager.startService(ShortcutService.Lifecycle.class);
|
||||
|
||||
mSystemServiceManager.startService(LauncherAppsService.class);
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 12 KiB |
BIN
services/tests/servicestests/res/drawable-nodpi/black_16x64.png
Normal file
|
After Width: | Height: | Size: 160 B |
BIN
services/tests/servicestests/res/drawable-nodpi/black_32x32.png
Normal file
|
After Width: | Height: | Size: 159 B |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 919 B |
BIN
services/tests/servicestests/res/drawable-nodpi/black_64x16.png
Normal file
|
After Width: | Height: | Size: 159 B |
BIN
services/tests/servicestests/res/drawable-nodpi/black_64x64.png
Normal file
|
After Width: | Height: | Size: 168 B |
BIN
services/tests/servicestests/res/drawable-nodpi/icon1.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
services/tests/servicestests/res/drawable-nodpi/icon2.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
@@ -25,19 +25,26 @@ import android.content.pm.LauncherApps.ShortcutQuery;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.content.pm.ShortcutServiceInternal;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Bundle;
|
||||
import android.os.FileUtils;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.UserHandle;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.mock.MockContext;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.frameworks.servicestests.R;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
@@ -45,13 +52,16 @@ import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Tests for ShortcutService and ShortcutManager.
|
||||
@@ -61,7 +71,11 @@ import java.util.Map;
|
||||
-r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
|
||||
adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest \
|
||||
-w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
|
||||
|
||||
* TODO: Add checks with assertAllNotHaveIcon()
|
||||
* TODO: Cross-user test (do in CTS?)
|
||||
*/
|
||||
@SmallTest
|
||||
public class ShortcutManagerTest extends AndroidTestCase {
|
||||
private static final String TAG = "ShortcutManagerTest";
|
||||
|
||||
@@ -77,10 +91,19 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
public String getPackageName() {
|
||||
return mInjectedClientPackage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
return ShortcutManagerTest.this.getContext().getResources();
|
||||
}
|
||||
}
|
||||
|
||||
/** Context used in the service side */
|
||||
private final class ServiceContext extends MockContext {
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
return ShortcutManagerTest.this.getContext().getResources();
|
||||
}
|
||||
}
|
||||
|
||||
/** ShortcutService with injection override methods. */
|
||||
@@ -95,6 +118,7 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
setResetIntervalForTest(INTERVAL);
|
||||
setMaxDynamicShortcutsForTest(MAX_SHORTCUTS);
|
||||
setMaxDailyUpdatesForTest(MAX_DAILY_UPDATES);
|
||||
setMaxIconDimensionForTest(MAX_ICON_DIMENSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,7 +132,7 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
int injectGetPackageUid(String packageName) {
|
||||
int injectGetPackageUid(String packageName, int userId) {
|
||||
Integer uid = mInjectedPackageUidMap.get(packageName);
|
||||
return uid != null ? uid : -1;
|
||||
}
|
||||
@@ -122,6 +146,11 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
File injectUserDataPath(@UserIdInt int userId) {
|
||||
return new File(mInjectedFilePathRoot, "user-" + userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
|
||||
// Can't check
|
||||
}
|
||||
}
|
||||
|
||||
/** ShortcutManager with injection override methods. */
|
||||
@@ -181,10 +210,12 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
|
||||
private static final long INTERVAL = 10000;
|
||||
|
||||
private static final int MAX_SHORTCUTS = 5;
|
||||
private static final int MAX_SHORTCUTS = 10;
|
||||
|
||||
private static final int MAX_DAILY_UPDATES = 3;
|
||||
|
||||
private static final int MAX_ICON_DIMENSION = 128;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
@@ -233,7 +264,8 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
/** Replace the current calling package */
|
||||
private void setCaller(String packageName) {
|
||||
mInjectedClientPackage = packageName;
|
||||
mInjectedCallingUid = Preconditions.checkNotNull(mInjectedPackageUidMap.get(packageName));
|
||||
mInjectedCallingUid = Preconditions.checkNotNull(mInjectedPackageUidMap.get(packageName),
|
||||
"Unknown package");
|
||||
}
|
||||
|
||||
private String getCallingPackage() {
|
||||
@@ -342,6 +374,28 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a shortcut with an ID and icon.
|
||||
*/
|
||||
private ShortcutInfo makeShortcutWithIcon(String id, Icon icon) {
|
||||
return makeShortcut(
|
||||
id, "Title-" + id, /* activity =*/ null, icon,
|
||||
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
|
||||
}
|
||||
|
||||
private ShortcutInfo makePackageShortcut(String packageName, String id) {
|
||||
String origCaller = getCallingPackage();
|
||||
|
||||
setCaller(packageName);
|
||||
ShortcutInfo s = makeShortcut(
|
||||
id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
|
||||
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
|
||||
setCaller(origCaller); // restore the caller
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make multiple shortcuts with IDs.
|
||||
*/
|
||||
@@ -413,6 +467,7 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
@NonNull
|
||||
private List<ShortcutInfo> assertShortcutIds(@NonNull List<ShortcutInfo> actualShortcuts,
|
||||
String... expectedIds) {
|
||||
assertEquals(expectedIds.length, actualShortcuts.size());
|
||||
final HashSet<String> expected = new HashSet<>(Arrays.asList(expectedIds));
|
||||
final HashSet<String> actual = new HashSet<>();
|
||||
for (ShortcutInfo s : actualShortcuts) {
|
||||
@@ -460,6 +515,35 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
return actualShortcuts;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<ShortcutInfo> assertAllNotHaveIcon(
|
||||
@NonNull List<ShortcutInfo> actualShortcuts) {
|
||||
for (ShortcutInfo s : actualShortcuts) {
|
||||
assertNull("ID " + s.getId(), s.getIcon());
|
||||
}
|
||||
return actualShortcuts;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<ShortcutInfo> assertAllHaveIconResId(
|
||||
@NonNull List<ShortcutInfo> actualShortcuts) {
|
||||
for (ShortcutInfo s : actualShortcuts) {
|
||||
assertTrue("ID " + s.getId() + " not have icon res ID", s.hasIconResource());
|
||||
assertFalse("ID " + s.getId() + " shouldn't have icon FD", s.hasIconFile());
|
||||
}
|
||||
return actualShortcuts;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<ShortcutInfo> assertAllHaveIconFile(
|
||||
@NonNull List<ShortcutInfo> actualShortcuts) {
|
||||
for (ShortcutInfo s : actualShortcuts) {
|
||||
assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource());
|
||||
assertTrue("ID " + s.getId() + " not have icon FD", s.hasIconFile());
|
||||
}
|
||||
return actualShortcuts;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<ShortcutInfo> assertAllHaveFlags(@NonNull List<ShortcutInfo> actualShortcuts,
|
||||
int shortcutFlags) {
|
||||
@@ -469,6 +553,24 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
return actualShortcuts;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<ShortcutInfo> assertAllKeyFieldsOnly(
|
||||
@NonNull List<ShortcutInfo> actualShortcuts) {
|
||||
for (ShortcutInfo s : actualShortcuts) {
|
||||
assertTrue("ID " + s.getId(), s.hasKeyFieldsOnly());
|
||||
}
|
||||
return actualShortcuts;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<ShortcutInfo> assertAllNotKeyFieldsOnly(
|
||||
@NonNull List<ShortcutInfo> actualShortcuts) {
|
||||
for (ShortcutInfo s : actualShortcuts) {
|
||||
assertFalse("ID " + s.getId(), s.hasKeyFieldsOnly());
|
||||
}
|
||||
return actualShortcuts;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<ShortcutInfo> assertAllDynamic(@NonNull List<ShortcutInfo> actualShortcuts) {
|
||||
return assertAllHaveFlags(actualShortcuts, ShortcutInfo.FLAG_DYNAMIC);
|
||||
@@ -488,6 +590,31 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
return actualShortcuts;
|
||||
}
|
||||
|
||||
private void assertBitmapSize(int expectedWidth, int expectedHeight, @NonNull Bitmap bitmap) {
|
||||
assertEquals("width", expectedWidth, bitmap.getWidth());
|
||||
assertEquals("height", expectedHeight, bitmap.getHeight());
|
||||
}
|
||||
|
||||
private <T> void assertAllUnique(Collection<T> list) {
|
||||
final Set<Object> set = new HashSet<>();
|
||||
for (T item : list) {
|
||||
if (set.contains(item)) {
|
||||
fail("Duplicate item found: " + item + " (in the list: " + list + ")");
|
||||
}
|
||||
set.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Bitmap pfdToBitmap(@NonNull ParcelFileDescriptor pfd) {
|
||||
Preconditions.checkNotNull(pfd);
|
||||
try {
|
||||
return BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
|
||||
} finally {
|
||||
IoUtils.closeQuietly(pfd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the first launch path, no settings file available.
|
||||
*/
|
||||
@@ -594,13 +721,17 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
final ShortcutInfo si3 = makeShortcut("shortcut3");
|
||||
|
||||
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
|
||||
assertEquals(2, mManager.getDynamicShortcuts().size());
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mManager.getDynamicShortcuts()),
|
||||
"shortcut1", "shortcut2");
|
||||
assertEquals(2, mManager.getRemainingCallCount());
|
||||
|
||||
// TODO: Check fields
|
||||
|
||||
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
|
||||
assertEquals(1, mManager.getDynamicShortcuts().size());
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mManager.getDynamicShortcuts()),
|
||||
"shortcut1");
|
||||
assertEquals(1, mManager.getRemainingCallCount());
|
||||
|
||||
assertTrue(mManager.setDynamicShortcuts(Arrays.asList()));
|
||||
@@ -629,16 +760,22 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
|
||||
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
|
||||
assertEquals(2, mManager.getRemainingCallCount());
|
||||
assertEquals(1, mManager.getDynamicShortcuts().size());
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mManager.getDynamicShortcuts()),
|
||||
"shortcut1");
|
||||
|
||||
assertTrue(mManager.addDynamicShortcut(si2));
|
||||
assertEquals(1, mManager.getRemainingCallCount());
|
||||
assertEquals(2, mManager.getDynamicShortcuts().size());
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mManager.getDynamicShortcuts()),
|
||||
"shortcut1", "shortcut2");
|
||||
|
||||
// Add with the same ID
|
||||
assertTrue(mManager.addDynamicShortcut(makeShortcut("shortcut1")));
|
||||
assertEquals(0, mManager.getRemainingCallCount());
|
||||
assertEquals(2, mManager.getDynamicShortcuts().size()); // Still 2
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mManager.getDynamicShortcuts()),
|
||||
"shortcut1", "shortcut2");
|
||||
|
||||
// TODO Check max number
|
||||
|
||||
@@ -651,24 +788,35 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
final ShortcutInfo si3 = makeShortcut("shortcut3");
|
||||
|
||||
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
|
||||
assertEquals(3, mManager.getDynamicShortcuts().size());
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mManager.getDynamicShortcuts()),
|
||||
"shortcut1", "shortcut2", "shortcut3");
|
||||
|
||||
assertEquals(2, mManager.getRemainingCallCount());
|
||||
|
||||
mManager.deleteDynamicShortcut("shortcut1");
|
||||
assertEquals(2, mManager.getDynamicShortcuts().size());
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mManager.getDynamicShortcuts()),
|
||||
"shortcut2", "shortcut3");
|
||||
|
||||
mManager.deleteDynamicShortcut("shortcut1");
|
||||
assertEquals(2, mManager.getDynamicShortcuts().size());
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mManager.getDynamicShortcuts()),
|
||||
"shortcut2", "shortcut3");
|
||||
|
||||
mManager.deleteDynamicShortcut("shortcutXXX");
|
||||
assertEquals(2, mManager.getDynamicShortcuts().size());
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mManager.getDynamicShortcuts()),
|
||||
"shortcut2", "shortcut3");
|
||||
|
||||
mManager.deleteDynamicShortcut("shortcut2");
|
||||
assertEquals(1, mManager.getDynamicShortcuts().size());
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mManager.getDynamicShortcuts()),
|
||||
"shortcut3");
|
||||
|
||||
mManager.deleteDynamicShortcut("shortcut3");
|
||||
assertEquals(0, mManager.getDynamicShortcuts().size());
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mManager.getDynamicShortcuts()));
|
||||
|
||||
// Still 2 calls left.
|
||||
assertEquals(2, mManager.getRemainingCallCount());
|
||||
@@ -682,7 +830,9 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
final ShortcutInfo si3 = makeShortcut("shortcut3");
|
||||
|
||||
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
|
||||
assertEquals(3, mManager.getDynamicShortcuts().size());
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mManager.getDynamicShortcuts()),
|
||||
"shortcut1", "shortcut2", "shortcut3");
|
||||
|
||||
assertEquals(2, mManager.getRemainingCallCount());
|
||||
|
||||
@@ -732,7 +882,7 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
|
||||
// Now it should work.
|
||||
mInjectedCurrentTimeLillis++;
|
||||
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
|
||||
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); // fail
|
||||
assertEquals(2, mManager.getRemainingCallCount());
|
||||
|
||||
mInjectedCurrentTimeLillis++;
|
||||
@@ -831,6 +981,226 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si2)));
|
||||
}
|
||||
|
||||
public void testIcons() {
|
||||
final Icon res32x32 = Icon.createWithResource(mContext, R.drawable.black_32x32);
|
||||
final Icon res64x64 = Icon.createWithResource(mContext, R.drawable.black_64x64);
|
||||
final Icon res512x512 = Icon.createWithResource(mContext, R.drawable.black_512x512);
|
||||
|
||||
final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
|
||||
mContext.getResources(), R.drawable.black_32x32));
|
||||
final Icon bmp64x64 = Icon.createWithBitmap(BitmapFactory.decodeResource(
|
||||
mContext.getResources(), R.drawable.black_64x64));
|
||||
final Icon bmp512x512 = Icon.createWithBitmap(BitmapFactory.decodeResource(
|
||||
mContext.getResources(), R.drawable.black_512x512));
|
||||
|
||||
// Set from package 1
|
||||
setCaller(CALLING_PACKAGE_1);
|
||||
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
|
||||
makeShortcutWithIcon("res32x32", res32x32),
|
||||
makeShortcutWithIcon("res64x64", res64x64),
|
||||
makeShortcutWithIcon("bmp32x32", bmp32x32),
|
||||
makeShortcutWithIcon("bmp64x64", bmp64x64),
|
||||
makeShortcutWithIcon("bmp512x512", bmp512x512),
|
||||
makeShortcut("none")
|
||||
)));
|
||||
|
||||
// getDynamicShortcuts() shouldn't return icons, thus assertAllNotHaveIcon().
|
||||
assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
|
||||
"res32x32",
|
||||
"res64x64",
|
||||
"bmp32x32",
|
||||
"bmp64x64",
|
||||
"bmp512x512",
|
||||
"none");
|
||||
|
||||
// Call from another caller with the same ID, just to make sure storage is per-package.
|
||||
setCaller(CALLING_PACKAGE_2);
|
||||
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
|
||||
makeShortcutWithIcon("res32x32", res512x512),
|
||||
makeShortcutWithIcon("res64x64", res512x512),
|
||||
makeShortcutWithIcon("none", res512x512)
|
||||
)));
|
||||
assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
|
||||
"res32x32",
|
||||
"res64x64",
|
||||
"none");
|
||||
|
||||
dumpsysOnLogcat();
|
||||
|
||||
// Load from launcher.
|
||||
Bitmap bmp;
|
||||
|
||||
setCaller(LAUNCHER_1);
|
||||
|
||||
// Check hasIconResource()/hasIconFile().
|
||||
assertShortcutIds(assertAllHaveIconResId(mInternal.getShortcutInfo(
|
||||
getCallingPackage(), CALLING_PACKAGE_1, Arrays.asList("res32x32"),
|
||||
getCallingUserId())), "res32x32");
|
||||
|
||||
assertShortcutIds(assertAllHaveIconResId(mInternal.getShortcutInfo(
|
||||
getCallingPackage(), CALLING_PACKAGE_1, Arrays.asList("res64x64"),
|
||||
getCallingUserId())), "res64x64");
|
||||
|
||||
assertShortcutIds(assertAllHaveIconFile(mInternal.getShortcutInfo(
|
||||
getCallingPackage(), CALLING_PACKAGE_1, Arrays.asList("bmp32x32"),
|
||||
getCallingUserId())), "bmp32x32");
|
||||
assertShortcutIds(assertAllHaveIconFile(mInternal.getShortcutInfo(
|
||||
getCallingPackage(), CALLING_PACKAGE_1, Arrays.asList("bmp64x64"),
|
||||
getCallingUserId())), "bmp64x64");
|
||||
assertShortcutIds(assertAllHaveIconFile(mInternal.getShortcutInfo(
|
||||
getCallingPackage(), CALLING_PACKAGE_1, Arrays.asList("bmp512x512"),
|
||||
getCallingUserId())), "bmp512x512");
|
||||
|
||||
// Check
|
||||
assertEquals(
|
||||
R.drawable.black_32x32,
|
||||
mInternal.getShortcutIconResId(getCallingPackage(),
|
||||
makePackageShortcut(CALLING_PACKAGE_1, "res32x32"), getCallingUserId()));
|
||||
|
||||
assertEquals(
|
||||
R.drawable.black_64x64,
|
||||
mInternal.getShortcutIconResId(
|
||||
getCallingPackage(),
|
||||
makePackageShortcut(CALLING_PACKAGE_1, "res64x64"), getCallingUserId()));
|
||||
|
||||
assertEquals(
|
||||
0, // because it's not a resource
|
||||
mInternal.getShortcutIconResId(
|
||||
getCallingPackage(),
|
||||
makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUserId()));
|
||||
assertEquals(
|
||||
0, // because it's not a resource
|
||||
mInternal.getShortcutIconResId(
|
||||
getCallingPackage(),
|
||||
makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUserId()));
|
||||
assertEquals(
|
||||
0, // because it's not a resource
|
||||
mInternal.getShortcutIconResId(
|
||||
getCallingPackage(),
|
||||
makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUserId()));
|
||||
|
||||
bmp = pfdToBitmap(mInternal.getShortcutIconFd(
|
||||
getCallingPackage(),
|
||||
makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUserId()));
|
||||
assertBitmapSize(32, 32, bmp);
|
||||
|
||||
bmp = pfdToBitmap(mInternal.getShortcutIconFd(
|
||||
getCallingPackage(),
|
||||
makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUserId()));
|
||||
assertBitmapSize(64, 64, bmp);
|
||||
|
||||
bmp = pfdToBitmap(mInternal.getShortcutIconFd(
|
||||
getCallingPackage(),
|
||||
makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUserId()));
|
||||
assertBitmapSize(128, 128, bmp);
|
||||
|
||||
// TODO Test the content URI case too.
|
||||
}
|
||||
|
||||
private void checkShrinkBitmap(int expectedWidth, int expectedHeight, int resId, int maxSize) {
|
||||
assertBitmapSize(expectedWidth, expectedHeight,
|
||||
ShortcutService.shrinkBitmap(BitmapFactory.decodeResource(
|
||||
mContext.getResources(), resId),
|
||||
maxSize));
|
||||
}
|
||||
|
||||
public void testShrinkBitmap() {
|
||||
checkShrinkBitmap(32, 32, R.drawable.black_512x512, 32);
|
||||
checkShrinkBitmap(511, 511, R.drawable.black_512x512, 511);
|
||||
checkShrinkBitmap(512, 512, R.drawable.black_512x512, 512);
|
||||
|
||||
checkShrinkBitmap(1024, 4096, R.drawable.black_1024x4096, 4096);
|
||||
checkShrinkBitmap(1024, 4096, R.drawable.black_1024x4096, 4100);
|
||||
checkShrinkBitmap(512, 2048, R.drawable.black_1024x4096, 2048);
|
||||
|
||||
checkShrinkBitmap(4096, 1024, R.drawable.black_4096x1024, 4096);
|
||||
checkShrinkBitmap(4096, 1024, R.drawable.black_4096x1024, 4100);
|
||||
checkShrinkBitmap(2048, 512, R.drawable.black_4096x1024, 2048);
|
||||
}
|
||||
|
||||
private File openIconFileForWriteAndGetPath(int userId, String packageName)
|
||||
throws IOException {
|
||||
// Shortcut IDs aren't used in the path, so just pass the same ID.
|
||||
final FileOutputStreamWithPath out =
|
||||
mService.openIconFileForWrite(userId, makePackageShortcut(packageName, "id"));
|
||||
out.close();
|
||||
return out.getFile();
|
||||
}
|
||||
|
||||
public void testOpenIconFileForWrite() throws IOException {
|
||||
mInjectedCurrentTimeLillis = 1000;
|
||||
|
||||
final File p10_1_1 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
|
||||
final File p10_1_2 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
|
||||
|
||||
final File p10_2_1 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_2);
|
||||
final File p10_2_2 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_2);
|
||||
|
||||
final File p11_1_1 = openIconFileForWriteAndGetPath(11, CALLING_PACKAGE_1);
|
||||
final File p11_1_2 = openIconFileForWriteAndGetPath(11, CALLING_PACKAGE_1);
|
||||
|
||||
mInjectedCurrentTimeLillis++;
|
||||
|
||||
final File p10_1_3 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
|
||||
final File p10_1_4 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
|
||||
final File p10_1_5 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
|
||||
|
||||
final File p10_2_3 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_2);
|
||||
final File p11_1_3 = openIconFileForWriteAndGetPath(11, CALLING_PACKAGE_1);
|
||||
|
||||
// Make sure their paths are all unique
|
||||
assertAllUnique(Arrays.asList(
|
||||
p10_1_1,
|
||||
p10_1_2,
|
||||
p10_1_3,
|
||||
p10_1_4,
|
||||
p10_1_5,
|
||||
|
||||
p10_2_1,
|
||||
p10_2_2,
|
||||
p10_2_3,
|
||||
|
||||
p11_1_1,
|
||||
p11_1_2,
|
||||
p11_1_3
|
||||
));
|
||||
|
||||
// Check each set has the same parent.
|
||||
assertEquals(p10_1_1.getParent(), p10_1_2.getParent());
|
||||
assertEquals(p10_1_1.getParent(), p10_1_3.getParent());
|
||||
assertEquals(p10_1_1.getParent(), p10_1_4.getParent());
|
||||
assertEquals(p10_1_1.getParent(), p10_1_5.getParent());
|
||||
|
||||
assertEquals(p10_2_1.getParent(), p10_2_2.getParent());
|
||||
assertEquals(p10_2_1.getParent(), p10_2_3.getParent());
|
||||
|
||||
assertEquals(p11_1_1.getParent(), p11_1_2.getParent());
|
||||
assertEquals(p11_1_1.getParent(), p11_1_3.getParent());
|
||||
|
||||
// Check the parents are still unique.
|
||||
assertAllUnique(Arrays.asList(
|
||||
p10_1_1.getParent(),
|
||||
p10_2_1.getParent(),
|
||||
p11_1_1.getParent()
|
||||
));
|
||||
|
||||
// All files created at the same time for the same package/user, expcet for the first ones,
|
||||
// will have "_" in the path.
|
||||
assertFalse(p10_1_1.getName().contains("_"));
|
||||
assertTrue(p10_1_2.getName().contains("_"));
|
||||
assertFalse(p10_1_3.getName().contains("_"));
|
||||
assertTrue(p10_1_4.getName().contains("_"));
|
||||
assertTrue(p10_1_5.getName().contains("_"));
|
||||
|
||||
assertFalse(p10_2_1.getName().contains("_"));
|
||||
assertTrue(p10_2_2.getName().contains("_"));
|
||||
assertFalse(p10_2_3.getName().contains("_"));
|
||||
|
||||
assertFalse(p11_1_1.getName().contains("_"));
|
||||
assertTrue(p11_1_2.getName().contains("_"));
|
||||
assertFalse(p11_1_3.getName().contains("_"));
|
||||
}
|
||||
|
||||
// TODO: updateShortcuts()
|
||||
// TODO: getPinnedShortcuts()
|
||||
|
||||
@@ -860,9 +1230,10 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
|
||||
// Get dynamic
|
||||
assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
|
||||
assertAllNotKeyFieldsOnly(
|
||||
mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_1,
|
||||
/* activity =*/ null,
|
||||
ShortcutQuery.FLAG_GET_DYNAMIC, getCallingUserId()),
|
||||
ShortcutQuery.FLAG_GET_DYNAMIC, getCallingUserId())),
|
||||
"s1", "s2"))));
|
||||
|
||||
// Get pinned
|
||||
@@ -874,18 +1245,20 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
|
||||
// Get both, with timestamp
|
||||
assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
|
||||
mInternal.getShortcuts(getCallingPackage(), /* time =*/ 1000, CALLING_PACKAGE_2,
|
||||
assertAllNotKeyFieldsOnly(mInternal.getShortcuts(getCallingPackage(),
|
||||
/* time =*/ 1000, CALLING_PACKAGE_2,
|
||||
/* activity =*/ null,
|
||||
ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_DYNAMIC,
|
||||
getCallingUserId()),
|
||||
getCallingUserId())),
|
||||
"s2", "s3"))));
|
||||
|
||||
// FLAG_GET_KEY_FIELDS_ONLY
|
||||
assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
|
||||
mInternal.getShortcuts(getCallingPackage(), /* time =*/ 1000, CALLING_PACKAGE_2,
|
||||
assertAllKeyFieldsOnly(mInternal.getShortcuts(getCallingPackage(),
|
||||
/* time =*/ 1000, CALLING_PACKAGE_2,
|
||||
/* activity =*/ null,
|
||||
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY,
|
||||
getCallingUserId()),
|
||||
getCallingUserId())),
|
||||
"s2", "s3"))));
|
||||
|
||||
// Pin some shortcuts.
|
||||
@@ -894,19 +1267,20 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
|
||||
// Pinned ones only
|
||||
assertAllPinned(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
|
||||
mInternal.getShortcuts(getCallingPackage(), /* time =*/ 1000, CALLING_PACKAGE_2,
|
||||
assertAllNotKeyFieldsOnly(mInternal.getShortcuts(getCallingPackage(),
|
||||
/* time =*/ 1000, CALLING_PACKAGE_2,
|
||||
/* activity =*/ null,
|
||||
ShortcutQuery.FLAG_GET_PINNED,
|
||||
getCallingUserId()),
|
||||
getCallingUserId())),
|
||||
"s3"))));
|
||||
|
||||
// All packages.
|
||||
assertShortcutIds(
|
||||
assertShortcutIds(assertAllNotKeyFieldsOnly(
|
||||
mInternal.getShortcuts(getCallingPackage(),
|
||||
/* time =*/ 5000, /* package= */ null,
|
||||
/* activity =*/ null,
|
||||
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED,
|
||||
getCallingUserId()),
|
||||
getCallingUserId())),
|
||||
"s1", "s3");
|
||||
|
||||
// TODO More tests: pinned but dynamic, filter by activity
|
||||
@@ -968,8 +1342,9 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
|
||||
// Note we don't guarantee the orders.
|
||||
list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
|
||||
assertAllNotKeyFieldsOnly(
|
||||
mInternal.getShortcutInfo(getCallingPackage(), CALLING_PACKAGE_1,
|
||||
Arrays.asList("s2", "s1", "s3", null), getCallingUserId()))),
|
||||
Arrays.asList("s2", "s1", "s3", null), getCallingUserId())))),
|
||||
"s1", "s2");
|
||||
assertEquals("Title 1", findById(list, "s1").getTitle());
|
||||
assertEquals("Title 2", findById(list, "s2").getTitle());
|
||||
@@ -1036,19 +1411,19 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
setCaller(LAUNCHER_1);
|
||||
|
||||
// CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
|
||||
assertShortcutIds(assertAllPinned(
|
||||
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
|
||||
mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_1,
|
||||
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED, getCallingUserId())),
|
||||
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED, getCallingUserId()))),
|
||||
"s2");
|
||||
|
||||
assertShortcutIds(assertAllPinned(
|
||||
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
|
||||
mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_2,
|
||||
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED, getCallingUserId())),
|
||||
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED, getCallingUserId()))),
|
||||
"s3", "s4");
|
||||
|
||||
assertShortcutIds(assertAllPinned(
|
||||
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
|
||||
mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_3,
|
||||
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED, getCallingUserId()))
|
||||
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED, getCallingUserId())))
|
||||
/* none */);
|
||||
}
|
||||
|
||||
|
||||