快捷方式 应该来说 很多人都做过,我们就来看一下基本的快捷方式 是怎么实现的,会有什么问题?
首先 肯定要获取权限:
1 <!-- 添加快捷方式 --> 2 <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" /> 3 <!-- 移除快捷方式 --> 4 <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" /> 5 <!-- 查询快捷方式 --> 6 <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
然后定义一下我们要发起的action:
1 //删除快捷方式的action 2 public static final String ACTION_REMOVE_SHORTCUT = "com.android.launcher.action.UNINSTALL_SHORTCUT"; 3 //添加快捷方式的action 4 public static final String ACTION_ADD_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT";
然后写2个方法即可:
1 //删除快捷方式 2 public static void removeShortcut(Context context, Intent actionIntent, String name) { 3 Intent intent = new Intent(ACTION_REMOVE_SHORTCUT); 4 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name); 5 intent.putExtra("duplicate", false); 6 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, actionIntent); 7 context.sendBroadcast(intent); 8 } 9 //增加快捷方式 10 public static void addShortcut(Context context, Intent actionIntent, String name, 11 boolean allowRepeat, Bitmap iconBitmap) { 12 Intent addShortcutIntent = new Intent(ACTION_ADD_SHORTCUT); 13 // 是否允许重复创建 14 addShortcutIntent.putExtra("duplicate", allowRepeat); 15 // 快捷方式的标题 16 addShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name); 17 // 快捷方式的图标 18 addShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, iconBitmap); 19 // 快捷方式的动作 20 addShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, actionIntent); 21 context.sendBroadcast(addShortcutIntent); 22 }
这种方法,大部分的手机都能正常使用。但是在某些国产手机上会有各种各样的问题。比如hw的手机这样做就完全无效。
小米的手机 无法重复创建快捷方式等。那现在看看 有没有什么方法 能解决这些问题。
首先我们来看一张图:
这张图很清晰的告诉我们launcher这个应用下面 是有一个数据库的。
我们可以打开这个数据库 看看到底是什么?
你看这个就一目了然了,快捷方式 都是存在于这个表里面的!所以 我们除了发送广播 可以创建快捷方式以外,我们直接操作数据库也应该是可以创建快捷方式的。
但是我们要注意啊,各家的lanucher 都有很大不同的,我上面的截图 是用的模拟器,所以是官方的rom,那路径肯定就是常规的,但是深度定制的android系统
大家都知道各家lanucher都是自己在做,所以favorites 这张表的位置到底在哪里,这就是个问题了。我们需要先查找出来这张表到底在哪个位置才能好操作这个表,
要想知道这个表在哪个位置,我们首先要知道 对于这个rom来说,是哪个lanucher在启作用?包名是什么?
1 //此函数返回当前rom下的lanucher的包名 2 private String getCurrentLanucherPackageName(Context context) 3 { 4 //这个intent很好理解 就是启动lanucher的intent 5 Intent intent=new Intent(Intent.ACTION_MAIN); 6 intent.addCategory(Intent.CATEGORY_HOME); 7 //getPackageManager().resolveActivity 这个函数就是查询是否有符合条件的activity的 8 ResolveInfo res=context.getPackageManager().resolveActivity(intent,0); 9 //为避免空指针 我们要判定下空,虽然你我都知道这种情况不会发生 10 if(res==null||res.activityInfo==null) 11 { 12 return ""; 13 } 14 return res.activityInfo.packageName; 15 }
有些人可能对于4-5行还是理解不了,为什么这个相当于是启动lanucher的intent 其实很简单,我们在android studio里 run一个app的时候,我们通常都能看到下面这个界面:
你看android studio 实际上安装完我们的应用以后 也是通过 这个intent来启动手机里的lanucher 然后让lanucher来运行我们的app,因为studio也不知道你手机里的lanucher到底是哪个
所以只能用intent来完成,这也就是上述代码能必定成功运行的原因!否则的话 android studio 就无法把你的app run进去了~~
当然了你也可以进入shell ,打ps 命令看看到底是不是有这个 包名的 进程在 运行中~~
好 拿到了我们的lanucher的包名,下面一步就是去我们的包名下 操作那个数据库即可了。那显然这个步骤 最合适的就是contentprovider来操作了。
1 //此函数返回 要查找的permission的 provider的 authority 2 private String getAuthorityFromPermission(Context context, String permission) { 3 //返回安装的app的 provider的信息 4 List<PackageInfo> packs = context.getPackageManager().getInstalledPackages(PackageManager.GET_PROVIDERS); 5 //遍历获取到的安装包的信息 6 for (PackageInfo pack : packs) { 7 //每个安装包提供的provider 都在这个数组里面 8 ProviderInfo[] providers = pack.providers; 9 if (providers != null) { 10 //遍历每个provider 看需要的权限是否与我们传进来的权限参数相等 11 for (ProviderInfo providerInfo : providers) { 12 if (permission.equals(providerInfo.readPermission) || permission.equals(providerInfo.writePermission)) { 13 return providerInfo.authority; 14 } 15 } 16 } 17 } 18 return ""; 19 }
1 private String getAuthorityFromPermissionDefault(Context context) { 2 return getAuthorityFromPermission(context, "com.android.launcher.permission.READ_SETTINGS"); 3 }
1 private Uri getUriFromLauncher(Context context) { 2 StringBuilder uriStrBuilder = new StringBuilder(); 3 //为了速度考虑,这里我们先查找默认的 看是否能查找到 因为多数手机的rom还是用的默认的lanucher 4 String authority = getAuthorityFromPermissionDefault(context); 5 //如果找不到的话 就说明这个rom一定是用的其他的自定义的lanucher。那就拼一下 这个自定义的lanucher的permission再去查找一次 6 if (authority == null || authority.trim().equals("")) { 7 authority = getAuthorityFromPermission(context,getCurrentLanucherPackageName(context) + ".permission.READ_SETTINGS"); 8 } 9 uriStrBuilder.append("content://"); 10 //如果连上面的方法都查找不到这个authority的话 那下面的方法 就肯定查找到了 但是很少有情况会是如下这种 11 //多数都是else里面的逻辑 12 if (TextUtils.isEmpty(authority)) { 13 int sdkInt = android.os.Build.VERSION.SDK_INT; 14 if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的 15 uriStrBuilder.append("com.android.launcher.settings"); 16 } else if (sdkInt < 19) {// Android 4.4以下 17 uriStrBuilder.append("com.android.launcher2.settings"); 18 } else {// 4.4以及以上 19 uriStrBuilder.append("com.android.launcher3.settings"); 20 } 21 } else { 22 uriStrBuilder.append(authority); 23 } 24 uriStrBuilder.append("/favorites?notify=true"); 25 return Uri.parse(uriStrBuilder.toString()); 26 }
好,到这里 我们就能拿到所有rom 任意一款手机的 快捷方式 那张数据库表对应的uri了。
那到这里应该来说问题就基本解决了,你所有对快捷方式的操作 都可以直接通过这个uri来进行,无非就是一些crud的拼装。
再也不需要经过广播 那一道程序了,但是这里要注意的是 这种方法通常耗时都比较久,根据手机的性能的不同 200ms-600ms 才能完成uri的查找。所以记得做一下异步处理。
并且所有你对lanucher的 需求 都可以这么来做,比方说有些功能 在使用原生lanucher的手机上使用正常,在小米 华为 oppo使用不正常了,你就去找出来那个不正常的rom
的lanucher的 包名,然后找出他provider需要的权限,然后在你的manifest里 直接加权限 应该就可以正常使用了。第三方lanucher 引起的bug 基本上都可以通过这个方案
来解决。