将近二个多月没写博客了。
     之前一段时间一直在搞红包助手,就没抽时间写博客,但写这个真的是很好玩。没想到居然在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增加权限

[html] view plaincopy
  1. <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />

同时,根据Intent是隐式还是显示在相关的Activity声明相关的intent-filter。
相关代码:

删除快捷方式

跟增加快捷方式一样,也是需要增加权限的。加上

[html] view plaincopy
  1. <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />

相关代码:

快捷方式修改

需要增加权限

[html] view plaincopy
  1. <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
  2. <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />

如果适配所有桌面,请添加附录中第二条所列出的权限。
系统并没有提供API去更改桌面快捷方式。只能通过其他猥琐的办法了,可行的的办法之一就是通过ContentProvider去更改数据库相关的信息。当然有人会说了,先删掉快捷方式,再重新创建不就行了?这是个办法。但是有些系统是无法删除快捷方式的;另外,删除快捷方式与创建快捷方式都是通过广播实现的,这个地方需要控制两者的时间间隔。权衡之后,选用第一种办法相对稳妥。
废话不多少,上代码。

[java] view plaincopy
  1. /**
  2. * 更新桌面快捷方式图标,不一定所有图标都有效<br/>
  3. * 如果快捷方式不存在,则不更新<br/>.
  4. */
  5. public static void updateShortcutIcon(Context context, String title, Intent intent,Bitmap bitmap) {
  6. if(bitmap==null){
  7. XLog.i(TAG, "update shortcut icon,bitmap empty");
  8. return;
  9. }
  10. try{
  11. final ContentResolver cr = context.getContentResolver();
  12. StringBuilder uriStr = new StringBuilder();
  13. String urlTemp="";
  14. String authority = LauncherUtil.getAuthorityFromPermissionDefault(context);
  15. if(authority==null||authority.trim().equals("")){
  16. authority = LauncherUtil.getAuthorityFromPermission(context,LauncherUtil.getCurrentLauncherPackageName(context)+".permission.READ_SETTINGS");
  17. }
  18. uriStr.append("content://");
  19. if (TextUtils.isEmpty(authority)) {
  20. int sdkInt = android.os.Build.VERSION.SDK_INT;
  21. if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的
  22. uriStr.append("com.android.launcher.settings");
  23. } else if (sdkInt < 19) {// Android 4.4以下
  24. uriStr.append("com.android.launcher2.settings");
  25. } else {// 4.4以及以上
  26. uriStr.append("com.android.launcher3.settings");
  27. }
  28. } else {
  29. uriStr.append(authority);
  30. }
  31. urlTemp=uriStr.toString();
  32. uriStr.append("/favorites?notify=true");
  33. Uri uri = Uri.parse(uriStr.toString());
  34. Cursor c = cr.query(uri, new String[] {"_id", "title", "intent" },
  35. "title=?  and intent=? ",
  36. new String[] { title, intent.toUri(0) }, null);
  37. int index=-1;
  38. if (c != null && c.getCount() > 0) {
  39. c.moveToFirst();
  40. index=c.getInt(0);//获得图标索引
  41. ContentValues cv=new ContentValues();
  42. cv.put("icon", flattenBitmap(bitmap));
  43. Uri uri2=Uri.parse(urlTemp+"/favorites/"+index+"?notify=true");
  44. int i=context.getContentResolver().update(uri2, cv, null,null);
  45. context.getContentResolver().notifyChange(uri,null);//此处不能用uri2,是个坑
  46. XLog.i(TAG, "update ok: affected "+i+" rows,index is"+index);
  47. }else{
  48. XLog.i(TAG, "update result failed");
  49. }
  50. if (c != null && !c.isClosed()) {
  51. c.close();
  52. }
  53. }catch(Exception ex){
  54. ex.printStackTrace();
  55. XLog.i(TAG, "update shortcut icon,get errors:"+ex.getMessage());
  56. }
  57. }
  58. private static byte[] flattenBitmap(Bitmap bitmap) {
  59. // Try go guesstimate how much space the icon will take when serialized
  60. // to avoid unnecessary allocations/copies during the write.
  61. int size = bitmap.getWidth() * bitmap.getHeight() * 4;
  62. ByteArrayOutputStream out = new ByteArrayOutputStream(size);
  63. try {
  64. bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
  65. out.flush();
  66. out.close();
  67. return out.toByteArray();
  68. } catch (IOException e) {
  69. XLog.w(TAG, "Could not write icon");
  70. return null;
  71. }
  72. }

快捷方式存在判断

