添加桌面快捷方式

添加方式:

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;

使用新的方式:用ShortcutManagerrequestPinShortcut来创建快捷方式

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 较好

results for ""

    No results matching ""