UI界面与常见权限互动
拟解决的问题
运行脚本前,我们往往需要用户开启一些必须的权限。由于用户水平不同,造成部分人群使用困难,甚至不知道需要先开启权限,这与无障碍权限“以人为本”的初衷相违背,因此我们需要一个直观的引导界面,以及方便的跳转方案,降低沟通成本,提升用户使用体验。
解决思路
1)枚举常见权限
2)获取权限状态
3)开启或跳转到权限开启界面
4)将权限状态与ui界面关联互动
具体步骤
我们运行脚本,常见的权限主要有无障碍服务、悬浮窗权限、前台服务、无障碍稳定模式、截图权限、查看使用统计权限、后台弹出权限(小米手机的miui系统),下面将对这7个权限的状态和开启办法逐一进行讨论。
无障碍服务
无障碍服务是否开启可以通过auto.service或auto.rootInActiveWindow获取,开启方式在上一篇博客中已经进行了详细的介绍,这里不再赘述。
悬浮窗权限
悬浮窗权限的判断autojspro已经有封装好的方法,可以直接用floaty.checkPermission()的返回值进行判断,此权限的跳转,这里介绍两种方法,一种是autojspro自带的基于context.startActivity封装的floaty.requestPermission(),用起来比较简单。另外介绍一种基于activity.startActivityForResult()封装的方法,可以在activity结束的回调里进行自己的操作,相对复杂,但是一些情形下用起来比较方便,同时也可以初步学习activity.startActivityForResult()的用法,不仅可以用在权限获取,在媒体库选取等方面也同样适用。这里仅展示原理以及相关代码,不做过多细致的讲解。
使用activity.startActivityForResult()有两个核心部分,一个发起带有标记的intent,另一个接收此标记的监听。
let mIntent = app.intent({
action: "android.settings.action.MANAGE_OVERLAY_PERMISSION",
data: "package:" + context.getPackageName(),
});
//这里把数字1作为标记
activity.startActivityForResult(mIntent, 1);
activity.getEventEmitter().on("activity_result", (requestCode, resultCode, data) => {
if (requestCode == 1) {
//requestCode为1说明是跳转到开启悬浮窗权限的activity结束的回调
toast(floaty.checkPermission());
//这里可以进行ui界面的同步
}
});
前台服务
前台服务的开启和查询,官方的Settings模块都进行了很好地封装,我们可以直接使用。
查询是否开启前台服务:$settings.isEnabled(‘foreground_service’)
设置开启前台服务:$settings.setEnabled(‘foreground_service’, true);
无障碍稳定模式
无障碍有一个稳定模式,使用场景不多,这里可以看安卓文档AccessibilityServiceInfo里的相关说明,大致意思就是不访问不重要的视图。
稳定模式的查询可以通过$settings.isEnabled('stable_mode')
来查询。
设置开启稳定模式:$settings.setEnabled(‘stable_mode’, true);
截图权限
目前新版的autojspro的images模块也支持了截图权限的查询、申请和注销,使用起来也很方便
查询是否有截图权限:$images.getScreenCaptureOptions()
请求截图权限:$images.requestScreenCapture(options)
注销截图权限:$images.stopScreenCapture()
这里特别提一下,安卓11以上设备,一般可以不申请截图权限,直接使用无障碍权限可以获取到ajtojspro的image对象
安卓11+使用无障碍权限截图:$automator.takeScreenshot()
作者注:这种方式截图频繁有限制,一般是1秒一次。
查看使用统计权限
这个权限一般使用较少,但个别时候也有大作用,比如为了currentPackage()获取最近包名更准确,或者获取某一应用的运行状态。
由于官方没有提供此权限的查询办法,这里我查阅安卓文档,自行封装了一份,可以判断是否有此权限
checkSystemService("usage_stats")
function checkSystemService(service) {
importClass(android.app.AppOpsManager);
appOps = context.getSystemService(context.APP_OPS_SERVICE);
mode = appOps.checkOpNoThrow("android:get_" + service, android.os.Process.myUid(), context.getPackageName());
return (granted = mode == AppOpsManager.MODE_ALLOWED);
}
跳转直接使用intent即可
app.startActivity({
action: "android.settings.USAGE_ACCESS_SETTINGS",
});
后台弹出权限(miui系统)
这个权限主要是会影响到app.launchPackage(packageName)打开未启动的应用,因此必要时我们可以判断是否有该权限,然后对用户进行提示,此权限由于是miui系统自己创造的,且没有文档说明,研究起来用了不少时间,这里我直接贴出来源码
checkMiuiPermission(10021);
function checkMiuiPermission(flag) {
//flag为10021是后台弹出界面,为10016是NFC权限
importClass(android.app.AppOpsManager);
let appOps = context.getSystemService(context.APP_OPS_SERVICE);
try {
let myClass = util.java.array("java.lang.Class", 3);
myClass[0] = java.lang.Integer.TYPE;
myClass[1] = java.lang.Integer.TYPE;
myClass[2] = java.lang.Class.forName("java.lang.String");
let method = appOps.getClass().getMethod("checkOpNoThrow", myClass);
let op = new java.lang.Integer(flag);
result = method.invoke(appOps, op, new java.lang.Integer(android.os.Process.myUid()), context.getPackageName());
return result == AppOpsManager.MODE_ALLOWED;
} catch (err) {
console.error(err);
}
}