Merge "ShortcutManager: Implement max # of shortcuts" into nyc-mr1-dev
This commit is contained in:
@@ -26,11 +26,13 @@ import android.os.PersistableBundle;
|
|||||||
import android.text.format.Formatter;
|
import android.text.format.Formatter;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.Preconditions;
|
import com.android.internal.util.Preconditions;
|
||||||
import com.android.internal.util.XmlUtils;
|
import com.android.internal.util.XmlUtils;
|
||||||
|
import com.android.server.pm.ShortcutService.ShortcutOperation;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
@@ -40,6 +42,8 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
@@ -126,7 +130,8 @@ class ShortcutPackage extends ShortcutPackageItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a shortcut is about to be published. At this point we know the publisher package
|
* Called when a shortcut is about to be published. At this point we know the publisher
|
||||||
|
* package
|
||||||
* exists (as opposed to Launcher trying to fetch shortcuts from a non-existent package), so
|
* exists (as opposed to Launcher trying to fetch shortcuts from a non-existent package), so
|
||||||
* we do some initialization for the package.
|
* we do some initialization for the package.
|
||||||
*/
|
*/
|
||||||
@@ -292,12 +297,17 @@ class ShortcutPackage extends ShortcutPackageItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a dynamic shortcut by ID.
|
* Remove 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, and is still enabled.
|
||||||
*/
|
*/
|
||||||
public void deleteDynamicWithId(@NonNull String shortcutId) {
|
public void deleteDynamicWithId(@NonNull String shortcutId) {
|
||||||
deleteOrDisableWithId(shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false);
|
deleteOrDisableWithId(shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
public void disableWithId(@NonNull String shortcutId, String disabledMessage,
|
public void disableWithId(@NonNull String shortcutId, String disabledMessage,
|
||||||
int disabledMessageResId, boolean overrideImmutable) {
|
int disabledMessageResId, boolean overrideImmutable) {
|
||||||
final ShortcutInfo disabled = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
|
final ShortcutInfo disabled = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
|
||||||
@@ -625,6 +635,10 @@ class ShortcutPackage extends ShortcutPackageItem {
|
|||||||
// (Re-)publish manifest shortcut.
|
// (Re-)publish manifest shortcut.
|
||||||
changed |= publishManifestShortcuts(newManifestShortcutList);
|
changed |= publishManifestShortcuts(newManifestShortcutList);
|
||||||
|
|
||||||
|
if (newManifestShortcutList != null) {
|
||||||
|
changed |= pushOutExcessShortcuts();
|
||||||
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
// This will send a notification to the launcher, and also save .
|
// This will send a notification to the launcher, and also save .
|
||||||
s.packageShortcutsChanged(getPackageName(), getPackageUserId());
|
s.packageShortcutsChanged(getPackageName(), getPackageUserId());
|
||||||
@@ -642,10 +656,6 @@ class ShortcutPackage extends ShortcutPackageItem {
|
|||||||
}
|
}
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
|
|
||||||
// TODO: Check dynamic count
|
|
||||||
|
|
||||||
// TODO: Kick out dynamic if too many
|
|
||||||
|
|
||||||
// Keep the previous IDs.
|
// Keep the previous IDs.
|
||||||
ArraySet<String> toDisableList = null;
|
ArraySet<String> toDisableList = null;
|
||||||
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
|
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
|
||||||
@@ -669,7 +679,7 @@ class ShortcutPackage extends ShortcutPackageItem {
|
|||||||
final ShortcutInfo newShortcut = newManifestShortcutList.get(i);
|
final ShortcutInfo newShortcut = newManifestShortcutList.get(i);
|
||||||
final boolean newDisabled = !newShortcut.isEnabled();
|
final boolean newDisabled = !newShortcut.isEnabled();
|
||||||
|
|
||||||
final String id = newShortcut.getId();
|
final String id = newShortcut.getId();
|
||||||
final ShortcutInfo oldShortcut = mShortcuts.get(id);
|
final ShortcutInfo oldShortcut = mShortcuts.get(id);
|
||||||
|
|
||||||
boolean wasPinned = false;
|
boolean wasPinned = false;
|
||||||
@@ -692,7 +702,6 @@ class ShortcutPackage extends ShortcutPackageItem {
|
|||||||
// Just keep it in toDisableList, so the previous one would be removed.
|
// Just keep it in toDisableList, so the previous one would be removed.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// TODO: Check dynamic count
|
|
||||||
|
|
||||||
// Note even if enabled=false, we still need to update all fields, so do it
|
// Note even if enabled=false, we still need to update all fields, so do it
|
||||||
// regardless.
|
// regardless.
|
||||||
@@ -725,6 +734,179 @@ class ShortcutPackage extends ShortcutPackageItem {
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each target activity, make sure # of dynamic + manifest shortcuts <= max.
|
||||||
|
* If too many, we'll remove the dynamic with the lowest ranks.
|
||||||
|
*/
|
||||||
|
private boolean pushOutExcessShortcuts() {
|
||||||
|
final ShortcutService service = mShortcutUser.mService;
|
||||||
|
final int maxShortcuts = service.getMaxActivityShortcuts();
|
||||||
|
|
||||||
|
boolean changed = false;
|
||||||
|
|
||||||
|
final ArrayMap<ComponentName, ArrayList<ShortcutInfo>> all =
|
||||||
|
sortShortcutsToActivities();
|
||||||
|
for (int outer = all.size() - 1; outer >= 0; outer--) {
|
||||||
|
final ArrayList<ShortcutInfo> list = all.valueAt(outer);
|
||||||
|
if (list.size() <= maxShortcuts) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Sort by isManifestShortcut() and getRank().
|
||||||
|
Collections.sort(list, mShortcutTypeAndRankComparator);
|
||||||
|
|
||||||
|
// Keep [0 .. max), and remove (as dynamic) [max .. size)
|
||||||
|
for (int inner = list.size() - 1; inner >= maxShortcuts; inner--) {
|
||||||
|
final ShortcutInfo shortcut = list.get(inner);
|
||||||
|
|
||||||
|
if (shortcut.isManifestShortcut()) {
|
||||||
|
// This shouldn't happen -- excess shortcuts should all be non-manifest.
|
||||||
|
// But just in case.
|
||||||
|
service.wtf("Found manifest shortcuts in excess list.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
deleteDynamicWithId(shortcut.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
service.verifyStates();
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To sort by isManifestShortcut() and getRank(). i.e. manifest shortcuts come before
|
||||||
|
* non-manifest shortcuts, then sort by rank.
|
||||||
|
*
|
||||||
|
* This is used to decide which dynamic shortcuts to remove when an upgraded version has more
|
||||||
|
* manifest shortcuts than before and as a result we need to remove some of the dynamic
|
||||||
|
* shortcuts. We sort manifest + dynamic shortcuts by this order, and remove the ones with
|
||||||
|
* the last ones.
|
||||||
|
*
|
||||||
|
* (Note the number of manifest shortcuts is always <= the max number, because if there are
|
||||||
|
* more, ShortcutParser would ignore the rest.)
|
||||||
|
*/
|
||||||
|
final Comparator<ShortcutInfo> mShortcutTypeAndRankComparator = (ShortcutInfo a,
|
||||||
|
ShortcutInfo b) -> {
|
||||||
|
if (a.isManifestShortcut() && !b.isManifestShortcut()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!a.isManifestShortcut() && b.isManifestShortcut()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return a.getRank() - b.getRank();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a list of shortcuts for each target activity and return as a map. The result won't
|
||||||
|
* contain "floating" shortcuts because they don't belong on any activities.
|
||||||
|
*/
|
||||||
|
private ArrayMap<ComponentName, ArrayList<ShortcutInfo>> sortShortcutsToActivities() {
|
||||||
|
final int maxShortcuts = mShortcutUser.mService.getMaxActivityShortcuts();
|
||||||
|
|
||||||
|
final ArrayMap<ComponentName, ArrayList<ShortcutInfo>> activitiesToShortcuts
|
||||||
|
= new ArrayMap<>();
|
||||||
|
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
|
||||||
|
final ShortcutInfo si = mShortcuts.valueAt(i);
|
||||||
|
if (si.isFloating()) {
|
||||||
|
continue; // Ignore floating shortcuts, which are not tied to any activities.
|
||||||
|
}
|
||||||
|
|
||||||
|
final ComponentName activity = si.getActivity();
|
||||||
|
|
||||||
|
ArrayList<ShortcutInfo> list = activitiesToShortcuts.get(activity);
|
||||||
|
if (list == null) {
|
||||||
|
list = new ArrayList<>(maxShortcuts * 2);
|
||||||
|
activitiesToShortcuts.put(activity, list);
|
||||||
|
}
|
||||||
|
list.add(si);
|
||||||
|
}
|
||||||
|
return activitiesToShortcuts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Used by {@link #enforceShortcutCountsBeforeOperation} */
|
||||||
|
private void incrementCountForActivity(ArrayMap<ComponentName, Integer> counts,
|
||||||
|
ComponentName cn, int increment) {
|
||||||
|
Integer oldValue = counts.get(cn);
|
||||||
|
if (oldValue == null) {
|
||||||
|
oldValue = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
counts.put(cn, oldValue + increment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by
|
||||||
|
* {@link android.content.pm.ShortcutManager#setDynamicShortcuts},
|
||||||
|
* {@link android.content.pm.ShortcutManager#addDynamicShortcuts}, and
|
||||||
|
* {@link android.content.pm.ShortcutManager#updateShortcuts} before actually performing
|
||||||
|
* the operation to make sure the operation wouldn't result in the target activities having
|
||||||
|
* more than the allowed number of dynamic/manifest shortcuts.
|
||||||
|
*
|
||||||
|
* @param newList shortcut list passed to set, add or updateShortcuts().
|
||||||
|
* @param operation add, set or update.
|
||||||
|
* @throws IllegalArgumentException if the operation would result in going over the max
|
||||||
|
* shortcut count for any activity.
|
||||||
|
*/
|
||||||
|
public void enforceShortcutCountsBeforeOperation(List<ShortcutInfo> newList,
|
||||||
|
@ShortcutOperation int operation) {
|
||||||
|
final ShortcutService service = mShortcutUser.mService;
|
||||||
|
|
||||||
|
// Current # of dynamic / manifest shortcuts for each activity.
|
||||||
|
// (If it's for update, then don't count dynamic shortcuts, since they'll be replaced
|
||||||
|
// anyway.)
|
||||||
|
final ArrayMap<ComponentName, Integer> counts = new ArrayMap<>(4);
|
||||||
|
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
|
||||||
|
final ShortcutInfo shortcut = mShortcuts.valueAt(i);
|
||||||
|
|
||||||
|
if (shortcut.isManifestShortcut()) {
|
||||||
|
incrementCountForActivity(counts, shortcut.getActivity(), 1);
|
||||||
|
} else if (shortcut.isDynamic() && (operation != ShortcutService.OPERATION_SET)) {
|
||||||
|
incrementCountForActivity(counts, shortcut.getActivity(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = newList.size() - 1; i >= 0; i--) {
|
||||||
|
final ShortcutInfo newShortcut = newList.get(i);
|
||||||
|
final ComponentName newActivity = newShortcut.getActivity();
|
||||||
|
if (newActivity == null) {
|
||||||
|
if (operation != ShortcutService.OPERATION_UPDATE) {
|
||||||
|
service.wtf("null Activity found for non-update");
|
||||||
|
}
|
||||||
|
continue; // Activity can be null for update.
|
||||||
|
}
|
||||||
|
|
||||||
|
final ShortcutInfo original = mShortcuts.get(newShortcut.getId());
|
||||||
|
if (original == null) {
|
||||||
|
if (operation == ShortcutService.OPERATION_UPDATE) {
|
||||||
|
continue; // When updating, ignore if there's no target.
|
||||||
|
}
|
||||||
|
// Add() or set(), and there's no existing shortcut with the same ID. We're
|
||||||
|
// simply publishing (as opposed to updating) this shortcut, so just +1.
|
||||||
|
incrementCountForActivity(counts, newActivity, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (original.isFloating() && (operation == ShortcutService.OPERATION_UPDATE)) {
|
||||||
|
// Updating floating shortcuts doesn't affect the count, so ignore.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's add() or update(), then need to decrement for the previous activity.
|
||||||
|
// Skip it for set() since it's already been taken care of by not counting the original
|
||||||
|
// dynamic shortcuts in the first loop.
|
||||||
|
if (operation != ShortcutService.OPERATION_SET) {
|
||||||
|
final ComponentName oldActivity = original.getActivity();
|
||||||
|
if (!original.isFloating()) {
|
||||||
|
incrementCountForActivity(counts, oldActivity, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
incrementCountForActivity(counts, newActivity, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then make sure none of the activities have more than the max number of shortcuts.
|
||||||
|
for (int i = counts.size() - 1; i >= 0; i--) {
|
||||||
|
service.enforceMaxActivityShortcuts(counts.valueAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
|
public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
|
||||||
pw.println();
|
pw.println();
|
||||||
|
|
||||||
@@ -995,4 +1177,46 @@ class ShortcutPackage extends ShortcutPackageItem {
|
|||||||
List<ShortcutInfo> getAllShortcutsForTest() {
|
List<ShortcutInfo> getAllShortcutsForTest() {
|
||||||
return new ArrayList<>(mShortcuts.values());
|
return new ArrayList<>(mShortcuts.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verifyStates() {
|
||||||
|
super.verifyStates();
|
||||||
|
|
||||||
|
boolean failed = false;
|
||||||
|
|
||||||
|
final ArrayMap<ComponentName, ArrayList<ShortcutInfo>> all =
|
||||||
|
sortShortcutsToActivities();
|
||||||
|
|
||||||
|
// Make sure each activity won't have more than max shortcuts.
|
||||||
|
for (int i = all.size() - 1; i >= 0; i--) {
|
||||||
|
if (all.valueAt(i).size() > mShortcutUser.mService.getMaxActivityShortcuts()) {
|
||||||
|
failed = true;
|
||||||
|
Log.e(TAG, "Package " + getPackageName() + ": activity " + all.keyAt(i)
|
||||||
|
+ " has " + all.valueAt(i).size() + " shortcuts.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
|
||||||
|
final ShortcutInfo si = mShortcuts.valueAt(i);
|
||||||
|
if (!(si.isManifestShortcut() || si.isDynamic() || si.isPinned())) {
|
||||||
|
failed = true;
|
||||||
|
Log.e(TAG, "Package " + getPackageName() + ": shortcut " + si.getId()
|
||||||
|
+ " is not manifest, dynamic or pinned.");
|
||||||
|
}
|
||||||
|
if (si.getActivity() == null) {
|
||||||
|
failed = true;
|
||||||
|
Log.e(TAG, "Package " + getPackageName() + ": shortcut " + si.getId()
|
||||||
|
+ " has null activity.");
|
||||||
|
}
|
||||||
|
if ((si.isDynamic() || si.isManifestShortcut()) && !si.isEnabled()) {
|
||||||
|
failed = true;
|
||||||
|
Log.e(TAG, "Package " + getPackageName() + ": shortcut " + si.getId()
|
||||||
|
+ " is not floating, but is disabled.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failed) {
|
||||||
|
throw new IllegalStateException("See logcat for errors");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,4 +132,10 @@ abstract class ShortcutPackageItem {
|
|||||||
|
|
||||||
public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
|
public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
|
||||||
throws IOException, XmlPullParserException;
|
throws IOException, XmlPullParserException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify various internal states.
|
||||||
|
*/
|
||||||
|
public void verifyStates() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ public class ShortcutParser {
|
|||||||
int type;
|
int type;
|
||||||
|
|
||||||
int rank = 0;
|
int rank = 0;
|
||||||
|
final int maxShortcuts = service.getMaxActivityShortcuts();
|
||||||
|
int numShortcuts = 0;
|
||||||
|
|
||||||
outer:
|
outer:
|
||||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||||
@@ -115,10 +117,17 @@ public class ShortcutParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (si != null) {
|
if (si != null) {
|
||||||
|
if (numShortcuts >= maxShortcuts) {
|
||||||
|
Slog.w(TAG, "More than " + maxShortcuts + " shortcuts found for "
|
||||||
|
+ activityInfo.getComponentName() + ", ignoring the rest.");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = new ArrayList<>();
|
result = new ArrayList<>();
|
||||||
}
|
}
|
||||||
result.add(si);
|
result.add(si);
|
||||||
|
numShortcuts++;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.server.pm;
|
package com.android.server.pm;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
@@ -102,6 +103,8 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -114,8 +117,6 @@ import java.util.function.Predicate;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO:
|
* TODO:
|
||||||
* - Implement # of dynamic shortcuts cap.
|
|
||||||
*
|
|
||||||
* - HandleUnlockUser needs to be async. Wait on it in onCleanupUser.
|
* - HandleUnlockUser needs to be async. Wait on it in onCleanupUser.
|
||||||
*
|
*
|
||||||
* - Implement reportShortcutUsed().
|
* - Implement reportShortcutUsed().
|
||||||
@@ -137,12 +138,12 @@ import java.util.function.Predicate;
|
|||||||
*
|
*
|
||||||
* - Add more call stats.
|
* - Add more call stats.
|
||||||
*
|
*
|
||||||
* - Rename getMaxDynamicShortcutCount and mMaxDynamicShortcuts
|
* - Rename mMaxDynamicShortcuts, because it includes manifest shortcuts too.
|
||||||
*/
|
*/
|
||||||
public class ShortcutService extends IShortcutService.Stub {
|
public class ShortcutService extends IShortcutService.Stub {
|
||||||
static final String TAG = "ShortcutService";
|
static final String TAG = "ShortcutService";
|
||||||
|
|
||||||
static final boolean DEBUG = true; // STOPSHIP if true
|
static final boolean DEBUG = false; // STOPSHIP if true
|
||||||
static final boolean DEBUG_LOAD = false; // STOPSHIP if true
|
static final boolean DEBUG_LOAD = false; // STOPSHIP if true
|
||||||
static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
|
static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
|
||||||
|
|
||||||
@@ -329,6 +330,19 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
private static final int PROCESS_STATE_FOREGROUND_THRESHOLD =
|
private static final int PROCESS_STATE_FOREGROUND_THRESHOLD =
|
||||||
ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
|
ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
|
||||||
|
|
||||||
|
static final int OPERATION_SET = 0;
|
||||||
|
static final int OPERATION_ADD = 1;
|
||||||
|
static final int OPERATION_UPDATE = 2;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@IntDef(value = {
|
||||||
|
OPERATION_SET,
|
||||||
|
OPERATION_ADD,
|
||||||
|
OPERATION_UPDATE
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@interface ShortcutOperation {}
|
||||||
|
|
||||||
public ShortcutService(Context context) {
|
public ShortcutService(Context context) {
|
||||||
this(context, BackgroundThread.get().getLooper());
|
this(context, BackgroundThread.get().getLooper());
|
||||||
}
|
}
|
||||||
@@ -1312,14 +1326,22 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throw if {@code numShortcuts} is bigger than {@link #mMaxDynamicShortcuts}.
|
* @throws IllegalArgumentException if {@code numShortcuts} is bigger than
|
||||||
|
* {@link #getMaxActivityShortcuts()}.
|
||||||
*/
|
*/
|
||||||
void enforceMaxDynamicShortcuts(int numShortcuts) {
|
void enforceMaxActivityShortcuts(int numShortcuts) {
|
||||||
if (numShortcuts > mMaxDynamicShortcuts) {
|
if (numShortcuts > mMaxDynamicShortcuts) {
|
||||||
throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
|
throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the max number of dynamic + manifest shortcuts for each launcher icon.
|
||||||
|
*/
|
||||||
|
int getMaxActivityShortcuts() {
|
||||||
|
return mMaxDynamicShortcuts;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* - Sends a notification to LauncherApps
|
* - Sends a notification to LauncherApps
|
||||||
* - Write to file
|
* - Write to file
|
||||||
@@ -1440,11 +1462,12 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
|
|
||||||
ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
|
ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
|
||||||
|
|
||||||
|
ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET);
|
||||||
|
|
||||||
// Throttling.
|
// Throttling.
|
||||||
if (!ps.tryApiCall()) {
|
if (!ps.tryApiCall()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
enforceMaxDynamicShortcuts(size);
|
|
||||||
|
|
||||||
// Validate the shortcuts.
|
// Validate the shortcuts.
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
@@ -1461,6 +1484,9 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
packageShortcutsChanged(packageName, userId);
|
packageShortcutsChanged(packageName, userId);
|
||||||
|
|
||||||
|
verifyStates();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1477,6 +1503,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
|
|
||||||
ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
|
ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
|
||||||
|
|
||||||
|
ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE);
|
||||||
|
|
||||||
// Throttling.
|
// Throttling.
|
||||||
if (!ps.tryApiCall()) {
|
if (!ps.tryApiCall()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1514,6 +1542,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
}
|
}
|
||||||
packageShortcutsChanged(packageName, userId);
|
packageShortcutsChanged(packageName, userId);
|
||||||
|
|
||||||
|
verifyStates();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1530,6 +1560,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
|
|
||||||
ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
|
ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
|
||||||
|
|
||||||
|
ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD);
|
||||||
|
|
||||||
// Throttling.
|
// Throttling.
|
||||||
if (!ps.tryApiCall()) {
|
if (!ps.tryApiCall()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1546,6 +1578,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
}
|
}
|
||||||
packageShortcutsChanged(packageName, userId);
|
packageShortcutsChanged(packageName, userId);
|
||||||
|
|
||||||
|
verifyStates();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1567,6 +1601,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
packageShortcutsChanged(packageName, userId);
|
packageShortcutsChanged(packageName, userId);
|
||||||
|
|
||||||
|
verifyStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1584,6 +1620,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
packageShortcutsChanged(packageName, userId);
|
packageShortcutsChanged(packageName, userId);
|
||||||
|
|
||||||
|
verifyStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1603,6 +1641,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
packageShortcutsChanged(packageName, userId);
|
packageShortcutsChanged(packageName, userId);
|
||||||
|
|
||||||
|
verifyStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1613,6 +1653,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts();
|
getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts();
|
||||||
}
|
}
|
||||||
packageShortcutsChanged(packageName, userId);
|
packageShortcutsChanged(packageName, userId);
|
||||||
|
|
||||||
|
verifyStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2023,6 +2065,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
launcher.pinShortcuts(userId, packageName, shortcutIds);
|
launcher.pinShortcuts(userId, packageName, shortcutIds);
|
||||||
}
|
}
|
||||||
packageShortcutsChanged(packageName, userId);
|
packageShortcutsChanged(packageName, userId);
|
||||||
|
|
||||||
|
verifyStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2940,4 +2984,29 @@ public class ShortcutService extends IShortcutService.Stub {
|
|||||||
return pkg.findShortcutById(shortcutId);
|
return pkg.findShortcutById(shortcutId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Control whether {@link #verifyStates} should be performed. We always perform it during unit
|
||||||
|
* tests.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean injectShouldPerformVerification() {
|
||||||
|
return DEBUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check various internal states and throws if there's any inconsistency.
|
||||||
|
* This is normally only enabled during unit tests.
|
||||||
|
*/
|
||||||
|
final void verifyStates() {
|
||||||
|
if (injectShouldPerformVerification()) {
|
||||||
|
verifyStatesInner();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyStatesInner() {
|
||||||
|
synchronized (this) {
|
||||||
|
forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
services/tests/servicestests/res/xml/shortcut_1_alt.xml
Normal file
28
services/tests/servicestests/res/xml/shortcut_1_alt.xml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2016 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.
|
||||||
|
-->
|
||||||
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="ms1-alt"
|
||||||
|
android:enabled="true"
|
||||||
|
android:shortcutIcon="@drawable/icon1"
|
||||||
|
android:shortcutShortLabel="@string/shortcut_title1"
|
||||||
|
android:shortcutLongLabel="@string/shortcut_text1"
|
||||||
|
android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
|
||||||
|
android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
|
||||||
|
android:shortcutIntentAction="action1"
|
||||||
|
android:shortcutIntentData="data1"
|
||||||
|
/>
|
||||||
|
</shortcuts>
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2016 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.
|
||||||
|
-->
|
||||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
<shortcut
|
<shortcut
|
||||||
android:shortcutId="ms1"
|
android:shortcutId="ms1"
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2016 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.
|
||||||
|
-->
|
||||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
<shortcut
|
<shortcut
|
||||||
android:shortcutId="ms1"
|
android:shortcutId="ms1"
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2016 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.
|
||||||
|
-->
|
||||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
<shortcut
|
<shortcut
|
||||||
android:shortcutId="ms1"
|
android:shortcutId="ms1"
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2016 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.
|
||||||
|
-->
|
||||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
<shortcut
|
<shortcut
|
||||||
android:shortcutId="ms1"
|
android:shortcutId="ms1"
|
||||||
|
|||||||
53
services/tests/servicestests/res/xml/shortcut_5_alt.xml
Normal file
53
services/tests/servicestests/res/xml/shortcut_5_alt.xml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2016 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.
|
||||||
|
-->
|
||||||
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="ms1_alt"
|
||||||
|
android:enabled="true"
|
||||||
|
android:shortcutIcon="@drawable/icon1"
|
||||||
|
android:shortcutShortLabel="@string/shortcut_title1"
|
||||||
|
android:shortcutLongLabel="@string/shortcut_text1"
|
||||||
|
android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
|
||||||
|
android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
|
||||||
|
android:shortcutIntentAction="action1"
|
||||||
|
android:shortcutIntentData="http://a.b.c/1"
|
||||||
|
/>
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="ms2_alt"
|
||||||
|
android:enabled="true"
|
||||||
|
android:shortcutIcon="@drawable/icon2"
|
||||||
|
android:shortcutShortLabel="@string/shortcut_title2"
|
||||||
|
android:shortcutLongLabel="@string/shortcut_text2"
|
||||||
|
android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
|
||||||
|
android:shortcutCategories="android.shortcut.conversation"
|
||||||
|
android:shortcutIntentAction="action2"
|
||||||
|
/>
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="ms3_alt"
|
||||||
|
android:shortcutShortLabel="@string/shortcut_title1"
|
||||||
|
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||||
|
/>
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="ms4_alt"
|
||||||
|
android:shortcutShortLabel="@string/shortcut_title1"
|
||||||
|
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||||
|
/>
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="ms5_alt"
|
||||||
|
android:shortcutShortLabel="@string/shortcut_title1"
|
||||||
|
android:shortcutIntentAction="android.intent.action.VIEW"
|
||||||
|
/>
|
||||||
|
</shortcuts>
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2016 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.
|
||||||
|
-->
|
||||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
<shortcut
|
<shortcut
|
||||||
android:shortcutShortLabel="@string/shortcut_title1"
|
android:shortcutShortLabel="@string/shortcut_title1"
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2016 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.
|
||||||
|
-->
|
||||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
<shortcut
|
<shortcut
|
||||||
android:shortcutId="manifest-shortcut-3"
|
android:shortcutId="manifest-shortcut-3"
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2016 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.
|
||||||
|
-->
|
||||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
<shortcut
|
<shortcut
|
||||||
android:shortcutId="manifest-shortcut-3"
|
android:shortcutId="manifest-shortcut-3"
|
||||||
|
|||||||
@@ -17,37 +17,57 @@ package com.android.server.pm;
|
|||||||
|
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDisabled;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDisabled;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamic;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamic;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamicOrPinned;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
|
.assertAllDynamicOrPinned;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllEnabled;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllEnabled;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIcon;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIcon;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIconFile;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIconResId;
|
.assertAllHaveIconFile;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIntents;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
|
.assertAllHaveIconResId;
|
||||||
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
|
.assertAllHaveIntents;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveTitle;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveTitle;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllImmutable;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllImmutable;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllKeyFieldsOnly;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
|
.assertAllKeyFieldsOnly;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllManifest;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllManifest;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotHaveIntents;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotHaveTitle;
|
.assertAllNotHaveIntents;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotKeyFieldsOnly;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotManifest;
|
.assertAllNotHaveTitle;
|
||||||
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
|
.assertAllNotKeyFieldsOnly;
|
||||||
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
|
.assertAllNotManifest;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllPinned;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllPinned;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllStringsResolved;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
|
.assertAllStringsResolved;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllUnique;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllUnique;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBitmapSize;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBitmapSize;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundleEmpty;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundleEmpty;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertCallbackNotReceived;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertCallbackReceived;
|
.assertCallbackNotReceived;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertCannotUpdateImmutable;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertDynamicAndPinned;
|
.assertCallbackReceived;
|
||||||
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
|
.assertCannotUpdateImmutable;
|
||||||
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
|
.assertDynamicAndPinned;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertDynamicOnly;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertDynamicOnly;
|
||||||
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
|
.assertDynamicShortcutCountExceeded;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertEmpty;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertEmpty;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
|
||||||
|
.assertExpectException;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertShortcutIds;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertShortcutIds;
|
||||||
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.cloneShortcutList;
|
||||||
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.filterByActivity;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.findShortcut;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.findShortcut;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.hashSet;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.hashSet;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.makeBundle;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.makeBundle;
|
||||||
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.parceled;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.pfdToBitmap;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.pfdToBitmap;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resetAll;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resetAll;
|
||||||
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
|
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
|
||||||
@@ -155,8 +175,8 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
* TODO More tests for pinning + manifest shortcuts
|
* TODO More tests for pinning + manifest shortcuts
|
||||||
* TODO Manifest shortcuts + app upgrade -> launcher callback.
|
* TODO Manifest shortcuts + app upgrade -> launcher callback.
|
||||||
* Also locale change should trigger launcehr callbacks too, when they use strign resoucres.
|
* Also locale change should trigger launcher callbacks too, when they use strign resoucres.
|
||||||
* (not implemented yet.)
|
* (not implemented yet.)
|
||||||
* TODO: Add checks with assertAllNotHaveIcon()
|
* TODO: Add checks with assertAllNotHaveIcon()
|
||||||
* TODO: Detailed test for hasShortcutPermissionInner().
|
* TODO: Detailed test for hasShortcutPermissionInner().
|
||||||
* TODO: Add tests for the command line functions too.
|
* TODO: Add tests for the command line functions too.
|
||||||
@@ -169,9 +189,9 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
* Whether to enable dump or not. Should be only true when debugging to avoid bugs where
|
* Whether to enable dump or not. Should be only true when debugging to avoid bugs where
|
||||||
* dump affecting the behavior.
|
* dump affecting the behavior.
|
||||||
*/
|
*/
|
||||||
private static final boolean ENABLE_DUMP = true; // DO NOT SUBMIT WITH true
|
private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH true
|
||||||
|
|
||||||
private static final boolean DUMP_IN_TEARDOWN = true; // DO NOT SUBMIT WITH true
|
private static final boolean DUMP_IN_TEARDOWN = false; // DO NOT SUBMIT WITH true
|
||||||
|
|
||||||
private static final String[] EMPTY_STRINGS = new String[0]; // Just for readability.
|
private static final String[] EMPTY_STRINGS = new String[0]; // Just for readability.
|
||||||
|
|
||||||
@@ -264,6 +284,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
mContext = context;
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean injectShouldPerformVerification() {
|
||||||
|
return true; // Always verify during unit tests.
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String injectShortcutManagerConstants() {
|
String injectShortcutManagerConstants() {
|
||||||
return ConfigConstants.KEY_RESET_INTERVAL_SEC + "=" + (INTERVAL / 1000) + ","
|
return ConfigConstants.KEY_RESET_INTERVAL_SEC + "=" + (INTERVAL / 1000) + ","
|
||||||
@@ -407,6 +432,25 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
protected int injectMyUserId() {
|
protected int injectMyUserId() {
|
||||||
return UserHandle.getUserId(mInjectedCallingUid);
|
return UserHandle.getUserId(mInjectedCallingUid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
|
||||||
|
// Note to simulate the binder RPC, we need to clone the incoming arguments.
|
||||||
|
// Otherwise bad things will happen because they're mutable.
|
||||||
|
return super.setDynamicShortcuts(cloneShortcutList(shortcutInfoList));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
|
||||||
|
// Note to simulate the binder RPC, we need to clone the incoming arguments.
|
||||||
|
return super.addDynamicShortcuts(cloneShortcutList(shortcutInfoList));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updateShortcuts(List<ShortcutInfo> shortcutInfoList) {
|
||||||
|
// Note to simulate the binder RPC, we need to clone the incoming arguments.
|
||||||
|
return super.updateShortcuts(cloneShortcutList(shortcutInfoList));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LauncherAppImplTestable extends LauncherAppsImpl {
|
private class LauncherAppImplTestable extends LauncherAppsImpl {
|
||||||
@@ -585,7 +629,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
|
|
||||||
mInjectedCurrentTimeLillis = START_TIME;
|
mInjectedCurrentTimeLillis = START_TIME;
|
||||||
|
|
||||||
mInjectedPackages = new HashMap<>();;
|
mInjectedPackages = new HashMap<>();
|
||||||
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 1);
|
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 1);
|
||||||
addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 2);
|
addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 2);
|
||||||
addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 3);
|
addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 3);
|
||||||
@@ -929,9 +973,9 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
mService.dumpInner(pw, null);
|
mService.dumpInner(pw, null);
|
||||||
pw.close();
|
pw.close();
|
||||||
|
|
||||||
Log.e(TAG, "Dumping ShortcutService: " + message);
|
Log.v(TAG, "Dumping ShortcutService: " + message);
|
||||||
for (String line : out.toString().split("\n")) {
|
for (String line : out.toString().split("\n")) {
|
||||||
Log.e(TAG, line);
|
Log.v(TAG, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -945,7 +989,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
private void dumpFileOnLogcat(String path, String message) {
|
private void dumpFileOnLogcat(String path, String message) {
|
||||||
if (!ENABLE_DUMP) return;
|
if (!ENABLE_DUMP) return;
|
||||||
|
|
||||||
Log.i(TAG, "Dumping file: " + path + " " + message);
|
Log.v(TAG, "Dumping file: " + path + " " + message);
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
|
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
|
||||||
String line;
|
String line;
|
||||||
@@ -953,7 +997,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
Log.i(TAG, line);
|
Log.i(TAG, line);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Couldn't read file", e);
|
Log.v(TAG, "Couldn't read file", e);
|
||||||
fail("Exception " + e);
|
fail("Exception " + e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -982,7 +1026,8 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void waitOnMainThread() throws Throwable {
|
private void waitOnMainThread() throws Throwable {
|
||||||
runTestOnUiThread(() -> {});
|
runTestOnUiThread(() -> {
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -991,13 +1036,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
private ShortcutInfo makeShortcut(String id) {
|
private ShortcutInfo makeShortcut(String id) {
|
||||||
return makeShortcut(
|
return makeShortcut(
|
||||||
id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
|
id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
|
||||||
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
|
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ShortcutInfo makeShortcutWithTitle(String id, String title) {
|
private ShortcutInfo makeShortcutWithTitle(String id, String title) {
|
||||||
return makeShortcut(
|
return makeShortcut(
|
||||||
id, title, /* activity =*/ null, /* icon =*/ null,
|
id, title, /* activity =*/ null, /* icon =*/ null,
|
||||||
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
|
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1006,7 +1051,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
private ShortcutInfo makeShortcutWithTimestamp(String id, long timestamp) {
|
private ShortcutInfo makeShortcutWithTimestamp(String id, long timestamp) {
|
||||||
final ShortcutInfo s = makeShortcut(
|
final ShortcutInfo s = makeShortcut(
|
||||||
id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
|
id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
|
||||||
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
|
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
|
||||||
s.setTimestamp(timestamp);
|
s.setTimestamp(timestamp);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -1018,7 +1063,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
ComponentName activity) {
|
ComponentName activity) {
|
||||||
final ShortcutInfo s = makeShortcut(
|
final ShortcutInfo s = makeShortcut(
|
||||||
id, "Title-" + id, activity, /* icon =*/ null,
|
id, "Title-" + id, activity, /* icon =*/ null,
|
||||||
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
|
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
|
||||||
s.setTimestamp(timestamp);
|
s.setTimestamp(timestamp);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -1029,7 +1074,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
private ShortcutInfo makeShortcutWithIcon(String id, Icon icon) {
|
private ShortcutInfo makeShortcutWithIcon(String id, Icon icon) {
|
||||||
return makeShortcut(
|
return makeShortcut(
|
||||||
id, "Title-" + id, /* activity =*/ null, icon,
|
id, "Title-" + id, /* activity =*/ null, icon,
|
||||||
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
|
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ShortcutInfo makePackageShortcut(String packageName, String id) {
|
private ShortcutInfo makePackageShortcut(String packageName, String id) {
|
||||||
@@ -1038,7 +1083,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
setCaller(packageName);
|
setCaller(packageName);
|
||||||
ShortcutInfo s = makeShortcut(
|
ShortcutInfo s = makeShortcut(
|
||||||
id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
|
id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
|
||||||
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
|
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
|
||||||
setCaller(origCaller); // restore the caller
|
setCaller(origCaller); // restore the caller
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
@@ -1059,6 +1104,26 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
return new ShortcutInfo.Builder(mClientContext);
|
return new ShortcutInfo.Builder(mClientContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ShortcutInfo makeShortcutWithActivity(String id, ComponentName activity) {
|
||||||
|
return makeShortcut(
|
||||||
|
id, "Title-" + id, activity, /* icon =*/ null,
|
||||||
|
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShortcutInfo makeShortcutWithActivityAndTitle(String id, ComponentName activity,
|
||||||
|
String title) {
|
||||||
|
return makeShortcut(
|
||||||
|
id, title, activity, /* icon =*/ null,
|
||||||
|
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShortcutInfo makeShortcutWithActivityAndRank(String id, ComponentName activity,
|
||||||
|
int rank) {
|
||||||
|
return makeShortcut(
|
||||||
|
id, "Title-" + id, activity, /* icon =*/ null,
|
||||||
|
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), rank);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a shortcut with details.
|
* Make a shortcut with details.
|
||||||
*/
|
*/
|
||||||
@@ -1297,6 +1362,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
i.putExtra(Intent.EXTRA_REPLACING, true);
|
i.putExtra(Intent.EXTRA_REPLACING, true);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Intent genPackageDataClear(String packageName, int userId) {
|
private Intent genPackageDataClear(String packageName, int userId) {
|
||||||
Intent i = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED);
|
Intent i = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED);
|
||||||
i.setData(Uri.parse("package:" + packageName));
|
i.setData(Uri.parse("package:" + packageName));
|
||||||
@@ -1304,15 +1370,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ShortcutInfo parceled(ShortcutInfo si) {
|
|
||||||
Parcel p = Parcel.obtain();
|
|
||||||
p.writeParcelable(si, 0);
|
|
||||||
p.setDataPosition(0);
|
|
||||||
ShortcutInfo si2 = p.readParcelable(getClass().getClassLoader());
|
|
||||||
p.recycle();
|
|
||||||
return si2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for the first launch path, no settings file available.
|
* Test for the first launch path, no settings file available.
|
||||||
*/
|
*/
|
||||||
@@ -7905,4 +7962,434 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
|
|||||||
});
|
});
|
||||||
checkManifestShortcuts_immutable_verify();
|
checkManifestShortcuts_immutable_verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure the APIs won't work on manifest shortcuts.
|
||||||
|
*/
|
||||||
|
public void testManifestShortcuts_tooMany() {
|
||||||
|
// Change the max number of shortcuts.
|
||||||
|
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
|
||||||
|
|
||||||
|
mService.handleUnlockUser(USER_0);
|
||||||
|
|
||||||
|
addManifestShortcutResource(
|
||||||
|
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
|
||||||
|
R.xml.shortcut_5);
|
||||||
|
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||||
|
mService.mPackageMonitor.onReceive(getTestContext(),
|
||||||
|
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
|
||||||
|
|
||||||
|
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||||
|
// Only the first 3 should be published.
|
||||||
|
assertShortcutIds(mManager.getManifestShortcuts(), "ms1", "ms2", "ms3");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMaxShortcutCount_set() {
|
||||||
|
// Change the max number of shortcuts.
|
||||||
|
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
|
||||||
|
|
||||||
|
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||||
|
final ComponentName a1 = new ComponentName(mClientContext, ShortcutActivity.class);
|
||||||
|
final ComponentName a2 = new ComponentName(mClientContext, ShortcutActivity2.class);
|
||||||
|
final ShortcutInfo s1_1 = makeShortcutWithActivity("s11", a1);
|
||||||
|
final ShortcutInfo s1_2 = makeShortcutWithActivity("s12", a1);
|
||||||
|
final ShortcutInfo s1_3 = makeShortcutWithActivity("s13", a1);
|
||||||
|
final ShortcutInfo s1_4 = makeShortcutWithActivity("s14", a1);
|
||||||
|
final ShortcutInfo s1_5 = makeShortcutWithActivity("s15", a1);
|
||||||
|
final ShortcutInfo s1_6 = makeShortcutWithActivity("s16", a1);
|
||||||
|
final ShortcutInfo s2_1 = makeShortcutWithActivity("s21", a2);
|
||||||
|
final ShortcutInfo s2_2 = makeShortcutWithActivity("s22", a2);
|
||||||
|
final ShortcutInfo s2_3 = makeShortcutWithActivity("s23", a2);
|
||||||
|
final ShortcutInfo s2_4 = makeShortcutWithActivity("s24", a2);
|
||||||
|
|
||||||
|
// 3 shortcuts for 2 activities -> okay
|
||||||
|
mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13", "s21", "s22", "s23");
|
||||||
|
|
||||||
|
mManager.removeAllDynamicShortcuts();
|
||||||
|
|
||||||
|
// 4 shortcut for activity 1 -> too many.
|
||||||
|
assertDynamicShortcutCountExceeded(() -> {
|
||||||
|
mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3, s1_4, s2_1, s2_2, s2_3));
|
||||||
|
});
|
||||||
|
assertEmpty(mManager.getDynamicShortcuts());
|
||||||
|
|
||||||
|
// 4 shortcut for activity 2 -> too many.
|
||||||
|
assertDynamicShortcutCountExceeded(() -> {
|
||||||
|
mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3, s2_4));
|
||||||
|
});
|
||||||
|
assertEmpty(mManager.getDynamicShortcuts());
|
||||||
|
|
||||||
|
// First, set 3. Then set 4, which should be ignored.
|
||||||
|
mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13");
|
||||||
|
assertDynamicShortcutCountExceeded(() -> {
|
||||||
|
mManager.setDynamicShortcuts(list(s2_1, s2_2, s2_3, s2_4));
|
||||||
|
});
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13");
|
||||||
|
|
||||||
|
// Set will remove the old dynamic set, unlike add, so the following should pass.
|
||||||
|
mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13");
|
||||||
|
mManager.setDynamicShortcuts(list(s1_4, s1_5, s1_6));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s14", "s15", "s16");
|
||||||
|
|
||||||
|
// Now, test with 2 manifest shortcuts.
|
||||||
|
mManager.removeAllDynamicShortcuts();
|
||||||
|
addManifestShortcutResource(
|
||||||
|
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
|
||||||
|
R.xml.shortcut_2);
|
||||||
|
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||||
|
mService.mPackageMonitor.onReceive(getTestContext(),
|
||||||
|
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
|
||||||
|
assertEquals(2, mManager.getManifestShortcuts().size());
|
||||||
|
|
||||||
|
// Setting 1 to activity 1 will work.
|
||||||
|
mManager.setDynamicShortcuts(list(s1_1, s2_1, s2_2, s2_3));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s21", "s22", "s23");
|
||||||
|
assertEquals(2, mManager.getManifestShortcuts().size());
|
||||||
|
|
||||||
|
// But setting 2 will not.
|
||||||
|
mManager.removeAllDynamicShortcuts();
|
||||||
|
assertDynamicShortcutCountExceeded(() -> {
|
||||||
|
mManager.setDynamicShortcuts(list(s1_1, s1_2, s2_1, s2_2, s2_3));
|
||||||
|
});
|
||||||
|
assertEmpty(mManager.getDynamicShortcuts());
|
||||||
|
assertEquals(2, mManager.getManifestShortcuts().size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMaxShortcutCount_add() {
|
||||||
|
// Change the max number of shortcuts.
|
||||||
|
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
|
||||||
|
|
||||||
|
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||||
|
final ComponentName a1 = new ComponentName(mClientContext, ShortcutActivity.class);
|
||||||
|
final ComponentName a2 = new ComponentName(mClientContext, ShortcutActivity2.class);
|
||||||
|
final ShortcutInfo s1_1 = makeShortcutWithActivity("s11", a1);
|
||||||
|
final ShortcutInfo s1_2 = makeShortcutWithActivity("s12", a1);
|
||||||
|
final ShortcutInfo s1_3 = makeShortcutWithActivity("s13", a1);
|
||||||
|
final ShortcutInfo s1_4 = makeShortcutWithActivity("s14", a1);
|
||||||
|
final ShortcutInfo s2_1 = makeShortcutWithActivity("s21", a2);
|
||||||
|
final ShortcutInfo s2_2 = makeShortcutWithActivity("s22", a2);
|
||||||
|
final ShortcutInfo s2_3 = makeShortcutWithActivity("s23", a2);
|
||||||
|
final ShortcutInfo s2_4 = makeShortcutWithActivity("s24", a2);
|
||||||
|
|
||||||
|
// 3 shortcuts for 2 activities -> okay
|
||||||
|
mManager.addDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13", "s21", "s22", "s23");
|
||||||
|
|
||||||
|
mManager.removeAllDynamicShortcuts();
|
||||||
|
;
|
||||||
|
|
||||||
|
// 4 shortcut for activity 1 -> too many.
|
||||||
|
assertDynamicShortcutCountExceeded(() -> {
|
||||||
|
mManager.addDynamicShortcuts(list(s1_1, s1_2, s1_3, s1_4, s2_1, s2_2, s2_3));
|
||||||
|
});
|
||||||
|
assertEmpty(mManager.getDynamicShortcuts());
|
||||||
|
|
||||||
|
// 4 shortcut for activity 2 -> too many.
|
||||||
|
assertDynamicShortcutCountExceeded(() -> {
|
||||||
|
mManager.addDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3, s2_4));
|
||||||
|
});
|
||||||
|
assertEmpty(mManager.getDynamicShortcuts());
|
||||||
|
|
||||||
|
// First, set 3. Then add 1 more, which should be ignored.
|
||||||
|
mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13");
|
||||||
|
assertDynamicShortcutCountExceeded(() -> {
|
||||||
|
mManager.addDynamicShortcuts(list(s1_4, s2_1));
|
||||||
|
});
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13");
|
||||||
|
|
||||||
|
// Update existing one, which should work.
|
||||||
|
mManager.addDynamicShortcuts(list(makeShortcutWithActivityAndTitle(
|
||||||
|
"s11", a1, "xxx"), s2_1));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13", "s21");
|
||||||
|
assertEquals("xxx", getCallerShortcut("s11").getTitle());
|
||||||
|
|
||||||
|
// Make sure pinned shortcuts won't affect.
|
||||||
|
// - Pin s11 - s13, and remove all dynamic.
|
||||||
|
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||||
|
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s11", "s12", "s13"),
|
||||||
|
HANDLE_USER_0);
|
||||||
|
});
|
||||||
|
mManager.removeAllDynamicShortcuts();
|
||||||
|
|
||||||
|
assertEmpty(mManager.getDynamicShortcuts());
|
||||||
|
assertShortcutIds(mManager.getPinnedShortcuts(),
|
||||||
|
"s11", "s12", "s13");
|
||||||
|
|
||||||
|
// Then add dynamic.
|
||||||
|
mManager.addDynamicShortcuts(list(s1_4, s2_1, s2_2, s2_3));
|
||||||
|
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s14", "s21", "s22", "s23");
|
||||||
|
assertShortcutIds(mManager.getPinnedShortcuts(),
|
||||||
|
"s11", "s12", "s13");
|
||||||
|
|
||||||
|
// Adding "s11" and "s12" back, should work
|
||||||
|
mManager.addDynamicShortcuts(list(s1_1, s1_2));
|
||||||
|
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s14", "s11", "s12", "s21", "s22", "s23");
|
||||||
|
assertShortcutIds(mManager.getPinnedShortcuts(),
|
||||||
|
"s11", "s12", "s13");
|
||||||
|
|
||||||
|
// Adding back s13 doesn't work.
|
||||||
|
assertDynamicShortcutCountExceeded(() -> {
|
||||||
|
mManager.addDynamicShortcuts(list(s1_3));
|
||||||
|
});
|
||||||
|
|
||||||
|
assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a1),
|
||||||
|
"s11", "s12", "s14");
|
||||||
|
assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a2),
|
||||||
|
"s21", "s22", "s23");
|
||||||
|
|
||||||
|
// Now swap the activities.
|
||||||
|
mManager.updateShortcuts(list(
|
||||||
|
makeShortcutWithActivity("s11", a2),
|
||||||
|
makeShortcutWithActivity("s21", a1)));
|
||||||
|
|
||||||
|
assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a1),
|
||||||
|
"s21", "s12", "s14");
|
||||||
|
assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a2),
|
||||||
|
"s11", "s22", "s23");
|
||||||
|
|
||||||
|
// Now, test with 2 manifest shortcuts.
|
||||||
|
mManager.removeAllDynamicShortcuts();
|
||||||
|
addManifestShortcutResource(
|
||||||
|
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
|
||||||
|
R.xml.shortcut_2);
|
||||||
|
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||||
|
mService.mPackageMonitor.onReceive(getTestContext(),
|
||||||
|
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
|
||||||
|
|
||||||
|
assertEquals(2, mManager.getManifestShortcuts().size());
|
||||||
|
|
||||||
|
// Adding one shortcut to activity 1 works fine.
|
||||||
|
mManager.addDynamicShortcuts(list(s1_1, s2_1, s2_2, s2_3));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s21", "s22", "s23");
|
||||||
|
assertEquals(2, mManager.getManifestShortcuts().size());
|
||||||
|
|
||||||
|
// But adding one more doesn't.
|
||||||
|
assertDynamicShortcutCountExceeded(() -> {
|
||||||
|
mManager.addDynamicShortcuts(list(s1_4, s2_1));
|
||||||
|
});
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s21", "s22", "s23");
|
||||||
|
assertEquals(2, mManager.getManifestShortcuts().size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMaxShortcutCount_update() {
|
||||||
|
// Change the max number of shortcuts.
|
||||||
|
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
|
||||||
|
|
||||||
|
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||||
|
final ComponentName a1 = new ComponentName(mClientContext, ShortcutActivity.class);
|
||||||
|
final ComponentName a2 = new ComponentName(mClientContext, ShortcutActivity2.class);
|
||||||
|
final ShortcutInfo s1_1 = makeShortcutWithActivity("s11", a1);
|
||||||
|
final ShortcutInfo s1_2 = makeShortcutWithActivity("s12", a1);
|
||||||
|
final ShortcutInfo s1_3 = makeShortcutWithActivity("s13", a1);
|
||||||
|
final ShortcutInfo s1_4 = makeShortcutWithActivity("s14", a1);
|
||||||
|
final ShortcutInfo s1_5 = makeShortcutWithActivity("s15", a1);
|
||||||
|
final ShortcutInfo s2_1 = makeShortcutWithActivity("s21", a2);
|
||||||
|
final ShortcutInfo s2_2 = makeShortcutWithActivity("s22", a2);
|
||||||
|
final ShortcutInfo s2_3 = makeShortcutWithActivity("s23", a2);
|
||||||
|
final ShortcutInfo s2_4 = makeShortcutWithActivity("s24", a2);
|
||||||
|
|
||||||
|
// 3 shortcuts for 2 activities -> okay
|
||||||
|
mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13", "s21", "s22", "s23");
|
||||||
|
|
||||||
|
// Trying to move s11 from a1 to a2 should fail.
|
||||||
|
assertDynamicShortcutCountExceeded(() -> {
|
||||||
|
mManager.updateShortcuts(list(makeShortcutWithActivity("s11", a2)));
|
||||||
|
});
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13", "s21", "s22", "s23");
|
||||||
|
|
||||||
|
// Trying to move s21 from a2 to a1 should also fail.
|
||||||
|
assertDynamicShortcutCountExceeded(() -> {
|
||||||
|
mManager.updateShortcuts(list(makeShortcutWithActivity("s21", a1)));
|
||||||
|
});
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13", "s21", "s22", "s23");
|
||||||
|
|
||||||
|
// But, if we do these two at the same time, it should work.
|
||||||
|
mManager.updateShortcuts(list(
|
||||||
|
makeShortcutWithActivity("s11", a2),
|
||||||
|
makeShortcutWithActivity("s21", a1)));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13", "s21", "s22", "s23");
|
||||||
|
assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a1),
|
||||||
|
"s21", "s12", "s13");
|
||||||
|
assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a2),
|
||||||
|
"s11", "s22", "s23");
|
||||||
|
|
||||||
|
// Then reset.
|
||||||
|
mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s11", "s12", "s13", "s21", "s22", "s23");
|
||||||
|
|
||||||
|
// Pin some to have more shortcuts for a1.
|
||||||
|
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||||
|
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s11", "s12", "s13"),
|
||||||
|
HANDLE_USER_0);
|
||||||
|
});
|
||||||
|
mManager.setDynamicShortcuts(list(s1_4, s1_5, s2_1, s2_2, s2_3));
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s14", "s15", "s21", "s22", "s23");
|
||||||
|
assertShortcutIds(mManager.getPinnedShortcuts(),
|
||||||
|
"s11", "s12", "s13");
|
||||||
|
|
||||||
|
// a1 already has 2 dynamic shortcuts (and 3 pinned shortcuts that used to belong on it)
|
||||||
|
// But that doesn't matter for update -- the following should still work.
|
||||||
|
mManager.updateShortcuts(list(
|
||||||
|
makeShortcutWithActivityAndTitle("s11", a1, "xxx1"),
|
||||||
|
makeShortcutWithActivityAndTitle("s12", a1, "xxx2"),
|
||||||
|
makeShortcutWithActivityAndTitle("s13", a1, "xxx3"),
|
||||||
|
makeShortcutWithActivityAndTitle("s14", a1, "xxx4"),
|
||||||
|
makeShortcutWithActivityAndTitle("s15", a1, "xxx5")));
|
||||||
|
// All the shortcuts should still exist they all belong on same activities,
|
||||||
|
// with the updated titles.
|
||||||
|
assertShortcutIds(mManager.getDynamicShortcuts(),
|
||||||
|
"s14", "s15", "s21", "s22", "s23");
|
||||||
|
assertShortcutIds(mManager.getPinnedShortcuts(),
|
||||||
|
"s11", "s12", "s13");
|
||||||
|
|
||||||
|
assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a1),
|
||||||
|
"s14", "s15");
|
||||||
|
assertShortcutIds(filterByActivity(mManager.getDynamicShortcuts(), a2),
|
||||||
|
"s21", "s22", "s23");
|
||||||
|
|
||||||
|
assertEquals("xxx1", getCallerShortcut("s11").getTitle());
|
||||||
|
assertEquals("xxx2", getCallerShortcut("s12").getTitle());
|
||||||
|
assertEquals("xxx3", getCallerShortcut("s13").getTitle());
|
||||||
|
assertEquals("xxx4", getCallerShortcut("s14").getTitle());
|
||||||
|
assertEquals("xxx5", getCallerShortcut("s15").getTitle());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testShortcutsPushedOutByManifest() {
|
||||||
|
// Change the max number of shortcuts.
|
||||||
|
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
|
||||||
|
|
||||||
|
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||||
|
final ComponentName a1 = new ComponentName(mClientContext, ShortcutActivity.class);
|
||||||
|
final ComponentName a2 = new ComponentName(mClientContext, ShortcutActivity2.class);
|
||||||
|
final ShortcutInfo s1_1 = makeShortcutWithActivityAndRank("s11", a1, 4);
|
||||||
|
final ShortcutInfo s1_2 = makeShortcutWithActivityAndRank("s12", a1, 3);
|
||||||
|
final ShortcutInfo s1_3 = makeShortcutWithActivityAndRank("s13", a1, 2);
|
||||||
|
final ShortcutInfo s1_4 = makeShortcutWithActivityAndRank("s14", a1, 1);
|
||||||
|
final ShortcutInfo s1_5 = makeShortcutWithActivityAndRank("s15", a1, 0);
|
||||||
|
final ShortcutInfo s2_1 = makeShortcutWithActivityAndRank("s21", a2, 0);
|
||||||
|
final ShortcutInfo s2_2 = makeShortcutWithActivityAndRank("s22", a2, 1);
|
||||||
|
final ShortcutInfo s2_3 = makeShortcutWithActivityAndRank("s23", a2, 2);
|
||||||
|
final ShortcutInfo s2_4 = makeShortcutWithActivityAndRank("s24", a2, 3);
|
||||||
|
final ShortcutInfo s2_5 = makeShortcutWithActivityAndRank("s25", a2, 4);
|
||||||
|
|
||||||
|
// Initial state.
|
||||||
|
mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3, s2_1, s2_2, s2_3));
|
||||||
|
runWithCaller(LAUNCHER_1, USER_0, () -> {
|
||||||
|
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s11", "s12", "s21", "s22"),
|
||||||
|
HANDLE_USER_0);
|
||||||
|
});
|
||||||
|
mManager.setDynamicShortcuts(list(s1_2, s1_3, s1_4, s2_2, s2_3, s2_4));
|
||||||
|
assertShortcutIds(assertAllEnabled(mManager.getDynamicShortcuts()),
|
||||||
|
"s12", "s13", "s14",
|
||||||
|
"s22", "s23", "s24");
|
||||||
|
assertShortcutIds(assertAllEnabled(mManager.getPinnedShortcuts()),
|
||||||
|
"s11", "s12",
|
||||||
|
"s21", "s22");
|
||||||
|
|
||||||
|
// Add 1 manifest shortcut to a1.
|
||||||
|
addManifestShortcutResource(
|
||||||
|
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
|
||||||
|
R.xml.shortcut_1);
|
||||||
|
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||||
|
mService.mPackageMonitor.onReceive(getTestContext(),
|
||||||
|
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
|
||||||
|
assertEquals(1, mManager.getManifestShortcuts().size());
|
||||||
|
|
||||||
|
// s12 removed.
|
||||||
|
assertShortcutIds(assertAllEnabled(mManager.getDynamicShortcuts()),
|
||||||
|
"s13", "s14",
|
||||||
|
"s22", "s23", "s24");
|
||||||
|
assertShortcutIds(assertAllEnabled(mManager.getPinnedShortcuts()),
|
||||||
|
"s11", "s12",
|
||||||
|
"s21", "s22");
|
||||||
|
|
||||||
|
// Add more manifest shortcuts.
|
||||||
|
addManifestShortcutResource(
|
||||||
|
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
|
||||||
|
R.xml.shortcut_2);
|
||||||
|
addManifestShortcutResource(
|
||||||
|
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
|
||||||
|
R.xml.shortcut_1_alt);
|
||||||
|
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||||
|
mService.mPackageMonitor.onReceive(getTestContext(),
|
||||||
|
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
|
||||||
|
assertEquals(3, mManager.getManifestShortcuts().size());
|
||||||
|
|
||||||
|
// Note the ones with the highest rank values (== least important) will be removed.
|
||||||
|
assertShortcutIds(assertAllEnabled(mManager.getDynamicShortcuts()),
|
||||||
|
"s14",
|
||||||
|
"s22", "s23");
|
||||||
|
assertShortcutIds(assertAllEnabled(mManager.getPinnedShortcuts()),
|
||||||
|
"s11", "s12",
|
||||||
|
"s21", "s22");
|
||||||
|
|
||||||
|
// Add more manifest shortcuts.
|
||||||
|
addManifestShortcutResource(
|
||||||
|
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
|
||||||
|
R.xml.shortcut_2);
|
||||||
|
addManifestShortcutResource(
|
||||||
|
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
|
||||||
|
R.xml.shortcut_5_alt); // manifest has 5, but max is 3, so a2 will have 3.
|
||||||
|
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||||
|
mService.mPackageMonitor.onReceive(getTestContext(),
|
||||||
|
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
|
||||||
|
assertEquals(5, mManager.getManifestShortcuts().size());
|
||||||
|
|
||||||
|
assertShortcutIds(assertAllEnabled(mManager.getDynamicShortcuts()),
|
||||||
|
"s14" // a1 has 1 dynamic
|
||||||
|
); // a2 has no dynamic
|
||||||
|
assertShortcutIds(assertAllEnabled(mManager.getPinnedShortcuts()),
|
||||||
|
"s11", "s12",
|
||||||
|
"s21", "s22");
|
||||||
|
|
||||||
|
// Update, no manifest shortucts. This doesn't affect anything.
|
||||||
|
addManifestShortcutResource(
|
||||||
|
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
|
||||||
|
R.xml.shortcut_0);
|
||||||
|
addManifestShortcutResource(
|
||||||
|
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
|
||||||
|
R.xml.shortcut_0);
|
||||||
|
updatePackageVersion(CALLING_PACKAGE_1, 1);
|
||||||
|
mService.mPackageMonitor.onReceive(getTestContext(),
|
||||||
|
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
|
||||||
|
assertEquals(0, mManager.getManifestShortcuts().size());
|
||||||
|
|
||||||
|
assertShortcutIds(assertAllEnabled(mManager.getDynamicShortcuts()),
|
||||||
|
"s14");
|
||||||
|
assertShortcutIds(assertAllEnabled(mManager.getPinnedShortcuts()),
|
||||||
|
"s11", "s12",
|
||||||
|
"s21", "s22");
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.times;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import android.app.Instrumentation;
|
import android.app.Instrumentation;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.LauncherApps;
|
import android.content.pm.LauncherApps;
|
||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
@@ -38,6 +39,7 @@ import android.graphics.Bitmap;
|
|||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.os.BaseBundle;
|
import android.os.BaseBundle;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Parcel;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.test.MoreAsserts;
|
import android.test.MoreAsserts;
|
||||||
@@ -252,6 +254,17 @@ public class ShortcutManagerTestUtils {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<ShortcutInfo> filterByActivity(List<ShortcutInfo> list,
|
||||||
|
ComponentName activity) {
|
||||||
|
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
|
||||||
|
for (ShortcutInfo si : list) {
|
||||||
|
if (si.getActivity().equals(activity) && (si.isManifestShortcut() || si.isDynamic())) {
|
||||||
|
ret.add(si);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
|
public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
|
||||||
String expectedExceptionMessageRegex, Runnable r) {
|
String expectedExceptionMessageRegex, Runnable r) {
|
||||||
assertExpectException("", expectedExceptionType, expectedExceptionMessageRegex, r);
|
assertExpectException("", expectedExceptionType, expectedExceptionMessageRegex, r);
|
||||||
@@ -555,6 +568,27 @@ public class ShortcutManagerTestUtils {
|
|||||||
}, "Shortcut IDs=[" + Arrays.toString(ids) + "]", null);
|
}, "Shortcut IDs=[" + Arrays.toString(ids) + "]", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ShortcutInfo parceled(ShortcutInfo si) {
|
||||||
|
Parcel p = Parcel.obtain();
|
||||||
|
p.writeParcelable(si, 0);
|
||||||
|
p.setDataPosition(0);
|
||||||
|
ShortcutInfo si2 = p.readParcelable(ShortcutManagerTestUtils.class.getClassLoader());
|
||||||
|
p.recycle();
|
||||||
|
return si2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ShortcutInfo> cloneShortcutList(List<ShortcutInfo> list) {
|
||||||
|
if (list == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final List<ShortcutInfo> ret = new ArrayList<>(list.size());
|
||||||
|
for (ShortcutInfo si : list) {
|
||||||
|
ret.add(parceled(si));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
public static void waitUntil(String message, BooleanSupplier condition) {
|
public static void waitUntil(String message, BooleanSupplier condition) {
|
||||||
waitUntil(message, condition, STANDARD_TIMEOUT_SEC);
|
waitUntil(message, condition, STANDARD_TIMEOUT_SEC);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user