Accessbility 又叫做辅助功能,是Android官方推出帮助身体不便或者操作不灵活的人来辅助操作手机应用的。当然也可以用来干一些别的事,比如自动抢红包啊,静默安装app,帮助用于开一系列权限操作等。出于项目需求,大概研究了下Accessilibity的基本用法。
要实现自己的辅助功能,需要继承系统的AccessibilityService服务类。然后实现其中的抽象方法,这些方法被系统调用的顺序是:当辅助功能服务开始时onServiceConnected()方法会被调用,当辅助功能正在运行时onAccessibilityEvent(), onInterrupt()方法会被调用,当辅助功能结束时onUnbind()方法会被调用。以上也就是辅助功能的生命周期了。
想要实现自己的辅助功能,你需要在Manifest配置文件中注册该服务,且申明相应的权限。如此,你才能接收到系统的服务功能服务。
为了实现辅助功能,你需要在Manifest中添加如下代码:
<application>
<service android:name=".MyAccessibilityService"
android:label="@string/accessibility_service_label"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
intent-filter>
service>
application>
注意 1. action 是固定的“android.accessibilityservice.AccessibilityService”用于系统启动我们的辅助功能
注意2. 你必须给你的辅助功能服务申请权限 android:permission=”android.permission.BIND_ACCESSIBILITY_SERVICE”,否者不能使用辅助功能。
辅助功能必须指定一个辅助事件配置文件来处理和过滤指定的事件。辅助功能配置可以由AccessibilityServiceInfo来实现,在辅助功能服务的onServiceConnected()方法中调用AccessibilityServiceInfo类的setServiceInfo()方法来配置。
当然从Android 4.0开始,你也可以在manifest中添加 标签来未辅助功能指定一个xml的配置。代码如下:
".MyAccessibilityService">
...
"android.accessibilityservice"
android:resource="@xml/accessibility_service_config" />
标签指向一个XML资源文件,文件路径(/res/xml/accessibility_service_config.xml). 示例代码如下:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/accessibility_service_description"
android:packageNames="com.example.android.apis"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFlags="flagDefault"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
/>
如上配置可以指定该辅助功能监听哪个应用的操作,允许处理哪些事件,用户点击事件反馈的类型,查找节点方式,超时时间,是否允许辅助功能获得窗口节点信息等。配置文件属性解释如下:
从一个AccessibilityEvent中调查完全视图层级的能力隐式地暴露私有用户信息给你的无障碍服务。出于这个原因,你的服务必须通过无障碍服务配置XML文件请求这个级别的访问权,通过包含canRetrieveWindowContent属性和设置它为true。如果你不在你的服务配置xml文件中包含这个设置,那么对getSource()的调用会失败。
想要了解更多更详细的配置,请阅读AccessibilityServiceInfo类。该类就是用于辅助功能服务配置信息类。
如下代码判断辅助功能是否开启,如果没有开启,则跳转到系统页面去开启。
/**
* 该辅助功能开关是否打开了
* @param accessibilityServiceName:指定辅助服务名字
* @param context:上下文
* @return
*/
private boolean isAccessibilitySettingsOn(String accessibilityServiceName, Context context) {
int accessibilityEnable = 0;
String serviceName = context.getPackageName() + "/" +accessibilityServiceName;
try {
accessibilityEnable = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0);
} catch (Exception e) {
Log.e(TAG, "get accessibility enable failed, the err:" + e.getMessage());
}
if (accessibilityEnable == 1) {
TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
String settingValue = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
mStringColonSplitter.setString(settingValue);
while (mStringColonSplitter.hasNext()) {
String accessibilityService = mStringColonSplitter.next();
if (accessibilityService.equalsIgnoreCase(serviceName)) {
Log.v(TAG, "We've found the correct setting - accessibility is switched on!");
return true;
}
}
}
}else {
Log.d(TAG,"Accessibility service disable");
}
return false;
}
/**
* 跳转到系统设置页面开启辅助功能
* @param accessibilityServiceName:指定辅助服务名字
* @param context:上下文
*/
private void openAccessibility(String accessibilityServiceName, Context context){
if (!isAccessibilitySettingsOn(accessibilityServiceName,context)) {
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivity(intent);
}
}
首先我们可以通过如下方法获取当前页面节点信息AccessibilityNodeInfo:
以上两个方法都可以获取到当前页面的节点信息,然后我们可以通过AccessibilityNodeInfo来查找具体的View,然后对该View操作。查找View有两如下两种方法:
我们一般都通过指定的Text内容来查找,这样查找方便,简单
我们通过AccessibilityNodeInfo#findAccessibilityNodeInfosByText()查找到指定的node节点后,就可以通过相应的方法模拟点击事件了。方法如下:
比如点击事件代码如下:
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
用户常用的行为事件类型一般有如下:
虽然很多手机支持辅助功能,但是由于各手机厂商定制或者自定义View,没有严格按照Google标准来,使得有些界面使用了自定义的View,导致辅助功能找不到其节点,从而使得即使有辅助功能,也不能模拟点击View操作。