需要增加的权限同修改快捷方式
虽然说通过SharePreference来保证快捷方式不会重复创建,以及通过shortcutIntent.putExtra(“duplicate”, false)也可以确保,但是为了万无一失,还是可以通过去查询数据判断快捷方式是否存在,来避免重复创建。 代码如下:

[java] view plaincopy
  1. /**
  2. * 检查快捷方式是否存在 <br/>
  3. * <font color=red>注意:</font> 有些手机无法判断是否已经创建过快捷方式<br/>
  4. * 因此,在创建快捷方式时,请添加<br/>
  5. * shortcutIntent.putExtra("duplicate", false);// 不允许重复创建<br/>
  6. * 最好使用{@link #isShortCutExist(Context, String, Intent)}
  7. * 进行判断,因为可能有些应用生成的快捷方式名称是一样的的<br/>
  8. * 此处需要在AndroidManifest.xml中配置相关的桌面权限信息<br/>
  9. * 错误信息已捕获<br/>
  10. */
  11. public static boolean isShortCutExist(Context context, String title) {
  12. boolean result = false;
  13. try {
  14. final ContentResolver cr = context.getContentResolver();
  15. StringBuilder uriStr = new StringBuilder();
  16. String authority = LauncherUtil.getAuthorityFromPermissionDefault(context);
  17. if(authority==null||authority.trim().equals("")){
  18. authority = LauncherUtil.getAuthorityFromPermission(context,LauncherUtil.getCurrentLauncherPackageName(context)+".permission.READ_SETTINGS");
  19. }
  20. uriStr.append("content://");
  21. if (TextUtils.isEmpty(authority)) {
  22. int sdkInt = android.os.Build.VERSION.SDK_INT;
  23. if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的
  24. uriStr.append("com.android.launcher.settings");
  25. } else if (sdkInt < 19) {// Android 4.4以下
  26. uriStr.append("com.android.launcher2.settings");
  27. } else {// 4.4以及以上
  28. uriStr.append("com.android.launcher3.settings");
  29. }
  30. } else {
  31. uriStr.append(authority);
  32. }
  33. uriStr.append("/favorites?notify=true");
  34. Uri uri = Uri.parse(uriStr.toString());
  35. Cursor c = cr.query(uri, new String[] { "title" },
  36. "title=? ",
  37. new String[] { title }, null);
  38. if (c != null && c.getCount() > 0) {
  39. result = true;
  40. }
  41. if (c != null && !c.isClosed()) {
  42. c.close();
  43. }
  44. } catch (Exception e) {
  45. e.printStackTrace();
  46. result=false;
  47. }
  48. return result;
  49. }
  50. /**
  51. * 不一定所有的手机都有效,因为国内大部分手机的桌面不是系统原生的<br/>
  52. * 更多请参考{@link #isShortCutExist(Context, String)}<br/>
  53. * 桌面有两种,系统桌面(ROM自带)与第三方桌面,一般只考虑系统自带<br/>
  54. * 第三方桌面如果没有实现系统响应的方法是无法判断的,比如GO桌面<br/>
  55. * 此处需要在AndroidManifest.xml中配置相关的桌面权限信息<br/>
  56. * 错误信息已捕获<br/>
  57. */
  58. public static boolean isShortCutExist(Context context, String title, Intent intent) {
  59. boolean result = false;
  60. try{
  61. final ContentResolver cr = context.getContentResolver();
  62. StringBuilder uriStr = new StringBuilder();
  63. String authority = LauncherUtil.getAuthorityFromPermissionDefault(context);
  64. if(authority==null||authority.trim().equals("")){
  65. authority = LauncherUtil.getAuthorityFromPermission(context,LauncherUtil.getCurrentLauncherPackageName(context)+".permission.READ_SETTINGS");
  66. }
  67. uriStr.append("content://");
  68. if (TextUtils.isEmpty(authority)) {
  69. int sdkInt = android.os.Build.VERSION.SDK_INT;
  70. if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的
  71. uriStr.append("com.android.launcher.settings");
  72. } else if (sdkInt < 19) {// Android 4.4以下
  73. uriStr.append("com.android.launcher2.settings");
  74. } else {// 4.4以及以上
  75. uriStr.append("com.android.launcher3.settings");
  76. }
  77. } else {
  78. uriStr.append(authority);
  79. }
  80. uriStr.append("/favorites?notify=true");
  81. Uri uri = Uri.parse(uriStr.toString());
  82. Cursor c = cr.query(uri, new String[] { "title", "intent" },
  83. "title=?  and intent=?",
  84. new String[] { title, intent.toUri(0) }, null);
  85. if (c != null && c.getCount() > 0) {
  86. result = true;
  87. }
  88. if (c != null && !c.isClosed()) {
  89. c.close();
  90. }
  91. }catch(Exception ex){
  92. result=false;
  93. ex.printStackTrace();
  94. }
  95. return result;
  96. }

