Android 7.1 新特性 - App Shortcuts 简介

   2017-01-06 0
核心提示:原文信息 ⊙ 作者:厉圣杰⊙ 原文地址:http://www.jianshu.com/p/fddc8d5f8d64⊙ 个人公众号:牙锅 (yaguo2017) 欢迎厉圣杰 投稿,他的个人公众号:牙锅 这是一篇关于Android7.1新特新shortcut的简介 Android 7.1 新功能之一就是 App Shortcuts(应用快捷

原文信息

作者:厉圣杰 

原文地址:http://www.jianshu.com/p/fddc8d5f8d64

 个人公众号:牙锅 (yaguo2017)

欢迎 厉圣杰 投稿,他的个人公众号:牙锅

这是一篇关于Android7.1新特新shortcut的简介

Android 7.1 新功能之一就是 App Shortcuts(应用快捷方式) ,该功能与 iPhone 上的 3D Touch 功能相似,通过长按应用图标,可弹出应用快捷方式,点击可以直接跳转到相应的界面。目前最多支持 5 个快捷方式,可以 getMaxShortcutCountPerActivity() 查看 Launcher 最多支持几个快捷方式,不同的是 Android 支持通过拖拽将快捷方式固定到桌面。

看似美好,其实应用快捷方式还是有很多缺陷的:

  1. 只能在 Google 的 Nexus 及 Pixel 设备上使用

  2. 系统必须是 Android 7.1 及以上(API Level >= 25)

  3. 已经被用户固定到桌面的快捷方式必须得到兼容性处理,因为你基本上失去了对其控制,除了升级时禁用

    Launcher applications allow users to "pin" shortcuts so they're easier to access. Both manifest and dynamic shortcuts can be pinned. Pinned shortcuts cannot be removed by publisher applications; they're removed only when the user removes them, when the publisher application is uninstalled, or when the user performs the "clear data" action on the publisher application from the device's Settings application.
    However, the publisher application can disable pinned shortcuts so they cannot be started. See the following sections for details.

应用快捷方式可分为 Static Shortcuts(静态快捷方式)Dynamic Shortcuts(动态快捷方式) 两种。

  • 静态快捷方式:又名 Manifest Shortcuts ,在应用安装时创建,不能实现动态修改,只能通过应用更新相应的 XML 资源文件才能实现更新。

  • 动态快捷方式:应用运行时通过 ShortcutManager 实现动态添加、删除、禁用等操作。

下面分别来讲述如何创建静态快捷方式和动态快捷方式。

创建静态快捷方式

1

  1. 在 /res/xml 目录下创建 shortcuts.xml ,添加根元素 \<shortcuts> ,其包含一组 \<shortcut> 标签。每个 \<shortcut> 标签为一个静态快捷方式,它包含相应的图标、描述以及对应的 intent

    <?xml version="1.0" encoding="utf-8"?>
    <shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
        <shortcut
            android:enabled="true"
            android:icon="@mipmap/ic_launcher"
            android:shortcutDisabledMessage="@string/compose_disabled_message1"
            android:shortcutId="compose1"
            android:shortcutLongLabel="@string/compose_shortcut_long_label1"
            android:shortcutShortLabel="@string/compose_shortcut_short_label1">
            <intent
                android:action="android.intent.action.VIEW"
                android:targetClass="com.littlejie.shortcuts.MainActivity"
                android:targetPackage="com.littlejie.shortcuts" />
            <intent
                android:action="android.intent.action.VIEW"
                android:targetClass="com.littlejie.shortcuts.TestActivity"
                android:targetPackage="com.littlejie.shortcuts" />
            <categories android:name="android.shortcut.conversation" />
        </shortcut>
        <!-- Specify more shortcuts here. -->
    </shortcuts>
  2. 打开 AndroidManifest.xml 文件,找到其中 <intent-filter> 被设置为 android.intent.action.MAINandroid.intent.category.LAUNCHER 的 Activity

  3. 给这个 Activity 添加 <meta-data> ,引用资源 shortcuts.xml

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
    
            <meta-data
                android:name="android.app.shortcuts"
                android:resource="@xml/shortcuts" />
        </activity>
        <activity
            android:name=".TestActivity"
            android:icon="@mipmap/ic_alarm">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
    
            <meta-data
                android:name="android.app.shortcuts"
                android:resource="@xml/shortcuts2" />
        </activity>
    </application>

