12/11
2016

三方桌面支持 Android SDK 7.1 新特性 Shortcuts

Android 7.1 允许 App 自定义 Shortcuts,类似 iOS 的 3D touch。通过在桌面长按 App 弹出 Shortcut 列表,点击某个 Shortcut 快速进入某项操作,同时 Shortcut 可以拖动到桌面进行固定。

 

目前仅 7.1 系统桌面支持该特性,三方桌面需要通过LauncherApps这个 API 支持此功能,本文主要介绍三方桌面如何接入此特性。
可以下载ShortcutViewer查看效果:Google Play应用宝。截图如下:
Android Shortcuts Demo
关于 Shortcuts 的全面介绍可见:
第一篇:Android 7.1 新特性 Shortcuts 介绍
第二篇:Android 7.1 新特性 Shortcuts 一些实践和目前的问题
如果不了解 Shortcuts 基本使用建议先看上面第一篇。

 

1. Manifest 支持 Home category

在 AndroidManifest.xml 的 Main Launcher 对应的 Activity 内添加android.intent.category.HOME这个 category,表示此应用为桌面,如下:

<application
    ……>
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>

            <category android:name="android.intent.category.LAUNCHER"/>
            <category android:name="android.intent.category.HOME" />
        </intent-filter>
    </activity>
</application>

必须在 Main Launcher 对应的 Activity 内设置,其中android.intent.category.HOME表示这是个桌面程序。

 

2. 设置为默认桌面

Android SDK LauncherAppsAPI 要求必须是默认桌面才有权限获取到所有应用 Shortcuts 信息。

 

按 Home 键退到后台会提示是否将此应用设置为桌面,选择”始终”(某些手机可能是默认)。
或者通过设置-应用-配置应用-主屏幕应用,选择自己的应用作为默认桌面。

 

3. 获取各个 App 的所有 Shortcuts 信息

通过LauncherApps.getShortcuts获取各 App Shortcuts 信息:

LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
if (!launcherApps.hasShortcutHostPermission()) {
    // Don't have permission, you may need set this app as default desktop.
    return;
}

PackageManager packageManager = context.getPackageManager();
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveInfoList;
if (packageManager == null || CollectionUtils
        .isEmpty(resolveInfoList = packageManager.queryIntentActivities(mainIntent, 0))) {
    // No Main&Launcher Activity
    return;
}

// Get ShortcutInfo for every app
Set<String> packageNameSet = new HashSet<>();
for (ResolveInfo info : resolveInfoList) {
    ApplicationInfo applicationInfo;
    if (info == null || info.activityInfo == null
            || (applicationInfo = info.activityInfo.applicationInfo) == null || !applicationInfo.enabled
            || packageNameSet.contains(applicationInfo.packageName)) {
        continue;
    }
    packageNameSet.add(applicationInfo.packageName);

    int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_MANIFEST
            | ShortcutQuery.FLAG_MATCH_PINNED;
    List<ShortcutInfo> shortcutInfoList = launcherApps.getShortcuts(
            new ShortcutQuery().setPackage(applicationInfo.packageName).setQueryFlags(queryFlags),
            UserHandle.getUserHandleForUid(applicationInfo.uid));
    ……
}

上面主要步骤包括:
(1) 通过LauncherApps.hasShortcutHostPermission()判断是否拥有获取 shortcuts 信息的权限;
(2) 通过PackageManager.queryIntentActivities(…)得到所有已安装应用,并且含有 ACTION_MAIN&CATEGORY_LAUNCHER Intent 的ResolveInfo
(3) 遍历每个符合条件的ResolveInfo,通过LauncherApps.getShortcuts(…)得到其 shortcuts 信息。

 

注意:这里也可以通过其他方式得到所有适合在桌面显示的ApplicationInfo,而通过PackageManager.queryIntentActivities(…)是性能最优的方式。

 

int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_MANIFEST
        | ShortcutQuery.FLAG_MATCH_PINNED;
List<ShortcutInfo> shortcutInfoList = launcherApps.getShortcuts(
        new ShortcutQuery().setPackage(applicationInfo.packageName).setQueryFlags(queryFlags),
        UserHandle.getUserHandleForUid(applicationInfo.uid));