兼容与注意事项

兼容

所有的快捷方式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进行刷新的。
    以上【所有】【部分】,分别表示必定出现,部分出现。

参考

  1. http://grepcode.com/search/?query=InstallShortcutReceiver
  2. http://developer.android.com/index.html

附录

  1. 完整代码可参考https://gist.github.com/waylife/437a3d98a84f245b9582
  2. 通用更新快捷方式权限列表
[html] view plaincopy
  1. <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
  2. <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
  3. <uses-permission android:name="com.android.launcher2.permission.READ_SETTINGS" />
  4. <uses-permission android:name="com.android.launcher2.permission.WRITE_SETTINGS" />
  5. <uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
  6. <uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />
  7. <uses-permission android:name="org.adw.launcher.permission.READ_SETTINGS" />
  8. <uses-permission android:name="org.adw.launcher.permission.WRITE_SETTINGS" />
  9. <uses-permission android:name="com.htc.launcher.permission.READ_SETTINGS" />
  10. <uses-permission android:name="com.htc.launcher.permission.WRITE_SETTINGS" />
  11. <uses-permission android:name="com.qihoo360.launcher.permission.READ_SETTINGS" />
  12. <uses-permission android:name="com.qihoo360.launcher.permission.WRITE_SETTINGS" />
  13. <uses-permission android:name="com.lge.launcher.permission.READ_SETTINGS" />
  14. <uses-permission android:name="com.lge.launcher.permission.WRITE_SETTINGS" />
  15. <uses-permission android:name="net.qihoo.launcher.permission.READ_SETTINGS" />
  16. <uses-permission android:name="net.qihoo.launcher.permission.WRITE_SETTINGS" />
  17. <uses-permission android:name="org.adwfreak.launcher.permission.READ_SETTINGS" />
  18. <uses-permission android:name="org.adwfreak.launcher.permission.WRITE_SETTINGS" />
  19. <uses-permission android:name="org.adw.launcher_donut.permission.READ_SETTINGS" />
  20. <uses-permission android:name="org.adw.launcher_donut.permission.WRITE_SETTINGS" />
  21. <uses-permission android:name="com.huawei.launcher3.permission.READ_SETTINGS" />
  22. <uses-permission android:name="com.huawei.launcher3.permission.WRITE_SETTINGS" />
  23. <uses-permission android:name="com.fede.launcher.permission.READ_SETTINGS" />
  24. <uses-permission android:name="com.fede.launcher.permission.WRITE_SETTINGS" />
  25. <uses-permission android:name="com.sec.android.app.twlauncher.settings.READ_SETTINGS" />
  26. <uses-permission android:name="com.sec.android.app.twlauncher.settings.WRITE_SETTINGS" />
  27. <uses-permission android:name="com.anddoes.launcher.permission.READ_SETTINGS" />
  28. <uses-permission android:name="com.anddoes.launcher.permission.WRITE_SETTINGS" />
  29. <uses-permission android:name="com.tencent.qqlauncher.permission.READ_SETTINGS" />
  30. <uses-permission android:name="com.tencent.qqlauncher.permission.WRITE_SETTINGS" />
  31. <uses-permission android:name="com.huawei.launcher2.permission.READ_SETTINGS" />
  32. <uses-permission android:name="com.huawei.launcher2.permission.WRITE_SETTINGS" />
  33. <uses-permission android:name="com.android.mylauncher.permission.READ_SETTINGS" />
  34. <uses-permission android:name="com.android.mylauncher.permission.WRITE_SETTINGS" />
  35. <uses-permission android:name="com.ebproductions.android.launcher.permission.READ_SETTINGS" />
  36. <uses-permission android:name="com.ebproductions.android.launcher.permission.WRITE_SETTINGS" />
  37. <uses-permission android:name="com.oppo.launcher.permission.READ_SETTINGS" />
  38. <uses-permission android:name="com.oppo.launcher.permission.WRITE_SETTINGS" />
  39. <uses-permission android:name="com.huawei.android.launcher.permission.READ_SETTINGS" />
  40. <uses-permission android:name="com.huawei.android.launcher.permission.WRITE_SETTINGS" />
  41. <uses-permission android:name="telecom.mdesk.permission.READ_SETTINGS" />
  42. <uses-permission android:name="telecom.mdesk.permission.WRITE_SETTINGS" />
  43. <uses-permission android:name="dianxin.permission.ACCESS_LAUNCHER_DATA" />

转载地址:http://blog.zanlabs.com/2015/03/14/android-shortcut-summary/