补充:注意第 2 点的描述,也就是说如果 Manifest 中存在多个满足条件的 Activity ,那么就可以存在多组应用快捷方式,但 资源文件必须不同,主要是 shortcutId 必须不同,否则不会显示 。大家可以自己去尝试下~

<shortcut> 标签属性含义如下:

shortcutId:快捷方式的唯一标识。
当用户拖拽快捷方式到桌面,只要 shortcutId 不变,修改 <shortcut> 其余属性值,重新打包,修改之后的变化会体现到已拖拽到桌面的快捷方式上。
若存在多个 <shortcut> 但 shortcutId 相同,则只会显示一个

icon:快捷方式图标

shortcutShortLabel:快捷方式的 Label,当用户拖拽快捷方式到桌面时,显示的也是该字符串

shortcutLongLabel:长按 app 图标出现快捷方式时显示的图标

shortcutDisabledMessage:当快捷方式被禁用时显示的提示语

enabled:标识快捷方式是否可用,可用时,快捷方式图标正常显示,点击跳转到对应的 intent ;不可用时,快捷方式图标变灰,点击弹出 shortcutDisabledMessage 对应字符串的 Toast

intent:快捷方式关联的 intent,当用户点击快捷方式时,列表中所有 intent 都会被打开,但用户看见的是列表中最后一个 intent 。

categories:用于指定 shortcut 的 category,目前只有 SHORTCUT_CATEGORY_CONVERSATION 一个值

补充:如果 <shortcuts> 中只有一个 <shortcut> 且 enabled = false ,那么长按 app 图标是不会弹出任意快捷方式

gradle 配置:

//1.SDK版本要求25及以上
//2.最低兼容版本为23
compileSdkVersion 25
//主要是看 buildToolsVersion
buildToolsVersion "25.0.0"