LauncherApps.getShortcuts(LauncherApps.ShortcutQuery query, UserHandle user)的两个参数分别表示查询条件和查询的 App 对应的 UserHandle。
上面queryFlags表示同时匹配动态 Shortcuts、静态 Shortcuts、固定的 Shortcuts。

 

当然这个特性仅对 Android SDK 7.1 及以上才有效,所以最好先判断下系统 API 版本才开始调用LauncherApps相关 API。

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

6 thoughts on “三方桌面支持 Android SDK 7.1 新特性 Shortcuts

  1. I was looking at your website and noticed it appears the word “canot” is spelled wrong. I had similar problems on my site until someone mentioned it to me and I also now use software from SpellPerfect.com to keep my site error free.

  2. It looks like you have a couple spelling errors on your website such as the word “canot”. Check out a service like SpellAce.com to help. We’ve used it in the past and liked it.

  3. Greetings,

    I’m not the best speller but I see the word “canot” is spelled incorrectly on your website. In the past I’ve used a service like SpellAlerts.com or SiteChecker.com to help keep mistakes off of my websites.

    -Brenda

  4. Your site looks great but I did notice that the word “canot” appears to be spelled incorrectly. I saw a couple small issues like this. I thought you would like to know!

    In case you wanted to fix it, in the past we’ve used services from a websites like HelloSpell.com to keep our site error-free.

  5. It looks like you’ve misspelled the word “canot” on your website. I thought you would like to know :) . Silly mistakes can ruin your site’s credibility. I’ve used a tool called SpellScan.com in the past to keep mistakes off of my website.

    -Kerri

  6. Java

    LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
    if (!launcherApps.hasShortcutHostPermission()) {
    // Don’t have permission, you may need set this app as default desktop.
    return;
    }

    PackageManager packageManager = context.getPackageManager();
    Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
    mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
    List resolveInfoList;
    if (packageManager == null || CollectionUtils
    .isEmpty(resolveInfoList = packageManager.queryIntentActivities(mainIntent, 0))) {
    // No Main&Launcher Activity
    return;
    }

    // Get ShortcutInfo for every app
    Set packageNameSet = new HashSet();
    for (ResolveInfo info : resolveInfoList) {
    ApplicationInfo applicationInfo;
    if (info == null || info.activityInfo == null
    || (applicationInfo = info.activityInfo.applicationInfo) == null || !applicationInfo.enabled
    || packageNameSet.contains(applicationInfo.packageName)) {
    continue;
    }
    packageNameSet.add(applicationInfo.packageName);

    int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_MANIFEST
    | ShortcutQuery.FLAG_MATCH_PINNED;
    List shortcutInfoList = launcherApps.getShortcuts(
    new ShortcutQuery().setPackage(applicationInfo.packageName).setQueryFlags(queryFlags),
    UserHandle.getUserHandleForUid(applicationInfo.uid));
    ……
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
    if (!launcherApps.hasShortcutHostPermission()) {
    // Don’t have permission, you may need set this app as default desktop.
    return;
    }

    PackageManager packageManager = context.getPackageManager();
    Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
    mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
    List resolveInfoList;
    if (packageManager == null || CollectionUtils
    .isEmpty(resolveInfoList = packageManager.queryIntentActivities(mainIntent, 0))) {
    // No Main&Launcher Activity
    return;
    }

    // Get ShortcutInfo for every app
    Set packageNameSet = new HashSet();
    for (ResolveInfo info : resolveInfoList) {
    ApplicationInfo applicationInfo;
    if (info == null || info.activityInfo == null
    || (applicationInfo = info.activityInfo.applicationInfo) == null || !applicationInfo.enabled
    || packageNameSet.contains(applicationInfo.packageName)) {
    continue;
    }
    packageNameSet.add(applicationInfo.packageName);

    int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_MANIFEST
    | ShortcutQuery.FLAG_MATCH_PINNED;
    List shortcutInfoList = launcherApps.getShortcuts(
    new ShortcutQuery().setPackage(applicationInfo.packageName).setQueryFlags(queryFlags),
    UserHandle.getUserHandleForUid(applicationInfo.uid));
    ……
    }