下面列出了android.content.pm.ShortcutInfo#getActivity() 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
/**
* 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 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();
if (activity == null) {
mShortcutUser.mService.wtf("null activity detected.");
continue;
}
ArrayList<ShortcutInfo> list = activitiesToShortcuts.get(activity);
if (list == null) {
list = new ArrayList<>();
activitiesToShortcuts.put(activity, list);
}
list.add(si);
}
return activitiesToShortcuts;
}
/**
* When a shortcut has no target activity, set the default one from the package.
*/
private void fillInDefaultActivity(List<ShortcutInfo> shortcuts) {
ComponentName defaultActivity = null;
for (int i = shortcuts.size() - 1; i >= 0; i--) {
final ShortcutInfo si = shortcuts.get(i);
if (si.getActivity() == null) {
if (defaultActivity == null) {
defaultActivity = injectGetDefaultMainActivity(
si.getPackage(), si.getUserId());
Preconditions.checkState(defaultActivity != null,
"Launcher activity not found for package " + si.getPackage());
}
si.setActivity(defaultActivity);
}
}
}
/**
* @return false if any of the target activities are no longer enabled.
*/
private boolean areAllActivitiesStillEnabled() {
if (mShortcuts.size() == 0) {
return true;
}
final ShortcutService s = mShortcutUser.mService;
// Normally the number of target activities is 1 or so, so no need to use a complex
// structure like a set.
final ArrayList<ComponentName> checked = new ArrayList<>(4);
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
final ShortcutInfo si = mShortcuts.valueAt(i);
final ComponentName activity = si.getActivity();
if (checked.contains(activity)) {
continue; // Already checked.
}
checked.add(activity);
if ((activity != null)
&& !s.injectIsActivityEnabledAndExported(activity, getOwnerUserId())) {
return false;
}
}
return true;
}
/**
* Clean up / validate an incoming shortcut.
* - Make sure all mandatory fields are set.
* - Make sure the intent's extras are persistable, and them to set
* {@link ShortcutInfo#mIntentPersistableExtrases}. Also clear its extras.
* - Clear flags.
*/
private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate,
boolean forPinRequest) {
if (shortcut.isReturnedByServer()) {
Log.w(TAG,
"Re-publishing ShortcutInfo returned by server is not supported."
+ " Some information such as icon may lost from shortcut.");
}
Preconditions.checkNotNull(shortcut, "Null shortcut detected");
if (shortcut.getActivity() != null) {
Preconditions.checkState(
shortcut.getPackage().equals(shortcut.getActivity().getPackageName()),
"Cannot publish shortcut: activity " + shortcut.getActivity() + " does not"
+ " belong to package " + shortcut.getPackage());
Preconditions.checkState(
injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()),
"Cannot publish shortcut: activity " + shortcut.getActivity() + " is not"
+ " main activity");
}
if (!forUpdate) {
shortcut.enforceMandatoryFields(/* forPinned= */ forPinRequest);
if (!forPinRequest) {
Preconditions.checkState(shortcut.getActivity() != null,
"Cannot publish shortcut: target activity is not set");
}
}
if (shortcut.getIcon() != null) {
ShortcutInfo.validateIcon(shortcut.getIcon());
}
shortcut.replaceFlags(0);
}
private void addShortcutToActionPopup(final LauncherApps launcherApps, final ShortcutInfo shortcutInfo) {
if (Build.VERSION.SDK_INT>=25) {
if (shortcutInfo != null && shortcutInfo.getActivity() != null) {
//Log.d(TAG, shortcutInfo.getShortLabel() + " " + shortcutInfo.getActivity().getClassName());
if (shortcutInfo.isEnabled()) {
String label = "";
if (shortcutInfo.getShortLabel() != null)
label += shortcutInfo.getShortLabel();
if (shortcutInfo.getLongLabel() != null && !label.contentEquals(shortcutInfo.getLongLabel()))
label = shortcutInfo.getLongLabel() + "";
Drawable icon = launcherApps.getShortcutIconDrawable(shortcutInfo, DisplayMetrics.DENSITY_DEFAULT);
addActionMenuItem(label.trim(), icon, new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT >= 25) {
try {
launcherApps.startShortcut(shortcutInfo, null, null);
} catch (Exception e) {
Log.e(TAG, "Couldn't Launch shortcut", e);
}
}
dismissActionPopup();
}
});
}
}
}
}
/**
* Handle {@link android.content.pm.ShortcutManager#requestPinShortcut)}.
*/
@NonNull
private PinItemRequest requestPinShortcutLocked(ShortcutInfo inShortcut,
IntentSender resultIntentOriginal, Pair<ComponentName, Integer> confirmActivity) {
final ShortcutPackage ps = mService.getPackageShortcutsForPublisherLocked(
inShortcut.getPackage(), inShortcut.getUserId());
final ShortcutInfo existing = ps.findShortcutById(inShortcut.getId());
final boolean existsAlready = existing != null;
final boolean existingIsVisible = existsAlready && existing.isVisibleToPublisher();
if (DEBUG) {
Slog.d(TAG, "requestPinnedShortcut: package=" + inShortcut.getPackage()
+ " existsAlready=" + existsAlready
+ " existingIsVisible=" + existingIsVisible
+ " shortcut=" + inShortcut.toInsecureString());
}
// This is the shortcut that'll be sent to the launcher.
final ShortcutInfo shortcutForLauncher;
final String launcherPackage = confirmActivity.first.getPackageName();
final int launcherUserId = confirmActivity.second;
IntentSender resultIntentToSend = resultIntentOriginal;
if (existsAlready) {
validateExistingShortcut(existing);
final boolean isAlreadyPinned = mService.getLauncherShortcutsLocked(
launcherPackage, existing.getUserId(), launcherUserId).hasPinned(existing);
if (isAlreadyPinned) {
// When the shortcut is already pinned by this launcher, the request will always
// succeed, so just send the result at this point.
sendResultIntent(resultIntentOriginal, null);
// So, do not send the intent again.
resultIntentToSend = null;
}
// Pass a clone, not the original.
// Note this will remove the intent and icons.
shortcutForLauncher = existing.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
if (!isAlreadyPinned) {
// FLAG_PINNED may still be set, if it's pinned by other launchers.
shortcutForLauncher.clearFlags(ShortcutInfo.FLAG_PINNED);
}
} else {
// If the shortcut has no default activity, try to set the main activity.
// But in the request-pin case, it's optional, so it's okay even if the caller
// has no default activity.
if (inShortcut.getActivity() == null) {
inShortcut.setActivity(mService.injectGetDefaultMainActivity(
inShortcut.getPackage(), inShortcut.getUserId()));
}
// It doesn't exist, so it must have all mandatory fields.
mService.validateShortcutForPinRequest(inShortcut);
// Initialize the ShortcutInfo for pending approval.
inShortcut.resolveResourceStrings(mService.injectGetResourcesForApplicationAsUser(
inShortcut.getPackage(), inShortcut.getUserId()));
if (DEBUG) {
Slog.d(TAG, "Resolved shortcut=" + inShortcut.toInsecureString());
}
// We should strip out the intent, but should preserve the icon.
shortcutForLauncher = inShortcut.clone(
ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER_APPROVAL);
}
if (DEBUG) {
Slog.d(TAG, "Sending to launcher=" + shortcutForLauncher.toInsecureString());
}
// Create a request object.
final PinShortcutRequestInner inner =
new PinShortcutRequestInner(this, inShortcut, shortcutForLauncher,
resultIntentToSend, launcherPackage, launcherUserId,
mService.injectGetPackageUid(launcherPackage, launcherUserId),
existsAlready);
return new PinItemRequest(inner, PinItemRequest.REQUEST_TYPE_SHORTCUT);
}
/**
* The last step of the "request pin shortcut" flow. Called when the launcher accepted a
* request.
*/
public boolean directPinShortcut(PinShortcutRequestInner request) {
final ShortcutInfo original = request.shortcutOriginal;
final int appUserId = original.getUserId();
final String appPackageName = original.getPackage();
final int launcherUserId = request.launcherUserId;
final String launcherPackage = request.launcherPackage;
final String shortcutId = original.getId();
synchronized (mLock) {
if (!(mService.isUserUnlockedL(appUserId)
&& mService.isUserUnlockedL(request.launcherUserId))) {
Log.w(TAG, "User is locked now.");
return false;
}
final ShortcutLauncher launcher = mService.getLauncherShortcutsLocked(
launcherPackage, appUserId, launcherUserId);
launcher.attemptToRestoreIfNeededAndSave();
if (launcher.hasPinned(original)) {
if (DEBUG) {
Slog.d(TAG, "Shortcut " + original + " already pinned."); // This too.
}
return true;
}
final ShortcutPackage ps = mService.getPackageShortcutsForPublisherLocked(
appPackageName, appUserId);
final ShortcutInfo current = ps.findShortcutById(shortcutId);
// The shortcut might have been changed, so we need to do the same validation again.
try {
if (current == null) {
// It doesn't exist, so it must have all necessary fields.
mService.validateShortcutForPinRequest(original);
} else {
validateExistingShortcut(current);
}
} catch (RuntimeException e) {
Log.w(TAG, "Unable to pin shortcut: " + e.getMessage());
return false;
}
// If the shortcut doesn't exist, need to create it.
// First, create it as a dynamic shortcut.
if (current == null) {
if (DEBUG) {
Slog.d(TAG, "Temporarily adding " + shortcutId + " as dynamic");
}
// Add as a dynamic shortcut. In order for a shortcut to be dynamic, it must
// have a target activity, so we set a dummy here. It's later removed
// in deleteDynamicWithId().
if (original.getActivity() == null) {
original.setActivity(mService.getDummyMainActivity(appPackageName));
}
ps.addOrReplaceDynamicShortcut(original);
}
// Pin the shortcut.
if (DEBUG) {
Slog.d(TAG, "Pinning " + shortcutId);
}
launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId,
/*forPinRequest=*/ true);
if (current == null) {
if (DEBUG) {
Slog.d(TAG, "Removing " + shortcutId + " as dynamic");
}
ps.deleteDynamicWithId(shortcutId, /*ignoreInvisible=*/ false);
}
ps.adjustRanks(); // Shouldn't be needed, but just in case.
}
mService.verifyStates();
mService.packageShortcutsChanged(appPackageName, appUserId);
return true;
}
/**
* 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("Activity must not be null at this point");
continue; // Just ignore this invalid case.
}
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));
}
}