defaultConfig {
    applicationId "com.littlejie.shortcuts"
    minSdkVersion 25
    targetSdkVersion 23
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

Android 7.1 新特性 - App Shortcuts 简介

关于 compileSdkVersion 、 minSdkVersion 以及 targetSdkVersion 的区别可参考

http://www.open-open.com/lib/view/open1453253049558.html

创建动态快捷方式

2

创建动态快捷方式主要依靠 这几个类来实现。ShortcutInfo 和 ShortcutInfo.Builder 主要用来构造快捷方式对象, ShortManager 是一个系统服务,用于管理应用快捷方式,ShortManager 可以通过以下方式获取:

ShortManager shortManager = (ShortcutManager) getSystemService(Context.SHORTCUT_SERVICE);
ShortManager 主要有以下几个功能:
  • 发布:通过调用 setDynamicShortcuts(List) 替换整个快捷方式列表或者通过 addDynamicShortcuts(List) 往已存在的快捷方式列表中添加快捷方式。

  • 更新:调用 updateShortcuts(List) 来更新已存在的快捷方式列表

  • 移除:调用 removeDynamicShortcuts(List) 移除列表中指定快捷方式,调用 removeAllDynamicShortcuts() 移除列表中所以快捷方式。

  • 禁用:因为用户可能将您任意的快捷方式拖拽到桌面,而这些快捷方式会将用户引导至应用中不存在或过期的操作,所以可以通过调用 disableShortcuts(List) 来禁用任何已存在的快捷方式。调用 disableShortcuts(List, Charsquence) 会给出错误提示。

下面代码主要演示了使用 ShortManager 实现动态发布、更新、移除以及禁用快捷方式。

动态创建快捷方式核心代码:

public class MainActivity extends Activity implements View.OnClickListener {

    private static final String TAG = MainActivity.class.getSimpleName();
    private ShortcutManager mShortcutManager;
    private ShortcutInfo[] mShortcutInfos;
    private int mAddCount;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //这里常量Context.SHORTCUT_SERVICE会报错,不用管,可正常编译。看着烦的话把minSdkVersion改为25即可
        mShortcutManager = (ShortcutManager) getSystemService(Context.SHORTCUT_SERVICE);

        mShortcutInfos = new ShortcutInfo[]{getShoppingShortcut(), getDateShortcut()};
        findViewById(R.id.btn_set).setOnClickListener(this);
        findViewById(R.id.btn_add).setOnClickListener(this);
        findViewById(R.id.btn_update).setOnClickListener(this);
        findViewById(R.id.btn_disabled).setOnClickListener(this);
        findViewById(R.id.btn_remove).setOnClickListener(this);
        findViewById(R.id.btn_removeAll).setOnClickListener(this);
        findViewById(R.id.btn_print_max_shortcut_per_activity).setOnClickListener(this);
        findViewById(R.id.btn_print_dynamic_shortcut).setOnClickListener(this);
        findViewById(R.id.btn_print_static_shortcut).setOnClickListener(this);
    }

    private ShortcutInfo getAlarmShortcut(String shortLabel) {
        if (TextUtils.isEmpty(shortLabel)) {
            shortLabel = "Python";
        }
        ShortcutInfo alarm = new ShortcutInfo.Builder(this, "alarm" + mAddCount++)
                .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.baidu.org/")))
                .setShortLabel(shortLabel)
                .setLongLabel("不正经的闹钟")
                .setIcon(Icon.createWithResource(this, R.mipmap.ic_alarm))
                .build();
        return alarm;
    }

    private ShortcutInfo getShoppingShortcut() {
        ShortcutInfo shopping = new ShortcutInfo.Builder(this, "shopping")
                .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.taobao.com")))
                .setShortLabel("双十一剁手")
                .setLongLabel("一本正经的购物")
                .setIcon(Icon.createWithResource(this, R.mipmap.ic_shopping))
                .build();
        return shopping;
    }

    private ShortcutInfo getDateShortcut() {
        ShortcutInfo date = new ShortcutInfo.Builder(this, "date")
                .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com")))
                .setShortLabel("被强了")
                .setLongLabel("日程")
                .setIcon(Icon.createWithResource(this, R.mipmap.ic_today))
                .build();
        return date;
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_set:
                setDynamicShortcuts();
                break;
            case R.id.btn_add:
                addDynamicShortcuts();
                break;
            case R.id.btn_update:
                updateDynamicShortcuts();
                break;
            case R.id.btn_disabled:
                disableDynamicShortcuts();
                break;
            case R.id.btn_remove:
                removeDynamicShortcuts();
                break;
            case R.id.btn_removeAll:
                removeAllDynamicShortcuts();
                break;
            case R.id.btn_print_max_shortcut_per_activity:
                printMaxShortcutsPerActivity();
                break;
            case R.id.btn_print_dynamic_shortcut:
                printDynamicShortcuts();
                break;
            case R.id.btn_print_static_shortcut:
                printStaticShortcuts();
                break;
        }
    }

    /**
     * 动态设置快捷方式
     */
    private void setDynamicShortcuts() {
        mShortcutManager.setDynamicShortcuts(Arrays.asList(mShortcutInfos));
    }

    /**
     * 添加快捷方式
     */
    private void addDynamicShortcuts() {
        mShortcutManager.addDynamicShortcuts(Arrays.asList(getAlarmShortcut(null)));
    }

    /**
     * 更新快捷方式,类似于Notification,根据id进行更新
     */
    private void updateDynamicShortcuts() {
        mShortcutManager.updateShortcuts(Arrays.asList(getAlarmShortcut("Java")));
    }

    /**
     * 禁用动态快捷方式
     */
    private void disableDynamicShortcuts() {
        mShortcutManager.disableShortcuts(Arrays.asList("alarm0"));
        //因为shortcutId=compose2的快捷方式为静态,所以不能实现动态修改
        //mShortcutManager.disableShortcuts(Arrays.asList("compose2"));
    }

    /**
     * 移除快捷方式
     * <p>
     * 已被用户拖拽到桌面的快捷方式还是回继续存在,如果不在支持的话,最好将其置为disable
     */
    private void removeDynamicShortcuts() {
        mShortcutManager.removeDynamicShortcuts(Arrays.asList("alarm0"));
    }

    /**
     * 移除所有动态快捷方式
     */
    private void removeAllDynamicShortcuts() {
        mShortcutManager.removeAllDynamicShortcuts();
    }

    /**
     * 只要 intent-filter 的 action = android.intent.action.MAIN
     * 和 category = android.intent.category.LAUNCHER 的 activity 都可以设置 Shortcuts
     */
    private void printMaxShortcutsPerActivity() {
        Log.d(TAG, "每个 Launcher Activity 能显示的最多快捷方式个数:" + mShortcutManager.getMaxShortcutCountPerActivity());
    }

    /**
     * 打印动态快捷方式信息
     */
    private void printDynamicShortcuts() {
        Log.d(TAG, "动态快捷方式列表数量:" + mShortcutManager.getDynamicShortcuts().size() + "信息:" + mShortcutManager.getDynamicShortcuts());
    }

    /**
     * 打印静态快捷方式信息
     */
    private void printStaticShortcuts() {
        Log.d(TAG, "静态快捷方式列表数量:" + mShortcutManager.getManifestShortcuts().size() + "信息:" + mShortcutManager.getManifestShortcuts());
    }

}

