Merge "Adds unit tests for ShortcutChangeCallback APIs" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
082d0c47b7
@@ -507,7 +507,8 @@ public class LauncherApps {
|
||||
/**
|
||||
* Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to
|
||||
* register this callback, have been added or updated.
|
||||
* @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery)
|
||||
* @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery,
|
||||
* Executor)
|
||||
*
|
||||
* <p>Only the applications that are allowed to access the shortcut information,
|
||||
* as defined in {@link #hasShortcutHostPermission()}, will receive it.
|
||||
@@ -525,7 +526,8 @@ public class LauncherApps {
|
||||
/**
|
||||
* Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to
|
||||
* register this callback, have been removed.
|
||||
* @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery)
|
||||
* @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery,
|
||||
* Executor)
|
||||
*
|
||||
* <p>Only the applications that are allowed to access the shortcut information,
|
||||
* as defined in {@link #hasShortcutHostPermission()}, will receive it.
|
||||
|
||||
@@ -746,9 +746,8 @@ public class LauncherAppsService extends SystemService {
|
||||
}
|
||||
|
||||
UserHandle user = UserHandle.of(injectCallingUserId());
|
||||
if (mContext.checkCallingOrSelfPermission(
|
||||
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
if (injectHasInteractAcrossUsersFullPermission(injectBinderCallingPid(),
|
||||
injectBinderCallingUid())) {
|
||||
user = null;
|
||||
}
|
||||
|
||||
@@ -1053,29 +1052,6 @@ public class LauncherAppsService extends SystemService {
|
||||
}
|
||||
|
||||
public static class ShortcutChangeHandler implements LauncherApps.ShortcutChangeCallback {
|
||||
|
||||
static class QueryInfo {
|
||||
final long mChangedSince;
|
||||
final String mPackage;
|
||||
final List<String> mShortcutIds;
|
||||
final List<LocusId> mLocusIds;
|
||||
final ComponentName mActivity;
|
||||
final int mQueryFlags;
|
||||
final UserHandle mCallbackUser;
|
||||
|
||||
QueryInfo(long changedSince, String packageName, List<String> shortcutIds,
|
||||
List<LocusId> locusIds, ComponentName activity, int flags,
|
||||
UserHandle callbackUser) {
|
||||
mChangedSince = changedSince;
|
||||
mPackage = packageName;
|
||||
mShortcutIds = shortcutIds;
|
||||
mLocusIds = locusIds;
|
||||
mActivity = activity;
|
||||
mQueryFlags = flags;
|
||||
mCallbackUser = callbackUser;
|
||||
}
|
||||
}
|
||||
|
||||
private final UserManagerInternal mUserManagerInternal;
|
||||
|
||||
ShortcutChangeHandler(UserManagerInternal userManager) {
|
||||
@@ -1088,9 +1064,7 @@ public class LauncherAppsService extends SystemService {
|
||||
public synchronized void addShortcutChangeCallback(IShortcutChangeCallback callback,
|
||||
ShortcutQueryWrapper query, UserHandle user) {
|
||||
mCallbacks.unregister(callback);
|
||||
mCallbacks.register(callback, new QueryInfo(query.getChangedSince(),
|
||||
query.getPackage(), query.getShortcutIds(), query.getLocusIds(),
|
||||
query.getActivity(), query.getQueryFlags(), user));
|
||||
mCallbacks.register(callback, new Pair<>(query, user));
|
||||
}
|
||||
|
||||
public synchronized void removeShortcutChangeCallback(
|
||||
@@ -1116,16 +1090,19 @@ public class LauncherAppsService extends SystemService {
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
final IShortcutChangeCallback callback = mCallbacks.getBroadcastItem(i);
|
||||
final QueryInfo query = (QueryInfo) mCallbacks.getBroadcastCookie(i);
|
||||
final Pair<ShortcutQueryWrapper, UserHandle> cookie =
|
||||
(Pair<ShortcutQueryWrapper, UserHandle>)
|
||||
mCallbacks.getBroadcastCookie(i);
|
||||
|
||||
if (query.mCallbackUser != null && !hasUserAccess(query.mCallbackUser, user)) {
|
||||
final UserHandle callbackUser = cookie.second;
|
||||
if (callbackUser != null && !hasUserAccess(callbackUser, user)) {
|
||||
// Callback owner does not have access to the shortcuts' user.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filter the list by query, if any matches exists, send via callback.
|
||||
List<ShortcutInfo> matchedList =
|
||||
filterShortcutsByQuery(packageName, shortcuts, query);
|
||||
List<ShortcutInfo> matchedList = filterShortcutsByQuery(packageName, shortcuts,
|
||||
cookie.first, shortcutsRemoved);
|
||||
if (!CollectionUtils.isEmpty(matchedList)) {
|
||||
try {
|
||||
if (shortcutsRemoved) {
|
||||
@@ -1143,21 +1120,25 @@ public class LauncherAppsService extends SystemService {
|
||||
}
|
||||
|
||||
public static List<ShortcutInfo> filterShortcutsByQuery(String packageName,
|
||||
List<ShortcutInfo> shortcuts, QueryInfo query) {
|
||||
if (query.mPackage != null && query.mPackage != packageName) {
|
||||
List<ShortcutInfo> shortcuts, ShortcutQueryWrapper query,
|
||||
boolean shortcutsRemoved) {
|
||||
final long changedSince = query.getChangedSince();
|
||||
final String queryPackage = query.getPackage();
|
||||
final List<String> shortcutIds = query.getShortcutIds();
|
||||
final List<LocusId> locusIds = query.getLocusIds();
|
||||
final ComponentName activity = query.getActivity();
|
||||
final int flags = query.getQueryFlags();
|
||||
|
||||
if (queryPackage != null && !queryPackage.equals(packageName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<ShortcutInfo> matches = new ArrayList<>();
|
||||
|
||||
final boolean matchDynamic =
|
||||
(query.mQueryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) != 0;
|
||||
final boolean matchPinned =
|
||||
(query.mQueryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0;
|
||||
final boolean matchManifest =
|
||||
(query.mQueryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0;
|
||||
final boolean matchCached =
|
||||
(query.mQueryFlags & ShortcutQuery.FLAG_MATCH_CACHED) != 0;
|
||||
final boolean matchDynamic = (flags & ShortcutQuery.FLAG_MATCH_DYNAMIC) != 0;
|
||||
final boolean matchPinned = (flags & ShortcutQuery.FLAG_MATCH_PINNED) != 0;
|
||||
final boolean matchManifest = (flags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0;
|
||||
final boolean matchCached = (flags & ShortcutQuery.FLAG_MATCH_CACHED) != 0;
|
||||
final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0)
|
||||
| (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
|
||||
| (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
|
||||
@@ -1166,24 +1147,19 @@ public class LauncherAppsService extends SystemService {
|
||||
for (int i = 0; i < shortcuts.size(); i++) {
|
||||
final ShortcutInfo si = shortcuts.get(i);
|
||||
|
||||
if (query.mActivity != null && !query.mActivity.equals(si.getActivity())) {
|
||||
if (activity != null && !activity.equals(si.getActivity())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (query.mChangedSince != 0
|
||||
&& query.mChangedSince > si.getLastChangedTimestamp()) {
|
||||
if (changedSince != 0 && changedSince > si.getLastChangedTimestamp()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (query.mShortcutIds != null && !query.mShortcutIds.contains(si.getId())) {
|
||||
if (shortcutIds != null && !shortcutIds.contains(si.getId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (query.mLocusIds != null && !query.mLocusIds.contains(si.getLocusId())) {
|
||||
if (locusIds != null && !locusIds.contains(si.getLocusId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((shortcutFlags & si.getFlags()) != 0) {
|
||||
if (shortcutsRemoved || (shortcutFlags & si.getFlags()) != 0) {
|
||||
matches.add(si);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
if (shortcut != null) {
|
||||
mShortcutUser.mService.removeIconLocked(shortcut);
|
||||
shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
|
||||
| ShortcutInfo.FLAG_MANIFEST);
|
||||
| ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED);
|
||||
}
|
||||
return shortcut;
|
||||
}
|
||||
@@ -281,8 +281,10 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
* invisible.
|
||||
*
|
||||
* It checks the max number of dynamic shortcuts.
|
||||
*
|
||||
* @return True if it replaced an existing shortcut, False otherwise.
|
||||
*/
|
||||
public void addOrReplaceDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
|
||||
public boolean addOrReplaceDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
|
||||
|
||||
Preconditions.checkArgument(newShortcut.isEnabled(),
|
||||
"add/setDynamicShortcuts() cannot publish disabled shortcuts");
|
||||
@@ -291,38 +293,59 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
|
||||
final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
|
||||
|
||||
final boolean replaced;
|
||||
|
||||
final boolean wasPinned;
|
||||
final boolean wasCached;
|
||||
|
||||
if (oldShortcut == null) {
|
||||
replaced = false;
|
||||
wasPinned = false;
|
||||
wasCached = false;
|
||||
} else {
|
||||
// It's an update case.
|
||||
// Make sure the target is updatable. (i.e. should be mutable.)
|
||||
oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false);
|
||||
replaced = true;
|
||||
|
||||
wasPinned = oldShortcut.isPinned();
|
||||
wasCached = oldShortcut.isCached();
|
||||
}
|
||||
|
||||
// If it was originally pinned, the new one should be pinned too.
|
||||
if (wasPinned) {
|
||||
newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
|
||||
}
|
||||
if (wasCached) {
|
||||
newShortcut.addFlags(ShortcutInfo.FLAG_CACHED);
|
||||
}
|
||||
|
||||
forceReplaceShortcutInner(newShortcut);
|
||||
return replaced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a shortcut. If the max number of dynamic shortcuts is already reached, remove the
|
||||
* shortcut with the lowest rank before adding the new shortcut.
|
||||
*
|
||||
* Any shortcut that gets altered (removed or changed) as a result of this push operation will
|
||||
* be included and returned in changedShortcuts.
|
||||
*
|
||||
* @return True if a shortcut had to be removed to complete this operation, False otherwise.
|
||||
*/
|
||||
public boolean pushDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
|
||||
public boolean pushDynamicShortcut(@NonNull ShortcutInfo newShortcut,
|
||||
@NonNull List<ShortcutInfo> changedShortcuts) {
|
||||
Preconditions.checkArgument(newShortcut.isEnabled(),
|
||||
"pushDynamicShortcuts() cannot publish disabled shortcuts");
|
||||
|
||||
newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
|
||||
|
||||
changedShortcuts.clear();
|
||||
final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
|
||||
boolean wasPinned = false;
|
||||
boolean wasCached = false;
|
||||
|
||||
boolean deleted = false;
|
||||
|
||||
if (oldShortcut == null) {
|
||||
final ShortcutService service = mShortcutUser.mService;
|
||||
@@ -343,10 +366,11 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
// All shortcuts are manifest shortcuts and cannot be removed.
|
||||
Slog.e(TAG, "Failed to remove manifest shortcut while pushing dynamic shortcut "
|
||||
+ newShortcut.getId());
|
||||
return false;
|
||||
return true; // poppedShortcuts is empty which indicates a failure.
|
||||
}
|
||||
|
||||
deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true);
|
||||
changedShortcuts.add(shortcut);
|
||||
deleted = deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true) != null;
|
||||
}
|
||||
} else {
|
||||
// It's an update case.
|
||||
@@ -354,15 +378,19 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false);
|
||||
|
||||
wasPinned = oldShortcut.isPinned();
|
||||
wasCached = oldShortcut.isCached();
|
||||
}
|
||||
|
||||
// If it was originally pinned, the new one should be pinned too.
|
||||
// If it was originally pinned or cached, the new one should be pinned or cached too.
|
||||
if (wasPinned) {
|
||||
newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
|
||||
}
|
||||
if (wasCached) {
|
||||
newShortcut.addFlags(ShortcutInfo.FLAG_CACHED);
|
||||
}
|
||||
|
||||
forceReplaceShortcutInner(newShortcut);
|
||||
return true;
|
||||
return deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,8 +399,7 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
* @return List of removed shortcuts.
|
||||
*/
|
||||
private List<ShortcutInfo> removeOrphans() {
|
||||
ArrayList<String> removeList = null; // Lazily initialize.
|
||||
List<ShortcutInfo> removedShortcuts = null;
|
||||
List<ShortcutInfo> removeList = null;
|
||||
|
||||
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
|
||||
final ShortcutInfo si = mShortcuts.valueAt(i);
|
||||
@@ -381,18 +408,16 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
|
||||
if (removeList == null) {
|
||||
removeList = new ArrayList<>();
|
||||
removedShortcuts = new ArrayList<>();
|
||||
}
|
||||
removeList.add(si.getId());
|
||||
removedShortcuts.add(si);
|
||||
removeList.add(si);
|
||||
}
|
||||
if (removeList != null) {
|
||||
for (int i = removeList.size() - 1; i >= 0; i--) {
|
||||
forceDeleteShortcutInner(removeList.get(i));
|
||||
forceDeleteShortcutInner(removeList.get(i).getId());
|
||||
}
|
||||
}
|
||||
|
||||
return removedShortcuts;
|
||||
return removeList;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -424,68 +449,69 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
* Remove a dynamic shortcut by ID. It'll be removed from the dynamic set, but if the shortcut
|
||||
* is pinned or cached, it'll remain as a pinned or cached shortcut, and is still enabled.
|
||||
*
|
||||
* @return true if it's removed, or false if it was not actually removed because it is either
|
||||
* @return The deleted shortcut, or null if it was not actually removed because it is either
|
||||
* pinned or cached.
|
||||
*/
|
||||
public boolean deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
|
||||
final ShortcutInfo removed = deleteOrDisableWithId(
|
||||
public ShortcutInfo deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
|
||||
return deleteOrDisableWithId(
|
||||
shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
|
||||
ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
|
||||
return removed == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a dynamic shortcut by ID. It'll be removed from the dynamic set, but if the shortcut
|
||||
* Disable a dynamic shortcut by ID. It'll be removed from the dynamic set, but if the shortcut
|
||||
* is pinned, it'll remain as a pinned shortcut, but will be disabled.
|
||||
*
|
||||
* @return true if it's actually removed because it wasn't pinned, or false if it's still
|
||||
* pinned.
|
||||
* @return Shortcut if the disabled shortcut got removed because it wasn't pinned. Or null if
|
||||
* it's still pinned.
|
||||
*/
|
||||
private boolean disableDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
|
||||
private ShortcutInfo disableDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
|
||||
int disabledReason) {
|
||||
final ShortcutInfo disabled = deleteOrDisableWithId(
|
||||
shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false, ignoreInvisible,
|
||||
disabledReason);
|
||||
return disabled == null;
|
||||
return deleteOrDisableWithId(shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false,
|
||||
ignoreInvisible, disabledReason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a long lived shortcut by ID. If the shortcut is pinned, it'll remain as a pinned
|
||||
* shortcut, and is still enabled.
|
||||
*
|
||||
* @return true if it's actually removed because it wasn't pinned, or false if it's still
|
||||
* pinned.
|
||||
* @return The deleted shortcut, or null if it was not actually removed because it's pinned.
|
||||
*/
|
||||
public boolean deleteLongLivedWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
|
||||
public ShortcutInfo deleteLongLivedWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
|
||||
final ShortcutInfo shortcut = mShortcuts.get(shortcutId);
|
||||
if (shortcut != null) {
|
||||
shortcut.clearFlags(ShortcutInfo.FLAG_CACHED);
|
||||
}
|
||||
final ShortcutInfo removed = deleteOrDisableWithId(
|
||||
return deleteOrDisableWithId(
|
||||
shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
|
||||
ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
|
||||
return removed == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a dynamic shortcut by ID. It'll be removed from the dynamic set, but if the shortcut
|
||||
* is pinned, it'll remain as a pinned shortcut but will be disabled.
|
||||
*
|
||||
* @return Shortcut if the disabled shortcut got removed because it wasn't pinned. Or null if
|
||||
* it's still pinned.
|
||||
*/
|
||||
public void disableWithId(@NonNull String shortcutId, String disabledMessage,
|
||||
public ShortcutInfo disableWithId(@NonNull String shortcutId, String disabledMessage,
|
||||
int disabledMessageResId, boolean overrideImmutable, boolean ignoreInvisible,
|
||||
int disabledReason) {
|
||||
final ShortcutInfo disabled = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
|
||||
final ShortcutInfo deleted = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
|
||||
overrideImmutable, ignoreInvisible, disabledReason);
|
||||
|
||||
// If disabled id still exists, it is pinned and we need to update the disabled message.
|
||||
final ShortcutInfo disabled = mShortcuts.get(shortcutId);
|
||||
if (disabled != null) {
|
||||
if (disabledMessage != null) {
|
||||
disabled.setDisabledMessage(disabledMessage);
|
||||
} else if (disabledMessageResId != 0) {
|
||||
disabled.setDisabledMessageResId(disabledMessageResId);
|
||||
|
||||
mShortcutUser.mService.fixUpShortcutResourceNamesAndValues(disabled);
|
||||
}
|
||||
}
|
||||
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -521,10 +547,10 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
oldShortcut.setActivity(null);
|
||||
}
|
||||
|
||||
return oldShortcut;
|
||||
return null;
|
||||
} else {
|
||||
forceDeleteShortcutInner(shortcutId);
|
||||
return null;
|
||||
return oldShortcut;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1005,7 +1031,7 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
"%s is no longer main activity. Disabling shorcut %s.",
|
||||
getPackageName(), si.getId()));
|
||||
if (disableDynamicWithId(si.getId(), /*ignoreInvisible*/ false,
|
||||
ShortcutInfo.DISABLED_REASON_APP_CHANGED)) {
|
||||
ShortcutInfo.DISABLED_REASON_APP_CHANGED) != null) {
|
||||
continue; // Actually removed.
|
||||
}
|
||||
// Still pinned, so fall-through and possibly update the resources.
|
||||
|
||||
@@ -35,7 +35,7 @@ import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -513,10 +513,6 @@ class ShortcutRequestPinProcessor {
|
||||
|
||||
launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId,
|
||||
/*forPinRequest=*/ true);
|
||||
if (changedShortcuts == null) {
|
||||
changedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
changedShortcuts.add(original);
|
||||
|
||||
if (current == null) {
|
||||
if (DEBUG) {
|
||||
@@ -526,6 +522,8 @@ class ShortcutRequestPinProcessor {
|
||||
}
|
||||
|
||||
ps.adjustRanks(); // Shouldn't be needed, but just in case.
|
||||
|
||||
changedShortcuts = Collections.singletonList(ps.findShortcutById(shortcutId));
|
||||
}
|
||||
|
||||
mService.verifyStates();
|
||||
|
||||
@@ -1666,11 +1666,10 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
* - Write to file
|
||||
*/
|
||||
void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId,
|
||||
@Nullable List<ShortcutInfo> addedOrUpdatedShortcuts,
|
||||
@Nullable List<ShortcutInfo> removedShortcuts) {
|
||||
@Nullable final List<ShortcutInfo> changedShortcuts,
|
||||
@Nullable final List<ShortcutInfo> removedShortcuts) {
|
||||
notifyListeners(packageName, userId);
|
||||
notifyShortcutChangeCallbacks(packageName, userId, addedOrUpdatedShortcuts,
|
||||
removedShortcuts);
|
||||
notifyShortcutChangeCallbacks(packageName, userId, changedShortcuts, removedShortcuts);
|
||||
scheduleSaveUser(userId);
|
||||
}
|
||||
|
||||
@@ -1699,8 +1698,11 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
}
|
||||
|
||||
private void notifyShortcutChangeCallbacks(@NonNull String packageName, @UserIdInt int userId,
|
||||
@Nullable List<ShortcutInfo> addedOrUpdatedShortcuts,
|
||||
@Nullable List<ShortcutInfo> removedShortcuts) {
|
||||
@Nullable final List<ShortcutInfo> changedShortcuts,
|
||||
@Nullable final List<ShortcutInfo> removedShortcuts) {
|
||||
final List<ShortcutInfo> changedList = removeNonKeyFields(changedShortcuts);
|
||||
final List<ShortcutInfo> removedList = removeNonKeyFields(removedShortcuts);
|
||||
|
||||
final UserHandle user = UserHandle.of(userId);
|
||||
injectPostToHandler(() -> {
|
||||
try {
|
||||
@@ -1713,12 +1715,11 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
copy = new ArrayList<>(mShortcutChangeCallbacks);
|
||||
}
|
||||
for (int i = copy.size() - 1; i >= 0; i--) {
|
||||
if (!CollectionUtils.isEmpty(addedOrUpdatedShortcuts)) {
|
||||
copy.get(i).onShortcutsAddedOrUpdated(packageName, addedOrUpdatedShortcuts,
|
||||
user);
|
||||
if (!CollectionUtils.isEmpty(changedList)) {
|
||||
copy.get(i).onShortcutsAddedOrUpdated(packageName, changedList, user);
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(removedShortcuts)) {
|
||||
copy.get(i).onShortcutsRemoved(packageName, removedShortcuts, user);
|
||||
if (!CollectionUtils.isEmpty(removedList)) {
|
||||
copy.get(i).onShortcutsRemoved(packageName, removedList, user);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
@@ -1726,6 +1727,25 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
});
|
||||
}
|
||||
|
||||
private List<ShortcutInfo> removeNonKeyFields(@Nullable List<ShortcutInfo> shortcutInfos) {
|
||||
if (CollectionUtils.isEmpty(shortcutInfos)) {
|
||||
return shortcutInfos;
|
||||
}
|
||||
|
||||
final int size = shortcutInfos.size();
|
||||
List<ShortcutInfo> keyFieldOnlyShortcuts = new ArrayList<>(size);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
final ShortcutInfo si = shortcutInfos.get(i);
|
||||
if (si.hasKeyFieldsOnly()) {
|
||||
keyFieldOnlyShortcuts.add(si);
|
||||
} else {
|
||||
keyFieldOnlyShortcuts.add(si.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO));
|
||||
}
|
||||
}
|
||||
return keyFieldOnlyShortcuts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up / validate an incoming shortcut.
|
||||
* - Make sure all mandatory fields are set.
|
||||
@@ -1820,6 +1840,7 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
|
||||
injectBinderCallingPid(), injectBinderCallingUid());
|
||||
|
||||
List<ShortcutInfo> changedShortcuts = null;
|
||||
List<ShortcutInfo> removedShortcuts = null;
|
||||
|
||||
synchronized (mLock) {
|
||||
@@ -1846,7 +1867,12 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
|
||||
}
|
||||
|
||||
// First, remove all un-pinned; dynamic shortcuts
|
||||
ArrayList<ShortcutInfo> cachedOrPinned = new ArrayList<>();
|
||||
ps.findAll(cachedOrPinned, (ShortcutInfo si) -> si.isVisibleToPublisher()
|
||||
&& si.isDynamic() && (si.isCached() || si.isPinned()),
|
||||
ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
|
||||
|
||||
// First, remove all un-pinned and non-cached; dynamic shortcuts
|
||||
removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
|
||||
|
||||
// Then, add/update all. We need to make sure to take over "pinned" flag.
|
||||
@@ -1857,8 +1883,12 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
|
||||
// Lastly, adjust the ranks.
|
||||
ps.adjustRanks();
|
||||
|
||||
changedShortcuts = prepareChangedShortcuts(
|
||||
cachedOrPinned, newShortcuts, removedShortcuts, ps);
|
||||
}
|
||||
packageShortcutsChanged(packageName, userId, newShortcuts, removedShortcuts);
|
||||
|
||||
packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
|
||||
|
||||
verifyStates();
|
||||
|
||||
@@ -1916,6 +1946,11 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
"ShortcutInfo.enabled cannot be changed with updateShortcuts()");
|
||||
}
|
||||
|
||||
if (target.isLongLived() != source.isLongLived()) {
|
||||
Slog.w(TAG,
|
||||
"ShortcutInfo.longLived cannot be changed with updateShortcuts()");
|
||||
}
|
||||
|
||||
// When updating the rank, we need to insert between existing ranks, so set
|
||||
// this setRankChanged, and also copy the implicit rank fo adjustRanks().
|
||||
if (source.hasRank()) {
|
||||
@@ -2026,8 +2061,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
verifyCaller(packageName, userId);
|
||||
verifyShortcutInfoPackage(packageName, shortcut);
|
||||
|
||||
final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
|
||||
injectBinderCallingPid(), injectBinderCallingUid());
|
||||
List<ShortcutInfo> changedShortcuts = new ArrayList<>();
|
||||
List<ShortcutInfo> removedShortcuts = null;
|
||||
|
||||
synchronized (mLock) {
|
||||
throwIfUserLockedL(userId);
|
||||
@@ -2052,14 +2087,22 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
shortcut.setRankChanged();
|
||||
|
||||
// Push it.
|
||||
if (!ps.pushDynamicShortcut(shortcut)) {
|
||||
return;
|
||||
boolean deleted = ps.pushDynamicShortcut(shortcut, changedShortcuts);
|
||||
|
||||
if (deleted) {
|
||||
if (changedShortcuts.isEmpty()) {
|
||||
return; // Failed to push.
|
||||
}
|
||||
removedShortcuts = Collections.singletonList(changedShortcuts.get(0));
|
||||
changedShortcuts.clear();
|
||||
}
|
||||
changedShortcuts.add(shortcut);
|
||||
|
||||
// Lastly, adjust the ranks.
|
||||
ps.adjustRanks();
|
||||
}
|
||||
packageShortcutsChanged(packageName, userId, Collections.singletonList(shortcut), null);
|
||||
|
||||
packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
|
||||
|
||||
verifyStates();
|
||||
}
|
||||
@@ -2147,6 +2190,9 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
verifyCaller(packageName, userId);
|
||||
Objects.requireNonNull(shortcutIds, "shortcutIds must be provided");
|
||||
|
||||
List<ShortcutInfo> changedShortcuts = null;
|
||||
List<ShortcutInfo> removedShortcuts = null;
|
||||
|
||||
synchronized (mLock) {
|
||||
throwIfUserLockedL(userId);
|
||||
|
||||
@@ -2163,17 +2209,30 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
|
||||
continue;
|
||||
}
|
||||
ps.disableWithId(id,
|
||||
|
||||
final ShortcutInfo deleted = ps.disableWithId(id,
|
||||
disabledMessageString, disabledMessageResId,
|
||||
/* overrideImmutable=*/ false, /*ignoreInvisible=*/ true,
|
||||
ShortcutInfo.DISABLED_REASON_BY_APP);
|
||||
|
||||
if (deleted == null) {
|
||||
if (changedShortcuts == null) {
|
||||
changedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
changedShortcuts.add(ps.findShortcutById(id));
|
||||
} else {
|
||||
if (removedShortcuts == null) {
|
||||
removedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
removedShortcuts.add(deleted);
|
||||
}
|
||||
}
|
||||
|
||||
// We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
|
||||
ps.adjustRanks();
|
||||
}
|
||||
// TODO: Disabling dynamic shortcuts will removed them if not pinned. Cover all cases.
|
||||
packageShortcutsChanged(packageName, userId, null, null);
|
||||
|
||||
packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
|
||||
|
||||
verifyStates();
|
||||
}
|
||||
@@ -2200,13 +2259,10 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
}
|
||||
ps.enableWithId(id);
|
||||
|
||||
final ShortcutInfo si = ps.findShortcutById(id);
|
||||
if (si != null) {
|
||||
if (changedShortcuts == null) {
|
||||
changedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
changedShortcuts.add(si);
|
||||
if (changedShortcuts == null) {
|
||||
changedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
changedShortcuts.add(ps.findShortcutById(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2237,18 +2293,18 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
|
||||
continue;
|
||||
}
|
||||
final ShortcutInfo si = ps.findShortcutById(id);
|
||||
final boolean removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true);
|
||||
if (removed) {
|
||||
if (removedShortcuts == null) {
|
||||
removedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
removedShortcuts.add(si);
|
||||
} else {
|
||||
|
||||
ShortcutInfo removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true);
|
||||
if (removed == null) {
|
||||
if (changedShortcuts == null) {
|
||||
changedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
changedShortcuts.add(si);
|
||||
changedShortcuts.add(ps.findShortcutById(id));
|
||||
} else {
|
||||
if (removedShortcuts == null) {
|
||||
removedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
removedShortcuts.add(removed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2264,7 +2320,7 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
|
||||
verifyCaller(packageName, userId);
|
||||
|
||||
List<ShortcutInfo> changedShortcuts = null;
|
||||
List<ShortcutInfo> changedShortcuts = new ArrayList<>();
|
||||
List<ShortcutInfo> removedShortcuts = null;
|
||||
|
||||
synchronized (mLock) {
|
||||
@@ -2272,10 +2328,16 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
|
||||
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
|
||||
|
||||
// Dynamic shortcuts that are either cached or pinned will not get deleted.
|
||||
ps.findAll(changedShortcuts, (ShortcutInfo si) -> si.isVisibleToPublisher()
|
||||
&& si.isDynamic() && (si.isCached() || si.isPinned()),
|
||||
ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
|
||||
|
||||
removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
|
||||
changedShortcuts = prepareChangedShortcuts(
|
||||
changedShortcuts, null, removedShortcuts, ps);
|
||||
}
|
||||
|
||||
// TODO: Pinned and cached shortcuts are not removed, add those to changedShortcuts list
|
||||
packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
|
||||
|
||||
verifyStates();
|
||||
@@ -2300,22 +2362,21 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
|
||||
for (int i = shortcutIds.size() - 1; i >= 0; i--) {
|
||||
final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i));
|
||||
if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final ShortcutInfo si = ps.findShortcutById(id);
|
||||
final boolean removed = ps.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
|
||||
|
||||
if (si != null) {
|
||||
if (removed) {
|
||||
if (removedShortcuts == null) {
|
||||
removedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
removedShortcuts.add(si);
|
||||
} else {
|
||||
if (changedShortcuts == null) {
|
||||
changedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
changedShortcuts.add(si);
|
||||
ShortcutInfo removed = ps.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
|
||||
if (removed != null) {
|
||||
if (removedShortcuts == null) {
|
||||
removedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
removedShortcuts.add(removed);
|
||||
} else {
|
||||
if (changedShortcuts == null) {
|
||||
changedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
changedShortcuts.add(ps.findShortcutById(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2939,6 +3000,7 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
Objects.requireNonNull(shortcutIds, "shortcutIds");
|
||||
|
||||
List<ShortcutInfo> changedShortcuts = null;
|
||||
List<ShortcutInfo> removedShortcuts = null;
|
||||
|
||||
synchronized (mLock) {
|
||||
throwIfUserLockedL(userId);
|
||||
@@ -2948,24 +3010,31 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
|
||||
launcher.attemptToRestoreIfNeededAndSave();
|
||||
|
||||
launcher.pinShortcuts(userId, packageName, shortcutIds, /*forPinRequest=*/ false);
|
||||
|
||||
final ShortcutPackage sp = getUserShortcutsLocked(userId)
|
||||
.getPackageShortcutsIfExists(packageName);
|
||||
if (sp != null) {
|
||||
for (int i = 0; i < shortcutIds.size(); i++) {
|
||||
final ShortcutInfo si = sp.findShortcutById(shortcutIds.get(i));
|
||||
if (si != null) {
|
||||
if (changedShortcuts == null) {
|
||||
changedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
changedShortcuts.add(si);
|
||||
}
|
||||
// List the shortcuts that are pinned only, these will get removed.
|
||||
removedShortcuts = new ArrayList<>();
|
||||
sp.findAll(removedShortcuts, (ShortcutInfo si) -> si.isVisibleToPublisher()
|
||||
&& si.isPinned() && !si.isCached() && !si.isDynamic()
|
||||
&& !si.isDeclaredInManifest(), ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO,
|
||||
callingPackage, launcherUserId, false);
|
||||
}
|
||||
// Get list of shortcuts that will get unpinned.
|
||||
ArraySet<String> oldPinnedIds = launcher.getPinnedShortcutIds(packageName, userId);
|
||||
|
||||
launcher.pinShortcuts(userId, packageName, shortcutIds, /*forPinRequest=*/ false);
|
||||
|
||||
if (oldPinnedIds != null && removedShortcuts != null) {
|
||||
for (int i = 0; i < removedShortcuts.size(); i++) {
|
||||
oldPinnedIds.remove(removedShortcuts.get(i).getId());
|
||||
}
|
||||
}
|
||||
changedShortcuts = prepareChangedShortcuts(
|
||||
oldPinnedIds, new ArraySet<>(shortcutIds), removedShortcuts, sp);
|
||||
}
|
||||
// TODO: Include previously pinned shortcuts since they are not pinned anymore.
|
||||
packageShortcutsChanged(packageName, userId, changedShortcuts, null);
|
||||
|
||||
packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
|
||||
|
||||
verifyStates();
|
||||
}
|
||||
@@ -3045,17 +3114,17 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
+ "shortcut " + si.getId());
|
||||
}
|
||||
} else {
|
||||
boolean removed = false;
|
||||
ShortcutInfo removed = null;
|
||||
if (si.isDynamic()) {
|
||||
si.clearFlags(ShortcutInfo.FLAG_CACHED);
|
||||
} else {
|
||||
removed = sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
|
||||
}
|
||||
if (removed) {
|
||||
if (removed != null) {
|
||||
if (removedShortcuts == null) {
|
||||
removedShortcuts = new ArrayList<>(1);
|
||||
}
|
||||
removedShortcuts.add(si);
|
||||
removedShortcuts.add(removed);
|
||||
} else {
|
||||
if (changedShortcuts == null) {
|
||||
changedShortcuts = new ArrayList<>(1);
|
||||
@@ -3222,8 +3291,6 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
// Grant read uri permission to the caller on behalf of the shortcut owner. All
|
||||
// granted permissions are revoked when the default launcher changes, or when
|
||||
// device is rebooted.
|
||||
// b/151572645 is tracking a bug where Uri permissions are persisted across
|
||||
// reboots, even when Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION is not used.
|
||||
mUriGrantsManager.grantUriPermissionFromOwner(mUriPermissionOwner, packageUid,
|
||||
launcherPackage, Uri.parse(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION,
|
||||
userId, launcherUserId);
|
||||
@@ -4872,4 +4939,61 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
mShortcutBitmapSaver.waitForAllSavesLocked();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This helper method does the following 3 tasks:
|
||||
*
|
||||
* 1- Combines the |changed| and |updated| shortcut lists, while removing duplicates.
|
||||
* 2- If a shortcut is deleted and added at once in the same operation, removes it from the
|
||||
* |removed| list.
|
||||
* 3- Reloads the final list to get the latest flags.
|
||||
*/
|
||||
private List<ShortcutInfo> prepareChangedShortcuts(ArraySet<String> changedIds,
|
||||
ArraySet<String> newIds, List<ShortcutInfo> deletedList, final ShortcutPackage ps) {
|
||||
if (ps == null) {
|
||||
// This can happen when package restore is not finished yet.
|
||||
return null;
|
||||
}
|
||||
if (CollectionUtils.isEmpty(changedIds) && CollectionUtils.isEmpty(newIds)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArraySet<String> resultIds = new ArraySet<>();
|
||||
if (!CollectionUtils.isEmpty(changedIds)) {
|
||||
resultIds.addAll(changedIds);
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(newIds)) {
|
||||
resultIds.addAll(newIds);
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(deletedList)) {
|
||||
deletedList.removeIf((ShortcutInfo si) -> resultIds.contains(si.getId()));
|
||||
}
|
||||
|
||||
List<ShortcutInfo> result = new ArrayList<>();
|
||||
ps.findAll(result, (ShortcutInfo si) -> resultIds.contains(si.getId()),
|
||||
ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<ShortcutInfo> prepareChangedShortcuts(List<ShortcutInfo> changedList,
|
||||
List<ShortcutInfo> newList, List<ShortcutInfo> deletedList, final ShortcutPackage ps) {
|
||||
ArraySet<String> changedIds = new ArraySet<>();
|
||||
addShortcutIdsToSet(changedIds, changedList);
|
||||
|
||||
ArraySet<String> newIds = new ArraySet<>();
|
||||
addShortcutIdsToSet(newIds, newList);
|
||||
|
||||
return prepareChangedShortcuts(changedIds, newIds, deletedList, ps);
|
||||
}
|
||||
|
||||
private void addShortcutIdsToSet(ArraySet<String> ids, List<ShortcutInfo> shortcuts) {
|
||||
if (CollectionUtils.isEmpty(shortcuts)) {
|
||||
return;
|
||||
}
|
||||
final int size = shortcuts.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
ids.add(shortcuts.get(i).getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,775 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.server.pm;
|
||||
|
||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
|
||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.LauncherApps.ShortcutChangeCallback;
|
||||
import android.content.pm.LauncherApps.ShortcutQuery;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.os.test.TestLooper;
|
||||
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests for {@link android.content.pm.LauncherApps.ShortcutChangeCallback} and relevant APIs.
|
||||
*
|
||||
atest -c com.android.server.pm.ShortcutManagerTest11
|
||||
*/
|
||||
public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
|
||||
|
||||
private static final ShortcutQuery QUERY_MATCH_ALL = createShortcutQuery(
|
||||
ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED);
|
||||
|
||||
private final TestLooper mTestLooper = new TestLooper();
|
||||
|
||||
public void testShortcutChangeCallback_setDynamicShortcuts() {
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2")));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1", "s2");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_setDynamicShortcuts_replaceSameId() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2")));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s2", "s3")));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsRemoved(
|
||||
eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
assertWith(changedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s2", "s3");
|
||||
|
||||
assertWith(removedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_setDynamicShortcuts_pinnedAndCached() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(
|
||||
list(makeShortcut("s1"), makeLongLivedShortcut("s2"))));
|
||||
});
|
||||
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_0);
|
||||
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s3", "s4")));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
|
||||
|
||||
assertWith(changedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1", "s2", "s3", "s4");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_pinShortcuts() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2")));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_0);
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_pinShortcuts_unpinOthers() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2", "s3")));
|
||||
});
|
||||
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), HANDLE_USER_0);
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.removeDynamicShortcuts(list("s1", "s2"));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s3"), HANDLE_USER_0);
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsRemoved(
|
||||
eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
assertWith(changedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s2", "s3");
|
||||
|
||||
assertWith(removedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_cacheShortcuts() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(list(makeLongLivedShortcut("s1"),
|
||||
makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"))));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s3"), HANDLE_USER_0);
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1", "s3");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_uncacheShortcuts() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(list(makeLongLivedShortcut("s1"),
|
||||
makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"))));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), HANDLE_USER_0);
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s2");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_uncacheShortcuts_causeDeletion() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(list(makeLongLivedShortcut("s1"),
|
||||
makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"))));
|
||||
});
|
||||
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2", "s3"), HANDLE_USER_0);
|
||||
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.removeDynamicShortcuts(list("s2", "s3"));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s2", "s3"), HANDLE_USER_0);
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsRemoved(
|
||||
eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
assertWith(changedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s2");
|
||||
|
||||
assertWith(removedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s3");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_updateShortcuts() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
|
||||
makeShortcutWithActivity("s2", new ComponentName(CALLING_PACKAGE_1, "test")))));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
final ComponentName updatedCn = new ComponentName(CALLING_PACKAGE_1, "updated activity");
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.updateShortcuts(list(makeShortcutWithActivity("s2", updatedCn))));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s2");
|
||||
assertEquals(updatedCn, ((ShortcutInfo) shortcuts.getValue().get(0)).getActivity());
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_addDynamicShortcuts() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1")));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.addDynamicShortcuts(makeShortcuts("s1", "s2")));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1", "s2");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_pushDynamicShortcut() {
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.pushDynamicShortcut(makeShortcut("s1"));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_pushDynamicShortcut_existingId() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts((makeShortcuts("s1", "s2", "s3", "s4", "s5",
|
||||
"s6", "s7", "s8", "s9", "s10"))));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.pushDynamicShortcut(makeShortcut("s5"));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s5");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_pushDynamicShortcut_causeDeletion() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts((makeShortcuts("s1", "s2", "s3", "s4", "s5",
|
||||
"s6", "s7", "s8", "s9", "s10"))));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.pushDynamicShortcut(makeShortcut("s11"));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsRemoved(
|
||||
eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
assertWith(changedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s11");
|
||||
|
||||
assertWith(removedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s10");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_pushDynamicShortcut_causeDeletionButCached() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts((makeShortcuts("s1", "s2", "s3", "s4", "s5",
|
||||
"s6", "s7", "s8", "s9"))));
|
||||
ShortcutInfo s10 = makeLongLivedShortcut("s10");
|
||||
s10.setRank(10);
|
||||
mManager.pushDynamicShortcut(s10); // Add a long lived shortcut to the end of the list.
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s10"), HANDLE_USER_0);
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.pushDynamicShortcut(makeShortcut("s11"));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s10", "s11");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_disableShortcuts() {
|
||||
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2")));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.disableShortcuts(list("s2"));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verify(callback, times(0)).onShortcutsAddedOrUpdated(any(), any(), any());
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsRemoved(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s2");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_disableShortcuts_pinnedAndCached() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(
|
||||
list(makeShortcut("s1"), makeLongLivedShortcut("s2"), makeShortcut("s3"))));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
|
||||
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.disableShortcuts(list("s1", "s2", "s3"));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsRemoved(
|
||||
eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
assertWith(changedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s2", "s3");
|
||||
|
||||
assertWith(removedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_enableShortcuts() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(
|
||||
list(makeShortcut("s1"), makeLongLivedShortcut("s2"), makeShortcut("s3"))));
|
||||
});
|
||||
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
|
||||
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.disableShortcuts(list("s1", "s2", "s3"));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.enableShortcuts(list("s1", "s2", "s3"));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s2", "s3");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_removeDynamicShortcuts() {
|
||||
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2")));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.removeDynamicShortcuts(list("s2"));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verify(callback, times(0)).onShortcutsAddedOrUpdated(any(), any(), any());
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsRemoved(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s2");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_removeDynamicShortcuts_pinnedAndCached() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
|
||||
makeLongLivedShortcut("s2"), makeShortcut("s3"), makeShortcut("s4"))));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
|
||||
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.removeDynamicShortcuts(list("s1", "s2", "s3"));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsRemoved(
|
||||
eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
assertWith(changedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s2", "s3");
|
||||
|
||||
assertWith(removedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_removeAllDynamicShortcuts() {
|
||||
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2")));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.removeAllDynamicShortcuts();
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verify(callback, times(0)).onShortcutsAddedOrUpdated(any(), any(), any());
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsRemoved(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1", "s2");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_removeAllDynamicShortcuts_pinnedAndCached() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(
|
||||
list(makeShortcut("s1"), makeLongLivedShortcut("s2"), makeShortcut("s3"))));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
|
||||
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.removeAllDynamicShortcuts();
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsRemoved(
|
||||
eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
assertWith(changedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s2", "s3");
|
||||
|
||||
assertWith(removedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_removeLongLivedShortcuts_notCached() {
|
||||
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
|
||||
makeLongLivedShortcut("s2"), makeShortcut("s3"))));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.removeLongLivedShortcuts(list("s1", "s2"));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verify(callback, times(0)).onShortcutsAddedOrUpdated(any(), any(), any());
|
||||
|
||||
ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsRemoved(
|
||||
eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
assertWith(shortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1", "s2");
|
||||
}
|
||||
|
||||
public void testShortcutChangeCallback_removeLongLivedShortcuts_pinnedAndCached() {
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
|
||||
makeLongLivedShortcut("s2"), makeShortcut("s3"), makeShortcut("s4"))));
|
||||
});
|
||||
|
||||
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
|
||||
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
|
||||
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
|
||||
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
|
||||
mTestLooper.getNewExecutor());
|
||||
});
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
mManager.removeLongLivedShortcuts(list("s1", "s2", "s3"));
|
||||
});
|
||||
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsAddedOrUpdated(
|
||||
eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
|
||||
verify(callback, times(1)).onShortcutsRemoved(
|
||||
eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
|
||||
|
||||
assertWith(changedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s3");
|
||||
|
||||
assertWith(removedShortcuts.getValue())
|
||||
.areAllWithKeyFieldsOnly()
|
||||
.haveIds("s1", "s2");
|
||||
}
|
||||
|
||||
private static ShortcutQuery createShortcutQuery(int queryFlags) {
|
||||
ShortcutQuery q = new ShortcutQuery();
|
||||
return q.setQueryFlags(ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user