前文快捷方式适配(1)介绍了快捷方式踩坑之资源文件索引混乱和系统版本适配两个方向遇到的问题及解决方案,这篇文章继续介绍踩坑之旅。
权限适配
Android的碎片化生态一直是让开发者头大的问题,其中需要很多的适配问题,6.0后增加了动态申请危险权限的功能,但是某些非危险权限,各个rom的实现也是参次不齐。快捷方式的权限就是如此。
Google的Pixel系列和三星的部分手机,是默认会给创建快捷方式的权限的。但是国内的一些手机对这个权限管制的很严而且也很不友好,无法去动态申请,根本不清楚当前app到底有没有获取到权限,创建的成功率也随之降低很多。
为了提高成功率及优化用户使用体验,我们需要知道主流机型的权限是否获取到,然后根据是否有无权限给用户引导或者直接创建快捷方式。下面列举一下目前各大厂手机的权限识别:
1.1小米手机
小米手机众所周知,获取权限可以通过AppOpsManager获取到,但是快捷方式具体代表值可以暴力循环试出来:
AppOpsManager mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
String pkgName = context.getApplicationContext().getPackageName();
int uid = context.getApplicationInfo().uid;
Class<?> appOpsClass = Class.forName(AppOpsManager.class.getName());
Method checkOpNoThrowMethod = appOpsClass.getDeclaredMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE, String.class);
Object invoke = checkOpNoThrowMethod.invoke(mAppOps, 10017, uid, pkgName);//INSTALL_SHORTCUT is 10017
1.2Vivo
vivo通过创建成功过程抓trace可以发现里面有通过ContentProvider判断是否有权限的流程,然后找到对应的url再找到了可判断是否有权限的字段shortcutPermission
ContentResolver contentResolver = context.getContentResolver();
if (contentResolver == null) {
Logger.get().log(TAG, "contentResolver is null");
return PERMISSION_UNKNOWN;
}
Uri parse = Uri.parse("content://com.bbk.launcher2.settings/favorites");
query = contentResolver.query(parse, null, null, null, null);
if (query == null) {
Logger.get().log(TAG, "cursor is null (Uri : content://com.bbk.launcher2.settings/favorites)");
return PERMISSION_UNKNOWN;
}
1.3 Oppo
oppo手机和vivo类似也可以通过ContentProvider的内容获取到是否有权限
ContentResolver contentResolver = context.getContentResolver();
if (contentResolver == null) {
Logger.get().log(TAG, "contentResolver is null");
return PERMISSION_UNKNOWN;
}
Uri parse = Uri.parse("content://settings/secure/launcher_shortcut_permission_settings");
Cursor query = contentResolver.query(parse, null, null, null, null);
if (query == null) {
Logger.get().log(TAG, "cursor is null (Uri : content://settings/secure/launcher_shortcut_permission_settings)");
return PERMISSION_UNKNOWN;
}
try {
String pkg = context.getApplicationContext().getPackageName();
while (query.moveToNext()) {
String value = query.getString(query.getColumnIndex("value"));
Logger.get().log(TAG, "permission value is " + value);
if (!TextUtils.isEmpty(value)) {
if (value.contains(pkg + ", 1")) {
return PERMISSION_ALLOW;
}
if (value.contains(pkg + ", 0")) {
return PERMISSION_DENIED;
}
}
}
return PERMISSION_UNKNOWN;
} catch (Exception e) {
Logger.get().log(TAG, e.getMessage(), e);
return PERMISSION_UNKNOWN;
} finally {
query.close();
}
1.4华为
华为在创建过程抓到的trace里看到通过PermissionManager.canSendBroadcast,部分版本正常,高版本试过了失效了,可以参考下,目前没再深入研究高版本了(有方案的话可以下方评论下)
Class<?> PermissionManager = Class.forName("com.huawei.hsm.permission.PermissionManager");
Method[] declaredMethods = PermissionManager.getDeclaredMethods();
Method[] methods = PermissionManager.getMethods();
Method canSendBroadcast = PermissionManager.getDeclaredMethod("canSendBroadcast", Context.class, Intent.class);
boolean invokeResult = (boolean) canSendBroadcast.invoke(PermissionManager, context, intent);
其他机型没有再适配了,当时做的也比较仓促,代码也比较老旧了,可能新版本适配还有些问题(望各位大神有新方案可以分享下,互相交流)