Android创建桌面快捷方式所遇到的问题与解决方案
将近二个多月没写博客了。
之前一段时间一直在搞红包助手,就没抽时间写博客,但写这个真的是很好玩。没想到居然在Android上实现模拟点击,从而实现自动抢红包,有兴趣的同学可以参考https://github.com/waylife/RedEnvelopeAssistant ,代码已经开源。
红包助手还有一些问题,但是现在基本的抢红包基本没问题了。目前正在对它进行优化以及较低版本的一些适配,还有项目的国际化工作。
废话不多说了,下面是Andrioid开发过程中快捷方式相关的事与坑。
数据来源于上次组内自己的CodeReview总结。
背景
一般情况下,为了让用户更方便的打开应用,程序会在桌面上生成一些快捷方式。
本来呢,如果是原生的桌面,其实是十分简单,直接调用系统相关的API就行了。但是众多的系统厂商以及众多第三方自己定制的桌面(Launcher),导致在适配、兼容方面存在很多问题。
比如,有些桌面无法删除快捷方式(比如小米),有些桌面无法生成快捷方式(比如锤子),有些系统无法更新桌面图标(比如华为荣耀6)。
在升级、降级的时候快捷方式发生变化;比如,全部变成应用的主图标,升级、降级后点击快捷方式没有反应,删除应用后无法删除快捷方式。
很多问题都是需要解决的,虽然有些由于系统限制,没有办法搞定所有的,但是仍然需要寻求一个最优的方案。这也就是本文需要讨论的问题。
本文说指的快捷方式是指应用桌面快捷方式,不包含长按弹出的生成快捷方式。
快捷方式所有信息都是存在于launcher的favorite表。一般需要用到的字段为_id,title,intent,iconResource,icon,分别表示 快捷方式名称,快捷方式intent,快捷方式图标(本地),快捷方式图标(data二进制压缩数据)。
两个intent数据如下
数据可以通过SQLite Editor查看,需要已经ROOT的手机
实现
增加快捷方式
在AndroidManifest.xml增加权限
![](https://code.csdn.net/assets/CODE_ico.png)
- <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
同时,根据Intent是隐式还是显示在相关的Activity声明相关的intent-filter。
相关代码:
删除快捷方式
跟增加快捷方式一样,也是需要增加权限的。加上
![](https://code.csdn.net/assets/CODE_ico.png)
- <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />
相关代码:
快捷方式修改
需要增加权限
![](https://code.csdn.net/assets/CODE_ico.png)
- <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
如果适配所有桌面,请添加附录中第二条所列出的权限。
系统并没有提供API去更改桌面快捷方式。只能通过其他猥琐的办法了,可行的的办法之一就是通过ContentProvider去更改数据库相关的信息。当然有人会说了,先删掉快捷方式,再重新创建不就行了?这是个办法。但是有些系统是无法删除快捷方式的;另外,删除快捷方式与创建快捷方式都是通过广播实现的,这个地方需要控制两者的时间间隔。权衡之后,选用第一种办法相对稳妥。
废话不多少,上代码。
![](https://code.csdn.net/assets/CODE_ico.png)
- /**
- * 更新桌面快捷方式图标,不一定所有图标都有效<br/>
- * 如果快捷方式不存在,则不更新<br/>.
- */
- public static void updateShortcutIcon(Context context, String title, Intent intent,Bitmap bitmap) {
- if(bitmap==null){
- XLog.i(TAG, "update shortcut icon,bitmap empty");
- return;
- }
- try{
- final ContentResolver cr = context.getContentResolver();
- StringBuilder uriStr = new StringBuilder();
- String urlTemp="";
- String authority = LauncherUtil.getAuthorityFromPermissionDefault(context);
- if(authority==null||authority.trim().equals("")){
- authority = LauncherUtil.getAuthorityFromPermission(context,LauncherUtil.getCurrentLauncherPackageName(context)+".permission.READ_SETTINGS");
- }
- uriStr.append("content://");
- if (TextUtils.isEmpty(authority)) {
- int sdkInt = android.os.Build.VERSION.SDK_INT;
- if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的
- uriStr.append("com.android.launcher.settings");
- } else if (sdkInt < 19) {// Android 4.4以下
- uriStr.append("com.android.launcher2.settings");
- } else {// 4.4以及以上
- uriStr.append("com.android.launcher3.settings");
- }
- } else {
- uriStr.append(authority);
- }
- urlTemp=uriStr.toString();
- uriStr.append("/favorites?notify=true");
- Uri uri = Uri.parse(uriStr.toString());
- Cursor c = cr.query(uri, new String[] {"_id", "title", "intent" },
- "title=? and intent=? ",
- new String[] { title, intent.toUri(0) }, null);
- int index=-1;
- if (c != null && c.getCount() > 0) {
- c.moveToFirst();
- index=c.getInt(0);//获得图标索引
- ContentValues cv=new ContentValues();
- cv.put("icon", flattenBitmap(bitmap));
- Uri uri2=Uri.parse(urlTemp+"/favorites/"+index+"?notify=true");
- int i=context.getContentResolver().update(uri2, cv, null,null);
- context.getContentResolver().notifyChange(uri,null);//此处不能用uri2,是个坑
- XLog.i(TAG, "update ok: affected "+i+" rows,index is"+index);
- }else{
- XLog.i(TAG, "update result failed");
- }
- if (c != null && !c.isClosed()) {
- c.close();
- }
- }catch(Exception ex){
- ex.printStackTrace();
- XLog.i(TAG, "update shortcut icon,get errors:"+ex.getMessage());
- }
- }
- private static byte[] flattenBitmap(Bitmap bitmap) {
- // Try go guesstimate how much space the icon will take when serialized
- // to avoid unnecessary allocations/copies during the write.
- int size = bitmap.getWidth() * bitmap.getHeight() * 4;
- ByteArrayOutputStream out = new ByteArrayOutputStream(size);
- try {
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
- out.flush();
- out.close();
- return out.toByteArray();
- } catch (IOException e) {
- XLog.w(TAG, "Could not write icon");
- return null;
- }
- }
快捷方式存在判断
需要增加的权限同修改快捷方式
虽然说通过SharePreference来保证快捷方式不会重复创建,以及通过shortcutIntent.putExtra(“duplicate”, false)也可以确保,但是为了万无一失,还是可以通过去查询数据判断快捷方式是否存在,来避免重复创建。 代码如下:
![](https://code.csdn.net/assets/CODE_ico.png)
- /**
- * 检查快捷方式是否存在 <br/>
- * <font color=red>注意:</font> 有些手机无法判断是否已经创建过快捷方式<br/>
- * 因此,在创建快捷方式时,请添加<br/>
- * shortcutIntent.putExtra("duplicate", false);// 不允许重复创建<br/>
- * 最好使用{@link #isShortCutExist(Context, String, Intent)}
- * 进行判断,因为可能有些应用生成的快捷方式名称是一样的的<br/>
- * 此处需要在AndroidManifest.xml中配置相关的桌面权限信息<br/>
- * 错误信息已捕获<br/>
- */
- public static boolean isShortCutExist(Context context, String title) {
- boolean result = false;
- try {
- final ContentResolver cr = context.getContentResolver();
- StringBuilder uriStr = new StringBuilder();
- String authority = LauncherUtil.getAuthorityFromPermissionDefault(context);
- if(authority==null||authority.trim().equals("")){
- authority = LauncherUtil.getAuthorityFromPermission(context,LauncherUtil.getCurrentLauncherPackageName(context)+".permission.READ_SETTINGS");
- }
- uriStr.append("content://");
- if (TextUtils.isEmpty(authority)) {
- int sdkInt = android.os.Build.VERSION.SDK_INT;
- if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的
- uriStr.append("com.android.launcher.settings");
- } else if (sdkInt < 19) {// Android 4.4以下
- uriStr.append("com.android.launcher2.settings");
- } else {// 4.4以及以上
- uriStr.append("com.android.launcher3.settings");
- }
- } else {
- uriStr.append(authority);
- }
- uriStr.append("/favorites?notify=true");
- Uri uri = Uri.parse(uriStr.toString());
- Cursor c = cr.query(uri, new String[] { "title" },
- "title=? ",
- new String[] { title }, null);
- if (c != null && c.getCount() > 0) {
- result = true;
- }
- if (c != null && !c.isClosed()) {
- c.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- result=false;
- }
- return result;
- }
- /**
- * 不一定所有的手机都有效,因为国内大部分手机的桌面不是系统原生的<br/>
- * 更多请参考{@link #isShortCutExist(Context, String)}<br/>
- * 桌面有两种,系统桌面(ROM自带)与第三方桌面,一般只考虑系统自带<br/>
- * 第三方桌面如果没有实现系统响应的方法是无法判断的,比如GO桌面<br/>
- * 此处需要在AndroidManifest.xml中配置相关的桌面权限信息<br/>
- * 错误信息已捕获<br/>
- */
- public static boolean isShortCutExist(Context context, String title, Intent intent) {
- boolean result = false;
- try{
- final ContentResolver cr = context.getContentResolver();
- StringBuilder uriStr = new StringBuilder();
- String authority = LauncherUtil.getAuthorityFromPermissionDefault(context);
- if(authority==null||authority.trim().equals("")){
- authority = LauncherUtil.getAuthorityFromPermission(context,LauncherUtil.getCurrentLauncherPackageName(context)+".permission.READ_SETTINGS");
- }
- uriStr.append("content://");
- if (TextUtils.isEmpty(authority)) {
- int sdkInt = android.os.Build.VERSION.SDK_INT;
- if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的
- uriStr.append("com.android.launcher.settings");
- } else if (sdkInt < 19) {// Android 4.4以下
- uriStr.append("com.android.launcher2.settings");
- } else {// 4.4以及以上
- uriStr.append("com.android.launcher3.settings");
- }
- } else {
- uriStr.append(authority);
- }
- uriStr.append("/favorites?notify=true");
- Uri uri = Uri.parse(uriStr.toString());
- Cursor c = cr.query(uri, new String[] { "title", "intent" },
- "title=? and intent=?",
- new String[] { title, intent.toUri(0) }, null);
- if (c != null && c.getCount() > 0) {
- result = true;
- }
- if (c != null && !c.isClosed()) {
- c.close();
- }
- }catch(Exception ex){
- result=false;
- ex.printStackTrace();
- }
- return result;
- }
兼容与注意事项
兼容
所有的快捷方式Intent如果不是之前版本的存在很大问题,绝对不要改变参数,否则升级或者降级时快捷方式会出现问题;
同时,尽可能的采用隐式调用,自定义CATEGORY,而不是自定义ACTION,ACTION参数一定要为ACTION_MAIN,否则有些手机在卸载时无法删除快捷方式(WTF)。
注意事项
- 【所有】activity路径的变更导致老版本升级之后快捷方式无法使用
—> 1.一旦使用确定了activity的包路径,之后就不要再变更;
—> 2.尽可能使用隐式调用,但是如果之前已经发出去的版本,为了兼容性,就必须一直使用老的方式,新版本的尽可能的不要更改方式,如果用户降级,老版本快捷方式会无法使用。 - 【部分】多个快捷方式指向一个activity导致部分手机(三星SII)升级时图标变成应用图标
—> 尽可能的避免多个快捷方式指向同一个activity,可能通过多个activity再跳转过去 - 【部分】应用删除时无法删除快捷方式。与系统桌面Launcher实现有关。
—> 为了适配所有Launcher,Intent Action使用Intent.ACTION_MAIN。如果是隐式调用,尽可能自定义CATEGORY,而不是自定义ACTION。 - 【部分】应用升级时需要删除老版本部分快捷方式,但是部分手机无法删除
—> 无解 - 【部分】第三方桌面无法生成、删除、更新快捷方式
—> 呵呵,一般来说生成没有问题,但是删除,更新大部分桌面会有问题。尽可能避免这些操作。或者专门适配该桌面,成本较高。 - 【部分】部分桌面无法实时更新图标,需要重启
—> 无解,尝试过重启Launcher,但是结果是之前的快捷方式也消失了。只有重启手机,按理来说应该是有方式触发Launcher进行刷新的。
以上【所有】【部分】,分别表示必定出现,部分出现。
参考
- http://grepcode.com/search/?query=InstallShortcutReceiver
- http://developer.android.com/index.html
附录
- 完整代码可参考https://gist.github.com/waylife/437a3d98a84f245b9582
- 通用更新快捷方式权限列表
![](https://code.csdn.net/assets/CODE_ico.png)
- <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.android.launcher2.permission.READ_SETTINGS" />
- <uses-permission android:name="com.android.launcher2.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
- <uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />
- <uses-permission android:name="org.adw.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="org.adw.launcher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.htc.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.htc.launcher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.qihoo360.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.qihoo360.launcher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.lge.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.lge.launcher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="net.qihoo.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="net.qihoo.launcher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="org.adwfreak.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="org.adwfreak.launcher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="org.adw.launcher_donut.permission.READ_SETTINGS" />
- <uses-permission android:name="org.adw.launcher_donut.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.huawei.launcher3.permission.READ_SETTINGS" />
- <uses-permission android:name="com.huawei.launcher3.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.fede.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.fede.launcher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.sec.android.app.twlauncher.settings.READ_SETTINGS" />
- <uses-permission android:name="com.sec.android.app.twlauncher.settings.WRITE_SETTINGS" />
- <uses-permission android:name="com.anddoes.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.anddoes.launcher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.tencent.qqlauncher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.tencent.qqlauncher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.huawei.launcher2.permission.READ_SETTINGS" />
- <uses-permission android:name="com.huawei.launcher2.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.android.mylauncher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.android.mylauncher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.ebproductions.android.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.ebproductions.android.launcher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.oppo.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.oppo.launcher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="com.huawei.android.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.huawei.android.launcher.permission.WRITE_SETTINGS" />
- <uses-permission android:name="telecom.mdesk.permission.READ_SETTINGS" />
- <uses-permission android:name="telecom.mdesk.permission.WRITE_SETTINGS" />
- <uses-permission android:name="dianxin.permission.ACCESS_LAUNCHER_DATA" />
转载地址:http://blog.zanlabs.com/2015/03/14/android-shortcut-summary/
Android创建桌面快捷方式所遇到的问题与解决方案相关推荐
- android自动创建桌面,Android创建桌面快捷方式
需求:点击按钮创建快捷方式 1.用户触发创创建事件时,在手机桌面创建指定页面的快捷方式. 2.当APP关闭时,点击桌面快捷方式打开APP,跳转至指定页面 3.当APP在后台是,点击桌面快捷方式,跳转至 ...
- android保存连接地址到桌面快捷方式,Android创建桌面快捷方式两种方法
谈谈在桌面上直接生成.个人觉得这个比较爽快,既然都是快捷方式了干嘛还要再隐藏一层呢?当然喜欢桌面干净的就比较喜欢第二个了. 第一个是通过广播(Broadcast)的形式向Luncher发送请求生成快捷 ...
- Android 创建桌面快捷方式的方法
在Android O及后续更高平台上,应用在桌面创建快捷方式的方法有了较多变更,从交互方式上趋向于让用户二次确认.代码如下: public static void addShortCut(Contex ...
- [Android]为指定的应用创建桌面快捷方式
网上一搜一大把为自己的应用创建快捷方式,但是本文的侧重点在为"指定的应用"创建桌面快捷方式. 常见的桌面快捷方式有两要素:1.应用名 2.应用图标. 指定应用图标的信息是: ...
- android自动创建桌面,Android应用启动后自动创建桌面快捷方式的实现方法
Android的开发其实是比较灵活的,其实在安装了Android应用程序之后,会在桌面上自动创建快捷方式,接下来爱站技术频道小编将会介绍Android应用启动后自动创建桌面快捷方式的实现方法给大家,有 ...
- Android应用启动后自动创建桌面快捷方式
为什么80%的码农都做不了架构师?>>> 和IOS开发和Windows Phone开发相比,Android是开放的,Android上的开发也相对更加灵活,能够做很多事情.有的朋 ...
- Android应用程序创建桌面快捷方式
2019独角兽企业重金招聘Python工程师标准>>> public static final String READ_SETTINGS_PERMISSION = "com ...
- Ubuntu 17.04系统创建Android Studio桌面快捷方式的方法
下面以"Android Studio"为例,阐述Ubuntu系统中创建桌面快捷方式的方法,假设已将"Android Studio"下载到"/home/ ...
- Android小技巧之创建桌面快捷方式(已适配8.0)
前言 在某些情况下需要对应用创建桌面快捷方式,特别是在使用原生系统(4.3到8.0)运行物联网app时,往往存在二级菜单,而应用图标默认安装在二级菜单(现在国内主流手机厂商的系统都没有二级菜单了),如 ...
最新文章
- 【Linux就该这么学 20期培训笔记 02】新手必须掌握的 Linux 命令
- 第二十五讲 用线性代数解微分方程组
- 精选文章 什么是跨域?怎么解决跨域问题?
- CG CTF WEB 签到题
- 【Qt】New Features in Qt 5.15
- asp.net core 环境(Development、Staging 、Production)
- Acwing 1088.旅行问题
- 【C#-枚举】枚举的使用
- php新窗口打开链接,wordpress如何设置在新窗口打开链接
- mysql binlog c++_MySQL binlog的格式解析
- 计算机电子设计论文,计算机毕业设计电子信息毕业设计电子信息毕业论文
- c语言自学基础知识视频,C语言 基础课堂视频教程
- java图片转换pdf_Java将图片转化为PDF的方法(1)
- Chap.20 总结《CL: An Introduction》 (Vyvyan Evans)
- 【转】用TrueType造字程序自己造字
- Vue环境搭建及第一个hello world
- Spring基础之IOCAOPDI
- minio存储之纠删码(Erasure Code)
- 安装mysql黑屏电脑卡死_电脑卡屏死机是怎么回事?
- arcgis10.8深度学习介绍课程梳理
热门文章
- 博士真正搞科研的姿势,是这样的丨知乎高赞
- 图对比学习入门 Contrastive Learning on Graph
- python解析torrent文件库:pytorrent
- 【错误记录】使用cv2报错 已解决
- Android使用Jack编译报错:communication error with Jack server (58) 解决方法
- 刺激却安全,普通人也能体验的方程式赛车
- mmsegmention数据集存放格式(三)
- 最近几个月学习情况的总结
- import statsmodels.api as sm 时 ImportError: DLL load failed while importing _arpack: 找不到指定的程序。
- js:秒转换为小时分钟秒格式