一、AccessibilityService
根据官方的介绍,是指开发者通过增加类似contentDescription的属性,从而在不修改代码的情况下,让残障人士能够获得使用体验的优化,大家可以打开AccessibilityService来试一下,点击区域,可以有语音或者触摸的提示,帮助残障人士更好的使用App
官方文档:https://developer.android.com/guide/topics/ui/accessibility/service
二、AccessibilityService 开发流程
- 确定执行脚本的APK安装包
- 通过UIAutomator 获取包名及UI控件ID,或者下载一个开发者助手apk,也可以进行控件ID获取,代码君已经帮你下载好了,需要自取
http://share.dmjzy.cn/f/17143538-501591536-a374b2(访问密码:8401) - 编写脚本代码
- 调试、兼容性处理
三、核心代码
- AccessibilityService主要是实现onAccessibilityEvent
public class AccessibilitySampleService extends AccessibilityService{
/**当无障碍服务连接之后回调*/
@Override
public void onServiceConnected() {
super.onServiceConnected()
}
/**当触发了需要监听的无障碍事件后回调*/
@Override
public void onAccessibilityEvent(AccessibilityEvent event){
// 获取包名
String pkgName = event.getPackageName().toString();
int eventType = event.getEventType();
AccessibilityOperator.getInstance().updateEvent(this, event);
//过滤出目标包,如果要检测所有包,可以去掉此判断
if (pkgName.equals(pageName)) {
AccessibilityLog.printLog("eventType: " + eventType + " pkgName: " + pkgName);
switch (eventType) {
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
//执行具体的脚本
toOperator();
break;
case AccessibilityEvent.TYPE_VIEW_CLICKED:
break;
case AccessibilityEvent.TYPE_VIEW_LONG_CLICKED:
break;
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
break;
}
}
}
/**无障碍服务断开后回调*/
@Override
public void onInterrupt(){
// TODO Auto-generated method stub
}
}
方法说明
1. onServiceConnected
当声明的无障碍服务连接之后, 系统会回调此方法.
在这个方法里, 可以做一些初始化工作. 比如保存服务的实例 标识服务连接的状态等.
也可以通过android.accessibilityservice.AccessibilityService#getServiceInfo动态更改xml配置文件中声明的无障碍配置信息.
2. onAccessibilityEvent
当监听的事件触发时, 系统会回调此方法, 比如view被点击了 window内容改变了等.
可以用android.view.accessibility.AccessibilityRecord#getSource获取对象AccessibilityNodeInfo, 这个对象就是无障碍操作的核心对象, 通常可以理解为android开发中的view控件.
可以通过AccessibilityNodeInfo对象, 进行控件的点击操作 输入文本操作 滚动操作 获取文本操作等
3. onInterrupt
当中途关闭了无障碍服务时回调, 通常这个时候无障碍服务不可用, 调用api都会失败.
4. AccessibilityService 其他方法说明
方法名 | 方法说明 |
disableSelf() | 禁用当前服务,也就是在服务可以通过该方法停止运行 |
findFoucs(int falg) | 查找拥有特定焦点类型的控件 |
getRootInActiveWindow() | 如果配置能够获取窗口内容,则会返回当前活动窗口的根结点 |
performGlobalAction(int action) | 执行全局操作,比如返回,回到主页,打开最近等操作,此方法可以模拟用户点击返回键和home键,操作见下面的官方文档 |
setServiceInfo(AccessibilityServiceInfo info) | 设置当前服务的配置信息 |
getSystemService(String name) | 获取系统服务 |
onKeyEvent(KeyEvent event) | 如果允许服务监听按键操作,该方法是按键事件的回调,需要注意,这个过程发生了系统处理按键事件之前 |
更多AccessibilityService参数说明见官方文档:
https://developer.android.google.cn/reference/kotlin/android/accessibilityservice/AccessibilityService
AccessibilityEvent
字段名 | 字段说明 |
TYPE_NOTIFICATION_STATE_CHANGED | 通知栏状态变化 |
TYPE_VIEW_CLICKED | 视图被点击 |
TYPE_WINDOW_CONTENT_CHANGED | 窗口内容变化 |
TYPE_WINDOW_STATE_CHANGED | 窗口状态变化,即切换activity |
- AndroidManifest.xml注册服务
<!-- 注册辅助功能服务-->
<service
android:name=".AccessibilitySampleService"
android:exported="true"
android:label="码君助手"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:process=":BackgroundService">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<!-- 通过xml文件完成辅助功能相关配置,也可以在onServiceConnected中动态配置-->
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_config" />
</service>
- 在资源文件夹新增xml文件夹,新建accessibility_config文件,代码如下
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:canRetrieveWindowContent="true"
android:canPerformGestures="true"
android:description="@string/accessibility_desc"
android:notificationTimeout="10" />
<!-- canPerformGestures //申请手势权限-->
<!--accessibility_desc:码君助手,让你的手机更智能一点 -->
accessibility_config说明
官方文档说明:https://developer.android.google.cn/reference/android/R.styleable#AccessibilityService
这里列举一些比较常用的
字段名 | 字段说明 |
accessibilityEventTypes | 表示该服务对界面中的哪些变化感兴趣,即哪些事件通知,比如窗口打开,滑动,焦点变化,长按等.具体的值可以在AccessibilityEvent类中查到,如typeAllMask表示接受所有的事件通知 |
accessibilityFeedbackType | 表示反馈方式,比如是语音播放,还是震动。feedbackGeneric代表所有 |
canRetrieveWindowContent | 表示该服务能否访问活动窗口中的内容.也就是如果你希望在服务中获取窗体内容的化,则需要设置其值为true |
notificationTimeout | 接受事件的时间间隔,通常将其设置为100即可 |
packageNames | 表示对该服务是用来监听哪个包的产生的事件。如果不写代表监听所有的应用。中间可以用";"来分割。 |
canPerformGestures | 表示可以执行手势属性 |
canTakeScreenshot | 是否能够截屏 |
4、编写执行脚本
try {
Thread.sleep(2000);
AccessibilityOperator.getInstance().clickById("com.xxxx.packagename:id/btn_later");// 关闭弹框
Thread.sleep(1000);
AccessibilityOperator.getInstance().clickById("com.xxxx.packagename:id/tab_work");//切换到工作tab
AccessibilityLog.printLog("切换到工作tab: ");
} catch (InterruptedException e) {
e.printStackTrace();
}
AccessibilityNodeInfo
方法名 | 方法说明 |
findAccessibilityNodeInfosByText() | 通过字符串查找节点元素 |
findAccessibilityNodeInfosByViewId() | 通过视图id查找节点元素 |
performAction() | 在节点上执行一个动作,比如点击、向上滑动等,更多操作见下面的官方文档 |
getParent() | 获取父节点 |
getChild() | 获取子节点 |
isEnabled() | 判断节点是否激活 |
isClickable() | 判断节点是否可以点击 |
isScrollable() | 判断节点是否可以滚动 |
isSelected() | 判断节点是否选中 |
isPassword() | 判断节点是否是密码输入框 |
isFocusable() | 判断节点是否可以获取焦点 |
getText() | 获取节点的文本信息 |
getContentDescription() | 节点的内容描述 |
getViewIdResourceName() | 获取节点控件的id ,获取到的值大概是这样的:com.ss.android.ugc.aweme:id/afy |
getBoundsInScreen() | 获取节点在屏幕中的位置 |
getClassName() | 获取节点的类型/类名,值:android.widget.LinearLayout |
getChild() | 获取子节点的AccessibilityNodeInfo信息 |
更多请查看官方文档:
https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityNodeInfo