Merge "Add UserHandle to ShortcutInfo, and simplify LauncherApps APIs." into nyc-dev

This commit is contained in:
Makoto Onuki
2016-04-11 16:45:08 +00:00
committed by Android (Google) Code Review
11 changed files with 396 additions and 167 deletions

View File

@@ -9498,9 +9498,10 @@ package android.content.pm {
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle);
method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo);
method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle);
method public int getShortcutIconResId(android.content.pm.ShortcutInfo);
method public int getShortcutIconResId(java.lang.String, java.lang.String, android.os.UserHandle);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
method public boolean hasShortcutHostPermission();
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
@@ -9512,6 +9513,7 @@ package android.content.pm {
method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
method public boolean startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
}
@@ -9533,6 +9535,7 @@ package android.content.pm {
method public void setChangedSince(long);
method public void setPackage(java.lang.String);
method public void setQueryFlags(int);
method public void setShortcutIds(java.util.List<java.lang.String>);
field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
field public static final int FLAG_GET_PINNED = 2; // 0x2
@@ -10046,6 +10049,7 @@ package android.content.pm {
method public java.lang.String getPackageName();
method public java.lang.String getText();
method public java.lang.String getTitle();
method public android.os.UserHandle getUserHandle();
method public int getWeight();
method public boolean hasIconFile();
method public boolean hasIconResource();

View File

@@ -9836,9 +9836,10 @@ package android.content.pm {
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle);
method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo);
method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle);
method public int getShortcutIconResId(android.content.pm.ShortcutInfo);
method public int getShortcutIconResId(java.lang.String, java.lang.String, android.os.UserHandle);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
method public boolean hasShortcutHostPermission();
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
@@ -9850,6 +9851,7 @@ package android.content.pm {
method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
method public boolean startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
}
@@ -9871,6 +9873,7 @@ package android.content.pm {
method public void setChangedSince(long);
method public void setPackage(java.lang.String);
method public void setQueryFlags(int);
method public void setShortcutIds(java.util.List<java.lang.String>);
field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
field public static final int FLAG_GET_PINNED = 2; // 0x2
@@ -10446,6 +10449,7 @@ package android.content.pm {
method public java.lang.String getPackageName();
method public java.lang.String getText();
method public java.lang.String getTitle();
method public android.os.UserHandle getUserHandle();
method public int getWeight();
method public boolean hasIconFile();
method public boolean hasIconResource();

View File

@@ -9507,9 +9507,10 @@ package android.content.pm {
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle);
method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo);
method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle);
method public int getShortcutIconResId(android.content.pm.ShortcutInfo);
method public int getShortcutIconResId(java.lang.String, java.lang.String, android.os.UserHandle);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
method public boolean hasShortcutHostPermission();
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
@@ -9521,6 +9522,7 @@ package android.content.pm {
method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
method public boolean startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
}
@@ -9542,6 +9544,7 @@ package android.content.pm {
method public void setChangedSince(long);
method public void setPackage(java.lang.String);
method public void setQueryFlags(int);
method public void setShortcutIds(java.util.List<java.lang.String>);
field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
field public static final int FLAG_GET_PINNED = 2; // 0x2
@@ -10056,6 +10059,7 @@ package android.content.pm {
method public java.lang.String getPackageName();
method public java.lang.String getText();
method public java.lang.String getTitle();
method public android.os.UserHandle getUserHandle();
method public int getWeight();
method public boolean hasIconFile();
method public boolean hasIconResource();

View File

@@ -47,17 +47,16 @@ interface ILauncherApps {
ApplicationInfo getApplicationInfo(String packageName, int flags, in UserHandle user);
ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName,
in ComponentName componentName, int flags, in UserHandle user);
ParceledListSlice getShortcutInfo(String callingPackage, String packageName, in List<String> ids,
in UserHandle user);
in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user);
void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
in UserHandle user);
boolean startShortcut(String callingPackage, String packageName, String id,
in Rect sourceBounds, in Bundle startActivityOptions, in UserHandle user);
in Rect sourceBounds, in Bundle startActivityOptions, int userId);
int getShortcutIconResId(String callingPackage, in ShortcutInfo shortcut, in UserHandle user);
ParcelFileDescriptor getShortcutIconFd(String callingPackage, in ShortcutInfo shortcut,
in UserHandle user);
int getShortcutIconResId(String callingPackage, String packageName, String id,
int userId);
ParcelFileDescriptor getShortcutIconFd(String callingPackage, String packageName, String id,
int userId);
boolean hasShortcutHostPermission(String callingPackage);
}

View File

@@ -204,6 +204,9 @@ public class LauncherApps {
@Nullable
String mPackage;
@Nullable
List<String> mShortcutIds;
@Nullable
ComponentName mActivity;
@@ -228,6 +231,14 @@ public class LauncherApps {
mPackage = packageName;
}
/**
* If non-null, return only the specified shortcuts by ID. When setting this field,
* a packange name must also be set with {@link #setPackage}.
*/
public void setShortcutIds(@Nullable List<String> shortcutIds) {
mShortcutIds = shortcutIds;
}
/**
* If non-null, returns only shortcuts associated with the activity.
*/
@@ -429,7 +440,8 @@ public class LauncherApps {
@NonNull UserHandle user) {
try {
return mService.getShortcuts(mContext.getPackageName(),
query.mChangedSince, query.mPackage, query.mActivity, query.mQueryFlags, user)
query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity,
query.mQueryFlags, user)
.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -437,29 +449,18 @@ public class LauncherApps {
}
/**
* Returns {@link ShortcutInfo}s with the given IDs from a package.
*
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
* #hasShortcutHostPermission()}.
*
* @param packageName The target package.
* @param ids IDs of the shortcuts to retrieve.
* @param user The UserHandle of the profile.
*
* @return list of {@link ShortcutInfo} associated with the package.
* @hide // No longer used. Use getShortcuts() instead. Kept for unit tests.
*/
@Nullable
public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName,
@NonNull List<String> ids, @NonNull UserHandle user) {
try {
return mService.getShortcutInfo(mContext.getPackageName(), packageName, ids, user)
.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
final ShortcutQuery q = new ShortcutQuery();
q.setPackage(packageName);
q.setShortcutIds(ids);
q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
return getShortcuts(q, user);
}
/**
* Pin shortcuts on a package.
*
@@ -490,11 +491,33 @@ public class LauncherApps {
* #hasShortcutHostPermission()}.
*
* @param shortcut The target shortcut.
*/
public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
return getShortcutIconResId(shortcut.getPackageName(), shortcut.getId(),
shortcut.getUserId());
}
/**
* Return the icon resource ID, if {@code shortcut} has one
* (i.e. when {@link ShortcutInfo#hasIconResource()} returns {@code true}).
*
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
* #hasShortcutHostPermission()}.
*
* @param packageName The target package name.
* @param shortcutId The ID of the shortcut to lad rom.
* @param user The UserHandle of the profile.
*/
public int getShortcutIconResId(@NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
@NonNull UserHandle user) {
return getShortcutIconResId(packageName, shortcutId, user.getIdentifier());
}
private int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
int userId) {
try {
return mService.getShortcutIconResId(mContext.getPackageName(), shortcut, user);
return mService.getShortcutIconResId(mContext.getPackageName(),
packageName, shortcutId, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -508,12 +531,34 @@ public class LauncherApps {
* #hasShortcutHostPermission()}.
*
* @param shortcut The target shortcut.
*/
public ParcelFileDescriptor getShortcutIconFd(
@NonNull ShortcutInfo shortcut) {
return getShortcutIconFd(shortcut.getPackageName(), shortcut.getId(),
shortcut.getUserId());
}
/**
* Return the icon as {@link ParcelFileDescriptor}, when it's stored as a file
* (i.e. when {@link ShortcutInfo#hasIconFile()} returns {@code true}).
*
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
* #hasShortcutHostPermission()}.
*
* @param packageName The target package name.
* @param shortcutId The ID of the shortcut to lad rom.
* @param user The UserHandle of the profile.
*/
public ParcelFileDescriptor getShortcutIconFd(
@NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
@NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) {
return getShortcutIconFd(packageName, shortcutId, user.getIdentifier());
}
private ParcelFileDescriptor getShortcutIconFd(
@NonNull String packageName, @NonNull String shortcutId, int userId) {
try {
return mService.getShortcutIconFd(mContext.getPackageName(), shortcut, user);
return mService.getShortcutIconFd(mContext.getPackageName(),
packageName, shortcutId, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -536,9 +581,35 @@ public class LauncherApps {
public boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId,
@Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
@NonNull UserHandle user) {
return startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions,
user.getIdentifier());
}
/**
* Launches a shortcut.
*
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
* #hasShortcutHostPermission()}.
*
* @param shortcut The target shortcut.
* @param sourceBounds The Rect containing the source bounds of the clicked icon.
* @param startActivityOptions Options to pass to startActivity.
* @return {@code false} when the shortcut is no longer valid (e.g. the creator application
* has been uninstalled). {@code true} when the shortcut is still valid.
*/
public boolean startShortcut(@NonNull ShortcutInfo shortcut,
@Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
return startShortcut(shortcut.getPackageName(), shortcut.getId(),
sourceBounds, startActivityOptions,
shortcut.getUserId());
}
private boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId,
@Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
int userId) {
try {
return mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
sourceBounds, startActivityOptions, user);
sourceBounds, startActivityOptions, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -18,6 +18,7 @@ package android.content.pm;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -153,7 +154,11 @@ public final class ShortcutInfo implements Parcelable {
@Nullable
private String mBitmapPath;
private final int mUserId;
private ShortcutInfo(Builder b) {
mUserId = b.mContext.getUserId();
mId = Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided");
// Note we can't do other null checks here because SM.updateShortcuts() takes partial
@@ -191,6 +196,7 @@ public final class ShortcutInfo implements Parcelable {
* Copy constructor.
*/
private ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags) {
mUserId = source.mUserId;
mId = source.mId;
mPackageName = source.mPackageName;
mFlags = source.mFlags;
@@ -238,6 +244,7 @@ public final class ShortcutInfo implements Parcelable {
* @hide
*/
public void copyNonNullFieldsFrom(ShortcutInfo source) {
Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
Preconditions.checkState(mId.equals(source.mId), "ID must match");
Preconditions.checkState(mPackageName.equals(source.mPackageName),
"Package name must match");
@@ -362,8 +369,8 @@ public final class ShortcutInfo implements Parcelable {
*
* <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)}.
* need to use {@link LauncherApps#getShortcutIconFd(ShortcutInfo)}
* and {@link LauncherApps#getShortcutIconResId(ShortcutInfo)}.
*/
@NonNull
public Builder setIcon(Icon icon) {
@@ -544,6 +551,18 @@ public final class ShortcutInfo implements Parcelable {
return mExtras;
}
/** @hide */
public int getUserId() {
return mUserId;
}
/**
* {@link UserHandle} on which the publisher created shortcuts.
*/
public UserHandle getUserHandle() {
return UserHandle.of(mUserId);
}
/**
* Last time when any of the fields was updated.
*/
@@ -590,7 +609,7 @@ public final class ShortcutInfo implements Parcelable {
/**
* Return whether a shortcut's icon is a resource in the owning package.
*
* @see LauncherApps#getShortcutIconResId(ShortcutInfo, UserHandle)
* @see LauncherApps#getShortcutIconResId(ShortcutInfo)
*/
public boolean hasIconResource() {
return hasFlags(FLAG_HAS_ICON_RES);
@@ -599,7 +618,7 @@ public final class ShortcutInfo implements Parcelable {
/**
* Return whether a shortcut's icon is stored as a file.
*
* @see LauncherApps#getShortcutIconFd(ShortcutInfo, UserHandle)
* @see LauncherApps#getShortcutIconFd(ShortcutInfo)
*/
public boolean hasIconFile() {
return hasFlags(FLAG_HAS_ICON_FILE);
@@ -661,6 +680,7 @@ public final class ShortcutInfo implements Parcelable {
private ShortcutInfo(Parcel source) {
final ClassLoader cl = getClass().getClassLoader();
mUserId = source.readInt();
mId = source.readString();
mPackageName = source.readString();
mActivityComponent = source.readParcelable(cl);
@@ -679,6 +699,7 @@ public final class ShortcutInfo implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mUserId);
dest.writeString(mId);
dest.writeString(mPackageName);
dest.writeParcelable(mActivityComponent, flags);
@@ -784,11 +805,13 @@ public final class ShortcutInfo implements Parcelable {
}
/** @hide */
public ShortcutInfo(String id, String packageName, ComponentName activityComponent,
public ShortcutInfo(
@UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
Icon icon, String title, String text, Intent intent,
PersistableBundle intentPersistableExtras,
int weight, PersistableBundle extras, long lastChangedTimestamp,
int flags, int iconResId, String bitmapPath) {
mUserId = userId;
mId = id;
mPackageName = packageName;
mActivityComponent = activityComponent;

View File

@@ -42,15 +42,10 @@ public abstract class ShortcutServiceInternal {
public abstract List<ShortcutInfo>
getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable ComponentName componentName,
@ShortcutQuery.QueryFlags int flags,
@Nullable String packageName, @Nullable List<String> shortcutIds,
@Nullable ComponentName componentName, @ShortcutQuery.QueryFlags int flags,
int userId);
public abstract List<ShortcutInfo>
getShortcutInfo(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @Nullable List<String> ids, int userId);
public abstract boolean
isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String id, int userId);
@@ -65,11 +60,11 @@ public abstract class ShortcutServiceInternal {
public abstract void addListener(@NonNull ShortcutChangeListener listener);
public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId);
@NonNull String packageName, @NonNull String shortcutId, int userId);
public abstract ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
@NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId);
@NonNull String packageName, @NonNull String shortcutId, int userId);
public abstract boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage);

View File

@@ -213,8 +213,11 @@ public class LauncherAppsService extends SystemService {
* Checks if the caller is in the same group as the userToCheck.
*/
private void ensureInUserProfiles(UserHandle userToCheck, String message) {
ensureInUserProfiles(userToCheck.getIdentifier(), message);
}
private void ensureInUserProfiles(int targetUserId, String message) {
final int callingUserId = injectCallingUserId();
final int targetUserId = userToCheck.getIdentifier();
if (targetUserId == callingUserId) return;
@@ -253,9 +256,13 @@ public class LauncherAppsService extends SystemService {
* Checks if the user is enabled.
*/
private boolean isUserEnabled(UserHandle user) {
return isUserEnabled(user.getIdentifier());
}
private boolean isUserEnabled(int userId) {
long ident = injectClearCallingIdentity();
try {
UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier());
UserInfo targetUserInfo = mUm.getUserInfo(userId);
return targetUserInfo != null && targetUserInfo.isEnabled();
} finally {
injectRestoreCallingIdentity(ident);
@@ -346,8 +353,12 @@ public class LauncherAppsService extends SystemService {
}
private void ensureShortcutPermission(@NonNull String callingPackage, UserHandle user) {
ensureShortcutPermission(callingPackage, user.getIdentifier());
}
private void ensureShortcutPermission(@NonNull String callingPackage, int userId) {
verifyCallingPackage(callingPackage);
ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId);
if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
callingPackage)) {
@@ -357,31 +368,23 @@ public class LauncherAppsService extends SystemService {
@Override
public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
String packageName, ComponentName componentName, int flags, UserHandle user) {
String packageName, List shortcutIds, ComponentName componentName, int flags,
UserHandle user) {
ensureShortcutPermission(callingPackage, user);
if (!isUserEnabled(user)) {
return new ParceledListSlice<>(new ArrayList(0));
}
if (shortcutIds != null && packageName == null) {
throw new IllegalArgumentException(
"To query by shortcut ID, package name must also be set");
}
return new ParceledListSlice<>(
mShortcutServiceInternal.getShortcuts(getCallingUserId(),
callingPackage, changedSince, packageName,
callingPackage, changedSince, packageName, shortcutIds,
componentName, flags, user.getIdentifier()));
}
@Override
public ParceledListSlice getShortcutInfo(String callingPackage, String packageName,
List<String> ids, UserHandle user) {
ensureShortcutPermission(callingPackage, user);
if (!isUserEnabled(user)) {
return new ParceledListSlice<>(new ArrayList(0));
}
return new ParceledListSlice<>(
mShortcutServiceInternal.getShortcutInfo(getCallingUserId(),
callingPackage, packageName, ids, user.getIdentifier()));
}
@Override
public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
UserHandle user) {
@@ -396,27 +399,27 @@ public class LauncherAppsService extends SystemService {
}
@Override
public int getShortcutIconResId(String callingPackage, ShortcutInfo shortcut,
UserHandle user) {
ensureShortcutPermission(callingPackage, user);
if (!isUserEnabled(user)) {
public int getShortcutIconResId(String callingPackage, String packageName, String id,
int userId) {
ensureShortcutPermission(callingPackage, userId);
if (!isUserEnabled(userId)) {
return 0;
}
return mShortcutServiceInternal.getShortcutIconResId(getCallingUserId(),
callingPackage, shortcut, user.getIdentifier());
callingPackage, packageName, id, userId);
}
@Override
public ParcelFileDescriptor getShortcutIconFd(String callingPackage, ShortcutInfo shortcut,
UserHandle user) {
ensureShortcutPermission(callingPackage, user);
if (!isUserEnabled(user)) {
public ParcelFileDescriptor getShortcutIconFd(String callingPackage,
String packageName, String id, int userId) {
ensureShortcutPermission(callingPackage, userId);
if (!isUserEnabled(userId)) {
return null;
}
return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
callingPackage, shortcut, user.getIdentifier());
callingPackage, packageName, id, userId);
}
@Override
@@ -428,23 +431,23 @@ public class LauncherAppsService extends SystemService {
@Override
public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
Rect sourceBounds, Bundle startActivityOptions, UserHandle user) {
Rect sourceBounds, Bundle startActivityOptions, int userId) {
verifyCallingPackage(callingPackage);
ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId);
if (!isUserEnabled(user)) {
if (!isUserEnabled(userId)) {
throw new IllegalStateException("Cannot start a shortcut for disabled profile "
+ user);
+ userId);
}
// Even without the permission, pinned shortcuts are always launchable.
if (!mShortcutServiceInternal.isPinnedByCaller(getCallingUserId(),
callingPackage, packageName, shortcutId, user.getIdentifier())) {
ensureShortcutPermission(callingPackage, user);
callingPackage, packageName, shortcutId, userId)) {
ensureShortcutPermission(callingPackage, userId);
}
final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(),
callingPackage, packageName, shortcutId, user.getIdentifier());
callingPackage, packageName, shortcutId, userId);
if (intent == null) {
return false;
}
@@ -455,7 +458,7 @@ public class LauncherAppsService extends SystemService {
final long ident = Binder.clearCallingIdentity();
try {
mContext.startActivityAsUser(intent, startActivityOptions, user);
mContext.startActivityAsUser(intent, startActivityOptions, UserHandle.of(userId));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -768,7 +771,8 @@ public class LauncherAppsService extends SystemService {
final List<ShortcutInfo> list =
mShortcutServiceInternal.getShortcuts(launcherUserId,
cookie.packageName,
/* changedSince= */ 0, packageName, /* component= */ null,
/* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
/* component= */ null,
ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
| ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC

View File

@@ -17,6 +17,7 @@ package com.android.server.pm;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
@@ -522,7 +523,7 @@ class ShortcutPackage extends ShortcutPackageItem {
ret.getPackageInfo().loadFromXml(parser, fromBackup);
continue;
case TAG_SHORTCUT:
final ShortcutInfo si = parseShortcut(parser, packageName);
final ShortcutInfo si = parseShortcut(parser, packageName, ownerUserId);
// Don't use addShortcut(), we don't need to save the icon.
ret.mShortcuts.put(si.getId(), si);
@@ -534,8 +535,8 @@ class ShortcutPackage extends ShortcutPackageItem {
return ret;
}
private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName)
throws IOException, XmlPullParserException {
private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName,
@UserIdInt int userId) throws IOException, XmlPullParserException {
String id;
ComponentName activityComponent;
// Icon icon;
@@ -586,7 +587,7 @@ class ShortcutPackage extends ShortcutPackageItem {
throw ShortcutService.throwForInvalidTag(depth, tag);
}
return new ShortcutInfo(
id, packageName, activityComponent, /* icon =*/ null, title, text, intent,
userId, id, packageName, activityComponent, /* icon =*/ null, title, text, intent,
intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
iconRes, bitmapPath);
}

View File

@@ -968,7 +968,8 @@ public class ShortcutService extends IShortcutService.Stub {
return; // has no icon
}
Bitmap bitmap = null;
Bitmap bitmap;
Bitmap bitmapToRecycle = null;
try {
switch (icon.getType()) {
case Icon.TYPE_RESOURCE: {
@@ -979,7 +980,7 @@ public class ShortcutService extends IShortcutService.Stub {
return;
}
case Icon.TYPE_BITMAP: {
bitmap = icon.getBitmap();
bitmap = icon.getBitmap(); // Don't recycle in this case.
break;
}
case Icon.TYPE_URI: {
@@ -987,7 +988,8 @@ public class ShortcutService extends IShortcutService.Stub {
try (InputStream is = mContext.getContentResolver().openInputStream(uri)) {
bitmap = BitmapFactory.decodeStream(is);
bitmapToRecycle = BitmapFactory.decodeStream(is);
bitmap = bitmapToRecycle;
} catch (IOException e) {
Slog.e(TAG, "Unable to load icon from " + uri);
@@ -1011,8 +1013,14 @@ public class ShortcutService extends IShortcutService.Stub {
try {
path = out.getFile();
shrinkBitmap(bitmap, mMaxIconDimension)
.compress(mIconPersistFormat, mIconPersistQuality, out);
Bitmap shrunk = shrinkBitmap(bitmap, mMaxIconDimension);
try {
shrunk.compress(mIconPersistFormat, mIconPersistQuality, out);
} finally {
if (bitmap != shrunk) {
shrunk.recycle();
}
}
shortcut.setBitmapPath(out.getFile().getAbsolutePath());
shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE);
@@ -1027,8 +1035,8 @@ public class ShortcutService extends IShortcutService.Stub {
}
}
} finally {
if (bitmap != null) {
bitmap.recycle();
if (bitmapToRecycle != null) {
bitmapToRecycle.recycle();
}
// Once saved, we won't use the original icon information, so null it out.
shortcut.clearIcon();
@@ -1076,8 +1084,6 @@ public class ShortcutService extends IShortcutService.Stub {
c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
in.recycle();
return scaledBitmap;
}
@@ -1580,13 +1586,17 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public List<ShortcutInfo> getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable ComponentName componentName,
@Nullable String packageName, @Nullable List<String> shortcutIds,
@Nullable ComponentName componentName,
int queryFlags, int userId) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
final int cloneFlag =
((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0)
? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER
: ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
if (packageName == null) {
shortcutIds = null; // LauncherAppsService already threw for it though.
}
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
@@ -1594,14 +1604,14 @@ public class ShortcutService extends IShortcutService.Stub {
if (packageName != null) {
getShortcutsInnerLocked(launcherUserId,
callingPackage, packageName, changedSince,
callingPackage, packageName, shortcutIds, changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
} else {
final ArrayMap<String, ShortcutPackage> packages =
getUserShortcutsLocked(userId).getAllPackages();
for (int i = packages.size() - 1; i >= 0; i--) {
getShortcutsInnerLocked(launcherUserId,
callingPackage, packages.keyAt(i), changedSince,
callingPackage, packages.keyAt(i), shortcutIds, changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
}
}
@@ -1610,14 +1620,20 @@ public class ShortcutService extends IShortcutService.Stub {
}
private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
@Nullable String packageName,long changedSince,
@Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
@Nullable ComponentName componentName, int queryFlags,
int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
final ArraySet<String> ids = shortcutIds == null ? null
: new ArraySet<>(shortcutIds);
getPackageShortcutsLocked(packageName, userId).findAll(ShortcutService.this, ret,
(ShortcutInfo si) -> {
if (si.getLastChangedTimestamp() < changedSince) {
return false;
}
if (ids != null && !ids.contains(si.getId())) {
return false;
}
if (componentName != null
&& !componentName.equals(si.getActivityComponent())) {
return false;
@@ -1632,27 +1648,6 @@ public class ShortcutService extends IShortcutService.Stub {
}, cloneFlag, callingPackage, launcherUserId);
}
@Override
public List<ShortcutInfo> getShortcutInfo(int launcherUserId,
@NonNull String callingPackage,
@NonNull String packageName, @Nullable List<String> ids, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
Preconditions.checkStringNotEmpty(packageName, "packageName");
final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
final ArraySet<String> idSet = new ArraySet<>(ids);
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave(ShortcutService.this);
getPackageShortcutsLocked(packageName, userId).findAll(
ShortcutService.this, ret,
(ShortcutInfo si) -> idSet.contains(si.getId()),
ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage, launcherUserId);
}
return ret;
}
@Override
public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
@@ -1733,17 +1728,18 @@ public class ShortcutService extends IShortcutService.Stub {
}
@Override
public int getShortcutIconResId(int launcherUserId,
@NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId) {
Preconditions.checkNotNull(shortcut, "shortcut");
public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
Preconditions.checkNotNull(callingPackage, "callingPackage");
Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(shortcutId, "shortcutId");
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave(ShortcutService.this);
final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
shortcut.getPackageName(), userId).findShortcutById(shortcut.getId());
packageName, userId).findShortcutById(shortcutId);
return (shortcutInfo != null && shortcutInfo.hasIconResource())
? shortcutInfo.getIconResourceId() : 0;
}
@@ -1751,16 +1747,18 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
@NonNull String callingPackage,
@NonNull ShortcutInfo shortcutIn, int userId) {
Preconditions.checkNotNull(shortcutIn, "shortcut");
@NonNull String callingPackage, @NonNull String packageName,
@NonNull String shortcutId, int userId) {
Preconditions.checkNotNull(callingPackage, "callingPackage");
Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(shortcutId, "shortcutId");
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave(ShortcutService.this);
final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId());
packageName, userId).findShortcutById(shortcutId);
if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
return null;
}

View File

@@ -172,6 +172,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
public String getPackageName() {
return mInjectedClientPackage;
}
@Override
public int getUserId() {
return getCallingUserId();
}
}
/** Context used in the service side */
@@ -190,6 +195,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options,
UserHandle userId) {
}
@Override
public int getUserId() {
return UserHandle.USER_SYSTEM;
}
}
/** ShortcutService with injection override methods. */
@@ -1152,9 +1162,25 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
return intentCaptor.getValue();
}
private Intent launchShortcutAndGetIntent_withShortcutInfo(
@NonNull String packageName, @NonNull String shortcutId, int userId) {
reset(mServiceContext);
assertTrue(mLauncherApps.startShortcut(
getShortcutInfoAsLauncher(packageName, shortcutId, userId), null, null));
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mServiceContext).startActivityAsUser(
intentCaptor.capture(),
any(Bundle.class),
eq(UserHandle.of(userId)));
return intentCaptor.getValue();
}
private void assertShortcutLaunchable(@NonNull String packageName, @NonNull String shortcutId,
int userId) {
assertNotNull(launchShortcutAndGetIntent(packageName, shortcutId, userId));
assertNotNull(launchShortcutAndGetIntent_withShortcutInfo(packageName, shortcutId, userId));
}
private void assertShortcutNotLaunchable(@NonNull String packageName,
@@ -1193,6 +1219,14 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED);
}
private ShortcutInfo getShortcutInfoAsLauncher(String packageName, String shortcutId,
int userId) {
final List<ShortcutInfo> infoList =
mLauncherApps.getShortcutInfo(packageName, list(shortcutId),
UserHandle.of(userId));
assertEquals("No shortcutInfo found (or too many of them)", 1, infoList.size());
return infoList.get(0);
}
private Intent genPackageDeleteIntent(String pakcageName, int userId) {
Intent i = new Intent(Intent.ACTION_PACKAGE_REMOVED);
@@ -1728,6 +1762,16 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
"res64x64",
"none");
// Different profile. Note the names and the contents don't match.
setCaller(CALLING_PACKAGE_1, USER_P0);
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcutWithIcon("res32x32", res512x512),
makeShortcutWithIcon("bmp32x32", bmp512x512)
)));
assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
"res32x32",
"bmp32x32");
// Re-initialize and load from the files.
mService.saveDirtyInfo();
initService();
@@ -1737,61 +1781,90 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
setCaller(LAUNCHER_1);
// Check hasIconResource()/hasIconFile().
assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
CALLING_PACKAGE_1, list("res32x32"),
getCallingUser())), "res32x32");
assertShortcutIds(assertAllHaveIconResId(
list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_0))),
"res32x32");
assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
CALLING_PACKAGE_1, list("res64x64"), getCallingUser())),
assertShortcutIds(assertAllHaveIconResId(
list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res64x64", USER_0))),
"res64x64");
assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
CALLING_PACKAGE_1, list("bmp32x32"), getCallingUser())),
assertShortcutIds(assertAllHaveIconFile(
list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0))),
"bmp32x32");
assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
CALLING_PACKAGE_1, list("bmp64x64"), getCallingUser())),
assertShortcutIds(assertAllHaveIconFile(
list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0))),
"bmp64x64");
assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
CALLING_PACKAGE_1, list("bmp512x512"), getCallingUser())),
assertShortcutIds(assertAllHaveIconFile(
list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0))),
"bmp512x512");
assertShortcutIds(assertAllHaveIconResId(
list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0))),
"res32x32");
assertShortcutIds(assertAllHaveIconFile(
list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_P0))),
"bmp32x32");
// Check
assertEquals(
R.drawable.black_32x32,
mLauncherApps.getShortcutIconResId(
makePackageShortcut(CALLING_PACKAGE_1, "res32x32"), getCallingUser()));
getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_0)));
assertEquals(
R.drawable.black_64x64,
mLauncherApps.getShortcutIconResId(
makePackageShortcut(CALLING_PACKAGE_1, "res64x64"), getCallingUser()));
getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res64x64", USER_0)));
assertEquals(
0, // because it's not a resource
mLauncherApps.getShortcutIconResId(
makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0)));
assertEquals(
0, // because it's not a resource
mLauncherApps.getShortcutIconResId(
makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0)));
assertEquals(
0, // because it's not a resource
mLauncherApps.getShortcutIconResId(
makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0)));
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0)));
assertBitmapSize(32, 32, bmp);
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0)));
assertBitmapSize(64, 64, bmp);
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0)));
assertBitmapSize(128, 128, bmp);
assertEquals(
R.drawable.black_512x512,
mLauncherApps.getShortcutIconResId(
getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0)));
// Should be 512x512, so shrunk.
bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_P0)));
assertBitmapSize(128, 128, bmp);
// Also check the overload APIs too.
assertEquals(
R.drawable.black_32x32,
mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res32x32", HANDLE_USER_0));
assertEquals(
R.drawable.black_64x64,
mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res64x64", HANDLE_USER_0));
assertEquals(
R.drawable.black_512x512,
mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res32x32", HANDLE_USER_P0));
bmp = pfdToBitmap(
mLauncherApps.getShortcutIconFd(CALLING_PACKAGE_1, "bmp32x32", HANDLE_USER_P0));
assertBitmapSize(128, 128, bmp);
// TODO Test the content URI case too.
@@ -2034,9 +2107,16 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
private static ShortcutQuery buildQuery(long changedSince,
String packageName, ComponentName componentName,
/* @ShortcutQuery.QueryFlags */ int flags) {
return buildQuery(changedSince, packageName, null, componentName, flags);
}
private static ShortcutQuery buildQuery(long changedSince,
String packageName, List<String> shortcutIds, ComponentName componentName,
/* @ShortcutQuery.QueryFlags */ int flags) {
final ShortcutQuery q = new ShortcutQuery();
q.setChangedSince(changedSince);
q.setPackage(packageName);
q.setShortcutIds(shortcutIds);
q.setActivity(componentName);
q.setQueryFlags(flags);
return q;
@@ -2110,6 +2190,36 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
getCallingUser())),
"s2", "s3"))));
// With ID.
assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 1000, CALLING_PACKAGE_2, list("s3"),
/* activity =*/ null,
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
getCallingUser())),
"s3"))));
assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 1000, CALLING_PACKAGE_2, list("s3", "s2", "ss"),
/* activity =*/ null,
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
getCallingUser())),
"s2", "s3"))));
assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 1000, CALLING_PACKAGE_2, list("s3x", "s2x"),
/* activity =*/ null,
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
getCallingUser()))
/* empty */))));
assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 1000, CALLING_PACKAGE_2, list(),
/* activity =*/ null,
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
getCallingUser()))
/* empty */))));
// Pin some shortcuts.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
list("s3", "s4"), getCallingUser());
@@ -2132,6 +2242,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
getCallingUser())),
"s1", "s3");
TestUtils.assertExpectException(
IllegalArgumentException.class, "package name must also be set", () -> {
mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 0, /* package= */ null, list("id"),
/* activity =*/ null, /* flags */ 0), getCallingUser());
});
// TODO More tests: pinned but dynamic, filter by activity
}
@@ -5063,12 +5180,15 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
}
public void testShortcutInfoParcel() {
ShortcutInfo si = parceled(new ShortcutInfo.Builder(getTestContext())
setCaller(CALLING_PACKAGE_1, USER_10);
ShortcutInfo si = parceled(new ShortcutInfo.Builder(mClientContext)
.setId("id")
.setTitle("title")
.setIntent(makeIntent("action", ShortcutActivity.class))
.build());
assertEquals(getTestContext().getPackageName(), si.getPackageName());
assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals(USER_10, si.getUserId());
assertEquals(HANDLE_USER_10, si.getUserHandle());
assertEquals("id", si.getId());
assertEquals("title", si.getTitle());
assertEquals("action", si.getIntent().getAction());
@@ -5109,9 +5229,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
}
public void testShortcutInfoClone() {
setCaller(CALLING_PACKAGE_1, USER_11);
PersistableBundle pb = new PersistableBundle();
pb.putInt("k", 1);
ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
.setId("id")
.setActivityComponent(new ComponentName("a", "b"))
.setIcon(Icon.createWithContentUri("content://a.b.c/"))
@@ -5127,7 +5249,9 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
assertEquals(getTestContext().getPackageName(), si.getPackageName());
assertEquals(USER_11, si.getUserId());
assertEquals(HANDLE_USER_11, si.getUserHandle());
assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals("content://a.b.c/", si.getIcon().getUriString());
@@ -5144,7 +5268,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
assertEquals(getTestContext().getPackageName(), si.getPackageName());
assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals(null, si.getIcon());
@@ -5161,7 +5285,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
assertEquals(getTestContext().getPackageName(), si.getPackageName());
assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
assertEquals(null, si.getIcon());
@@ -5177,7 +5301,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
assertEquals(getTestContext().getPackageName(), si.getPackageName());
assertEquals(mClientContext.getPackageName(), si.getPackageName());
assertEquals("id", si.getId());
assertEquals(null, si.getActivityComponent());
assertEquals(null, si.getIcon());
@@ -5267,7 +5391,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
}
public void testShortcutInfoSaveAndLoad() throws InterruptedException {
setCaller(CALLING_PACKAGE_1, USER_0);
setCaller(CALLING_PACKAGE_1, USER_10);
final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.black_32x32));
@@ -5293,11 +5417,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
// Save and load.
mService.saveDirtyInfo();
initService();
mService.handleUnlockUser(USER_0);
mService.handleUnlockUser(USER_10);
ShortcutInfo si;
si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10);
assertEquals(USER_10, si.getUserId());
assertEquals(HANDLE_USER_10, si.getUserHandle());
assertEquals(CALLING_PACKAGE_1, si.getPackageName());
assertEquals("id", si.getId());
assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName());