添加方式:
old方式 8.0以下
new方式 8.0及其以上
old方式:7.1及其以下通过发送action=com.android.launcher.action.INSTALL_SHORTCUT的广播来添加
快捷方式到桌面
代码操作:
public static final String ACTION_ADD_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT";
public void addShortcutBelowAndroidN(Context context) {
Intent addShortcutIntent = new Intent(ACTION_ADD_SHORTCUT);
// 不允许重复创建,不是根据快捷方式的名字判断重复的
addShortcutIntent.putExtra("duplicate", false);
addShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "Shortcut Name");
//图标
addShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_shortcut));
// 设置关联程序
Intent launcherIntent = new Intent();
launcherIntent.setClass(context, ShortcutActivity.class);
addShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launcherIntent);
// 发送广播
context.sendBroadcast(addShortcutIntent);
}
背后的执行逻辑:(有待进一步分析)
new方式:
在Android8.0以上,以前通过发送action=com.android.launcher.action.INSTALL_SHORTCUT的方式已经行不通了,
因为action=com.android.launcher.action.INSTALL_SHORTCUT已经在系统代码中被屏蔽了
ActivityManagerService.java->broadcastIntentLocked方法
case "com.android.launcher.action.INSTALL_SHORTCUT":
// As of O, we no longer support this broadcasts, even for pre-O apps.
// Apps should now be using ShortcutManager.pinRequestShortcut().
Log.w(TAG, "Broadcast " + action
+ " no longer supported. It will not be delivered.");
return ActivityManager.BROADCAST_SUCCESS;
使用新的方式:用ShortcutManager的requestPinShortcut来创建快捷方式
public static void addShortCut(Context context) {
ShortcutManager shortcutManager = (ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
if (shortcutManager.isRequestPinShortcutSupported()) {//确定系统的luancher支持该种方式的添加
Intent shortcutInfoIntent = new Intent(context, ShortcutActivity.class);
shortcutInfoIntent.setAction(Intent.ACTION_VIEW); //action必须设置,不然报错
ShortcutInfo info = new ShortcutInfo.Builder(context, "The only id")
.setIcon(Icon.createWithResource(context, R.mipmap.ic_shortcut))
.setShortLabel("Short Label")
.setIntent(shortcutInfoIntent)
.build();
//当添加快捷方式的确认弹框弹出来时,将被回调
PendingIntent shortcutCallbackIntent = PendingIntent.getBroadcast(context, 0, new Intent(context, MyReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
shortcutManager.requestPinShortcut(info, shortcutCallbackIntent.getIntentSender());
}
}
requestPinShortcut方式的调用必须是前台的activity或者前台的service,不然会报IllegalStateException
当然还有一种兼容的处理方式:
/**
* 使用ShortcutManagerCompat添加桌面快捷方式
* @param context
*/
public static void addShortCutCompact(Context context) {
if (ShortcutManagerCompat.isRequestPinShortcutSupported(context)) {
Intent shortcutInfoIntent = new Intent(context, ShortcutActivity.class);
shortcutInfoIntent.setAction(Intent.ACTION_VIEW); //action必须设置,不然报错
ShortcutInfoCompat info = new ShortcutInfoCompat.Builder(context, "The only id")
.setIcon(R.mipmap.ic_shortcut)
.setShortLabel("Short Label")
.setIntent(shortcutInfoIntent)
.build();
//当添加快捷方式的确认弹框弹出来时,将被回调
PendingIntent shortcutCallbackIntent = PendingIntent.getBroadcast(context, 0, new Intent(context, MyReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
ShortcutManagerCompat.requestPinShortcut(context, info, shortcutCallbackIntent.getIntentSender());
}
}
上面讲述了两种创建快捷方式的方法,接下来深入分析下,两种创建方法在framework层是怎样运作的
old方式:
直接方法带“com.android.laucnher.action.INSTALL_SHORTCUT”的广播,
那么就当从context.sendBroadcast说起
context.sendBroadcast->ContextImpl.sendBroadcast-->ActivityManager.getService().broadcastIntent
->ActivityManagerService.broadcastIntent->broadcastIntentLocked
将广播发送出去,在添加了“com.android.laucnher.action.INSTALL_SHORTCUT”过滤接收的reciever中处理
new方式:
ShortcutManager.requestPinShortcut->ShortcutService.requestPinShortcut
在requestPinShortcut中1)会校验当前uid的状态Preconditions.checkState;2)发送request到launcher,ShortcutRequestPinProcessor.requestPinItemLocked
requestPinItemLocked中
public boolean requestPinItemLocked(ShortcutInfo inShortcut, AppWidgetProviderInfo inAppWidget,
Bundle extras, int userId, IntentSender resultIntent) {
// First, make sure the launcher supports it.
// Find the confirmation activity in the default launcher.
//确定是shortcut还是app图标
final int requestType = inShortcut != null ?
PinItemRequest.REQUEST_TYPE_SHORTCUT : PinItemRequest.REQUEST_TYPE_APPWIDGET;
final Pair<ComponentName, Integer> confirmActivity =
getRequestPinConfirmationActivity(userId, requestType);
// If the launcher doesn't support it, just return a rejected result and finish.
if (confirmActivity == null) {
Log.w(TAG, "Launcher doesn't support requestPinnedShortcut(). Shortcut not created.");
return false;
}
final int launcherUserId = confirmActivity.second;
// Make sure the launcher user is unlocked. (it's always the parent profile, so should
// really be unlocked here though.)
mService.throwIfUserLockedL(launcherUserId);
// Next, validate the incoming shortcut, etc.
final PinItemRequest request;
if (inShortcut != null) {
request = requestPinShortcutLocked(inShortcut, resultIntent, confirmActivity);
} else {
int launcherUid = mService.injectGetPackageUid(
confirmActivity.first.getPackageName(), launcherUserId);
request = new PinItemRequest(
new PinAppWidgetRequestInner(this, resultIntent, launcherUid, inAppWidget,
extras),
PinItemRequest.REQUEST_TYPE_APPWIDGET);
}
return startRequestConfirmActivity(confirmActivity.first, launcherUserId, request,
requestType);
}
1)当inShortcut不为空时,执行requestPinShortcutLocked
2)当inShortcut为空时,
1)、2)都会创建出PinItemRequest
startRequestConfirmActivity
启动activity,并且该activity是可以处理LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT,
并且会在PinItemRequest的信息封装在Intent中进行传递
private boolean startRequestConfirmActivity(ComponentName activity, int launcherUserId,
PinItemRequest request, int requestType) {
final String action = requestType == LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT ?
LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT :
LauncherApps.ACTION_CONFIRM_PIN_APPWIDGET;
// Start the activity.
final Intent confirmIntent = new Intent(action);
confirmIntent.setComponent(activity);
confirmIntent.putExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST, request);
confirmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
final long token = mService.injectClearCallingIdentity();
try {
mService.mContext.startActivityAsUser(
confirmIntent, UserHandle.of(launcherUserId));
} catch (RuntimeException e) { // ActivityNotFoundException, etc.
Log.e(TAG, "Unable to start activity " + activity, e);
return false;
} finally {
mService.injectRestoreCallingIdentity(token);
}
return true;
}
另外分析:isRequestPinShortcutSupported
确定系统是否支持添加PinShortcut
ShortcutManager.isRequestPinShortcutSupported
public boolean isRequestPinShortcutSupported() {
try {
return mService.isRequestPinItemSupported(injectMyUserId(),
LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
跳转到ShortcutService
public boolean isRequestPinItemSupported(int callingUserId, int requestType) {
final long token = injectClearCallingIdentity();
try {
return mShortcutRequestPinProcessor
.isRequestPinItemSupported(callingUserId, requestType);
} finally {
injectRestoreCallingIdentity(token);
}
}
/**
* Find the activity that handles {@link LauncherApps#ACTION_CONFIRM_PIN_SHORTCUT} in the
* default launcher.
*/
Pair<ComponentName, Integer> getRequestPinConfirmationActivity(
int callingUserId, int requestType) {
// Find the default launcher.
final int launcherUserId = mService.getParentOrSelfUserId(callingUserId);
final ComponentName defaultLauncher = mService.getDefaultLauncher(launcherUserId);
if (defaultLauncher == null) {
Log.e(TAG, "Default launcher not found.");
return null;
}
final ComponentName activity = mService.injectGetPinConfirmationActivity(
defaultLauncher.getPackageName(), launcherUserId, requestType);
return (activity == null) ? null : Pair.create(activity, launcherUserId);
}
ComponentName injectGetPinConfirmationActivity(@NonNull String launcherPackageName,
int launcherUserId, int requestType) {
Preconditions.checkNotNull(launcherPackageName);
String action = requestType == LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT ?
LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT :
LauncherApps.ACTION_CONFIRM_PIN_APPWIDGET;
final Intent confirmIntent = new Intent(action).setPackage(launcherPackageName);
final List<ResolveInfo> candidates = queryActivities(
confirmIntent, launcherUserId, /* exportedOnly =*/ false);
for (ResolveInfo ri : candidates) {
return ri.activityInfo.getComponentName();
}
return null;
}
查询能够处理action=LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT的默认launcher
List<ResolveInfo> queryActivities(@NonNull Intent intent, int userId,
boolean exportedOnly) {
final List<ResolveInfo> resolved;
final long token = injectClearCallingIdentity();
try {
resolved =
mContext.getPackageManager().queryIntentActivitiesAsUser(
intent, PACKAGE_MATCH_FLAGS, userId);
} finally {
injectRestoreCallingIdentity(token);
}
if (resolved == null || resolved.size() == 0) {
return EMPTY_RESOLVE_INFO;
}
// Make sure the package is installed.
if (!isInstalled(resolved.get(0).activityInfo)) {
return EMPTY_RESOLVE_INFO;
}
if (exportedOnly) {
resolved.removeIf(ACTIVITY_NOT_EXPORTED);
}
return resolved;
}
一些参考资料:
http://blog.csdn.net/rentee/article/details/77005547
http://blog.csdn.net/qibin0506/article/details/52878690
https://github.com/OptimusPrimeRen/Android-O-Adaptive
http://blog.csdn.net/u012846789/article/details/77937620?locationNum=3&fps=1
http://blog.csdn.net/huanghuangjin/article/details/78275991 较好