Android创建桌面快捷方式所遇到的问题与解决方案相关推荐

  1. android自动创建桌面,Android创建桌面快捷方式

    需求:点击按钮创建快捷方式 1.用户触发创创建事件时,在手机桌面创建指定页面的快捷方式. 2.当APP关闭时,点击桌面快捷方式打开APP,跳转至指定页面 3.当APP在后台是,点击桌面快捷方式,跳转至 ...

  2. android保存连接地址到桌面快捷方式,Android创建桌面快捷方式两种方法

    谈谈在桌面上直接生成.个人觉得这个比较爽快,既然都是快捷方式了干嘛还要再隐藏一层呢?当然喜欢桌面干净的就比较喜欢第二个了. 第一个是通过广播(Broadcast)的形式向Luncher发送请求生成快捷 ...

  3. Android 创建桌面快捷方式的方法

    在Android O及后续更高平台上,应用在桌面创建快捷方式的方法有了较多变更,从交互方式上趋向于让用户二次确认.代码如下: public static void addShortCut(Contex ...

  4. [Android]为指定的应用创建桌面快捷方式

    网上一搜一大把为自己的应用创建快捷方式,但是本文的侧重点在为"指定的应用"创建桌面快捷方式.     常见的桌面快捷方式有两要素:1.应用名 2.应用图标. 指定应用图标的信息是: ...

  5. android自动创建桌面,Android应用启动后自动创建桌面快捷方式的实现方法

    Android的开发其实是比较灵活的,其实在安装了Android应用程序之后,会在桌面上自动创建快捷方式,接下来爱站技术频道小编将会介绍Android应用启动后自动创建桌面快捷方式的实现方法给大家,有 ...

  6. Android应用启动后自动创建桌面快捷方式

    为什么80%的码农都做不了架构师?>>>    和IOS开发和Windows Phone开发相比,Android是开放的,Android上的开发也相对更加灵活,能够做很多事情.有的朋 ...

  7. Android应用程序创建桌面快捷方式

    2019独角兽企业重金招聘Python工程师标准>>> public static final String READ_SETTINGS_PERMISSION = "com ...

  8. Ubuntu 17.04系统创建Android Studio桌面快捷方式的方法

    下面以"Android Studio"为例,阐述Ubuntu系统中创建桌面快捷方式的方法,假设已将"Android Studio"下载到"/home/ ...

  9. Android小技巧之创建桌面快捷方式(已适配8.0)

    前言 在某些情况下需要对应用创建桌面快捷方式,特别是在使用原生系统(4.3到8.0)运行物联网app时,往往存在二级菜单,而应用图标默认安装在二级菜单(现在国内主流手机厂商的系统都没有二级菜单了),如 ...

最新文章

  1. 【Linux就该这么学 20期培训笔记 02】新手必须掌握的 Linux 命令
  2. 第二十五讲 用线性代数解微分方程组
  3. 精选文章 什么是跨域?怎么解决跨域问题?
  4. CG CTF WEB 签到题
  5. 【Qt】New Features in Qt 5.15
  6. asp.net core 环境(Development、Staging 、Production)
  7. Acwing 1088.旅行问题
  8. 【C#-枚举】枚举的使用
  9. php新窗口打开链接,wordpress如何设置在新窗口打开链接
  10. mysql binlog c++_MySQL binlog的格式解析
  11. 计算机电子设计论文,计算机毕业设计电子信息毕业设计电子信息毕业论文
  12. c语言自学基础知识视频,C语言 基础课堂视频教程
  13. java图片转换pdf_Java将图片转化为PDF的方法(1)
  14. Chap.20 总结《CL: An Introduction》 (Vyvyan Evans)
  15. 【转】用TrueType造字程序自己造字
  16. Vue环境搭建及第一个hello world
  17. Spring基础之IOCAOPDI
  18. minio存储之纠删码(Erasure Code)
  19. 安装mysql黑屏电脑卡死_电脑卡屏死机是怎么回事?
  20. arcgis10.8深度学习介绍课程梳理

热门文章

  1. 博士真正搞科研的姿势,是这样的丨知乎高赞
  2. 图对比学习入门 Contrastive Learning on Graph
  3. python解析torrent文件库:pytorrent
  4. 【错误记录】使用cv2报错 已解决
  5. Android使用Jack编译报错:communication error with Jack server (58) 解决方法
  6. 刺激却安全,普通人也能体验的方程式赛车
  7. mmsegmention数据集存放格式(三)
  8. 最近几个月学习情况的总结
  9. import statsmodels.api as sm 时 ImportError: DLL load failed while importing _arpack: 找不到指定的程序。
  10. js:秒转换为小时分钟秒格式