代码基本涵盖了动态创建快捷方式的所有情况,组合测试一下就可以了。

注意代码中的 addDynamicShortcuts() 方法,该方法调用 getAlarmShortcut(String shortcutLabel) 方法生成 ShortcutInfo ,该方法生成的 ShortcutInfo 的 id 是在变化的,如果多次点击超过 mShortcutManager.getMaxShortcutCountPerActivity() 的值,就会抛出如下异常:

java.lang.IllegalArgumentException: Max number of dynamic shortcuts exceeded
       at android.os.Parcel.readException(Parcel.java:1688)
       at android.os.Parcel.readException(Parcel.java:1637)
       at android.content.pm.IShortcutService$Stub$Proxy.addDynamicShortcuts(IShortcutService.java:430)
       at android.content.pm.ShortcutManager.addDynamicShortcuts(ShortcutManager.java:535)

所以在动态添加快捷方式之前最好先检查一下是否超过最大值。

还有, disableDynamicShortcuts() 注释了使用 ShortManager 动态修改静态快捷方式的代码,因为静态快捷方式时不允许在运行时进行修改的,如果执行了修改会抛出如下异常:

java.lang.IllegalArgumentException: Manifest shortcut ID=compose2 may not be manipulated via APIs
    at android.os.Parcel.readException(Parcel.java:1688)
    at android.os.Parcel.readException(Parcel.java:1637)
    at android.content.pm.IShortcutService$Stub$Proxy.disableShortcuts(IShortcutService.java:540)
    at android.content.pm.ShortcutManager.disableShortcuts(ShortcutManager.java:615)
    at com.littlejie.shortcuts.MainActivity.disableDynamicShortcuts(MainActivity.java:135)

值得注意的地方

3

前面讲了创建静态快捷方式和动态快捷方式,可能某些要点还没讲到,这里做下总结。

被禁用的快捷方式还计入已经创建快捷方式里嘛

addDynamicShortcuts() disableDynamicShortcuts printDynamicShortcuts() 测试,发现被禁用的快捷方式时不算已经创建的快捷方式的。

总结

4

简单的总结了一下 Android 7.1 中应用快捷方式的创建及注意点,但某些不太常用的没怎么去研究,有兴趣的可以参考

https://developer.android.com/reference/android/content/pm/ShortcutManager.html

参考:

  1. ShortcutManager

    https://developer.android.com/reference/android/content/pm/ShortcutManager.html

  2. App Shortcuts

    https://developer.android.com/preview/shortcuts.html

  3. 源码地址公众号回复: 20170104

Android订阅是探讨Android开发的公众号,分享最有价值的Android干货文章

欢迎关注我们,一起讨论技术,扫描和长按下方的二维码可快速关注我们

Android 7.1 新特性 - App Shortcuts 简介

 
标签: 安卓开发
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • 安卓中通知功能的具体实现
    安卓中通知功能的具体实现
    通知[Notification]是Android中比较有特色的功能,当某个应用程序希望给用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知实现。使用通知的步骤1、需要一个NotificationManager来获得NotificationManager manager = (NotificationManager
    02-05 安卓开发
  • Android view系统分析-setContentView
    Android view系统分析-setContentView
    第一天上班,列了一下今年要学习的东西。主要就是深入学习Android相关的系统源代码,夯实基础。对于学习Android系统源代码,也没什么大概,就从我们平常使用最基础的东西学起,也就是从view这个切入点开始学习Android的源码,在没分析源码之前,我们有的时候
    02-05 安卓开发
  • 如何进行网络视频截图/获取视频的缩略图
    如何进行网络视频截图/获取视频的缩略图
    小编导读:获取视频的缩略图,截图正在播放的视频某一帧,是在音视频开发中,常遇到的问题。本文是主要用于点播中截图视频,同时还可以获取点播视频的缩略图进行显示,留下一个问题,如下图所示, 如果要获取直播中节目视频缩略图,该怎么做呢?(ps:直播是直
  • Android NDK 层发起 HTTP 请求的问题及解决
    Android NDK 层发起 HTTP 请求的问题及解决
    前言新的一年,大家新年快乐~~鸡年大吉!本次给大家带来何老师的最新文章~虽然何老师还在过节,但依然放心不下广大开发者,在此佳节还未结束之际,给大家带来最新的技术分享~ 事件的起因不说了,总之是需要实现一个 NDK 层的网络请求。为了多端适用,还是选择
  • Android插件化(六): OpenAtlasの改写aapt以防止资源ID冲突
    Android插件化(六): OpenAtlasの改写aapt以防
    引言Android应用程序的编译中,负责资源打包的是aapt,如果不对打包后的资源ID进行控制,就会导致插件中的资源ID冲突。所以,我们需要改写aapt的源码,以达到通过某种方式传递资源ID的Package ID,通过aapt打包时获取到这个Package ID并且应用才插件资源的命名
    02-05 安卓开发
  • Android架构(一)MVP架构在Android中的实践
    Android架构(一)MVP架构在Android中的实践
    为什么要重视程序的架构设计 对程序进行架构设计的原因,归根结底是为了 提高生产力 。通过设计是程序模块化,做到模块内部的 高聚合 和模块之间的 低耦合 (如依赖注入就是低耦合的集中体现)。 这样做的好处是使得程序开发过程中,开发人员主需要专注于一点,
    02-05 安卓开发
  • 安卓逆向系列教程 4.2 分析锁机软件
    安卓逆向系列教程 4.2 分析锁机软件
    安卓逆向系列教程 4.2 分析锁机软件 作者: 飞龙 这个教程中我们要分析一个锁机软件。像这种软件都比较简单,完全可以顺着入口看下去,但我这里还是用关键点来定位。首先这个软件的截图是这样,进入这个界面之后,除非退出模拟器,否则没办法回到桌面。上面那
    02-05 安卓开发
  • Android插件化(二):OpenAtlas插件安装过程分析
    Android插件化(二):OpenAtlas插件安装过程分析
    在前一篇博客 Android插件化(一):OpenAtlas架构以及实现原理概要 中,我们对应Android插件化存在的问题,实现原理,以及目前的实现方案进行了简单的叙述。从这篇开始,我们要深入到OpenAtlas的源码中进行插件安装过程的分析。 插件的安装分为3种:宿主启动时立
    02-05 安卓开发
  • [译] Android API 指南
    [译] Android API 指南
    众所周知,Android开发者有中文网站了,API 指南一眼看去最左侧的菜单都是中文,然而点进去内容还是很多是英文,并没有全部翻译,我这里整理了API 指南的目录,便于查看,如果之前还没有通读,现在可以好好看一遍。注意,如果标题带有英文,说明官方还没有翻
  • 使用FileProvider解决file:// URI引起的FileUriExposedException
    使用FileProvider解决file:// URI引起的FileUri
    问题以下是一段简单的代码,它调用系统的相机app来拍摄照片:void takePhoto(String cameraPhotoPath) {File cameraPhoto = new File(cameraPhotoPath);Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);takePhotoIntent.putExtra(Medi
    02-05 安卓开发
点击排行