一.Android Accessibility 简介
对于那些失明或低视力,色盲,耳聋或听力受损,以及运动技能受限的用户,Android
提供了Accessibility
(辅助功能/无障碍)更加简单地操作设备,包括文字转语音、触觉反馈、手势操作、轨迹球和手柄操作等。
二.简单使用
1.在你的AndroidManifest
中:
<!--
android:label(可选)在辅助功能(无障碍)的系统设置会使用该名称,若不设置,则会使用<applicationandroid:label
android:process(可选)把该服务设在单独进程中,进程名以[冒号:]开头,是本应用的私有进程,其它应用无法访问
android:permission(必需)添加权限以确保只有系统可以绑定到该服务
-->
<service
android:name=".MyAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
<!--在xml文件配置辅助功能,也可在onServiceConnected()中使用setServiceInfo()动态配置-->
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config"/>
</service>
2.在你的@xml/accessibility_service_config
中:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canPerformGestures="true"
android:canRetrieveWindowContent="true"
android:description="@string/accessibility_doc"
android:notificationTimeout="100"
tools:ignore="UnusedAttribute" />
<!--
android:canPerformGestures="true"允许发送手势android7.0以上
android:description辅助功能描述
android:packageNames指定辅助功能监听的包名,不指定表示监听所有应用
android:accessibilityEventTypes事件类型,typeAllMask表示接收所有事件
android:accessibilityFlags查找截点方式,一般配置为flagDefault默认方式
android:accessibilityFeedbackType操作按钮以后给用户的反馈类型,包括声音,震动等
android:notificationTimeout响应超时
android:canRetrieveWindowContent是否允许提取窗口的节点信息
-->
3.创建MyAccessibilityService
继承自 AccessibilityService
@SuppressLint("Registered")
public class MyAccessibilityService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
三.工具类封装
1.工具类AccessibilityServiceUtil
封装
abstract public class AccessibilityUtil {
//开关
public static boolean isService = false;
//判断服务是否开启
public static boolean isService(Context context, Class service) {
int enable = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0);
if (enable != 1) return false;
String services = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (!TextUtils.isEmpty(services)) {
TextUtils.SimpleStringSplitter split = new TextUtils.SimpleStringSplitter(':');
split.setString(services);
while (split.hasNext()) {
if (split.next().equalsIgnoreCase(context.getPackageName() + "/" + service.getName()))
return true;
}
}
return false;
}
//通过文字点击指定项
public static boolean clickText(AccessibilityService service, String string, int location) {
AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
if (accessibilityNodeInfo == null) return false;
List<AccessibilityNodeInfo> nodeInfoByText = accessibilityNodeInfo.findAccessibilityNodeInfosByText(string);
List<AccessibilityNodeInfo> nodeInfoList = new ArrayList<>();
if (nodeInfoByText != null && !nodeInfoByText.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoByText) {
if (nodeInfo != null && (nodeInfo.getText().equals(string) || nodeInfo.getContentDescription().equals(string))) {
nodeInfoList.add(nodeInfo);
}
}
}
if (nodeInfoList.isEmpty()) return false;
if (nodeInfoList.size() > location) {
return click(nodeInfoList.get(location));
} else {
return click(nodeInfoList.get(0));
}
}
//通过文字点击第一项
public static boolean clickText(AccessibilityService service, String string) {
AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
if (accessibilityNodeInfo == null) return false;
List<AccessibilityNodeInfo> nodeInfoByText = accessibilityNodeInfo.findAccessibilityNodeInfosByText(string);
if (nodeInfoByText != null && !nodeInfoByText.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoByText) {
if (nodeInfo != null && (nodeInfo.getText().equals(string) || nodeInfo.getContentDescription().equals(string))) {
return click(nodeInfo);
}
}
}
return false;
}
//通过文字长按指定项
public static boolean longClickText(AccessibilityService service, String string, int location) {
AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
if (accessibilityNodeInfo == null) return false;
List<AccessibilityNodeInfo> nodeInfoByText = accessibilityNodeInfo.findAccessibilityNodeInfosByText(string);
List<AccessibilityNodeInfo> nodeInfoList = new ArrayList<>();
if (nodeInfoByText != null && !nodeInfoByText.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoByText) {
if (nodeInfo != null && (nodeInfo.getText().equals(string) || nodeInfo.getContentDescription().equals(string))) {
nodeInfoList.add(nodeInfo);
}
}
}
if (nodeInfoList.isEmpty()) return false;
if (nodeInfoList.size() > location) {
return longClick(nodeInfoList.get(location));
} else {
return longClick(nodeInfoList.get(0));
}
}
//通过文字长按第一项
public static boolean longClickText(AccessibilityService service, String string) {
AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
if (accessibilityNodeInfo == null) return false;
List<AccessibilityNodeInfo> nodeInfoByText = accessibilityNodeInfo.findAccessibilityNodeInfosByText(string);
if (nodeInfoByText != null && !nodeInfoByText.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoByText) {
if (nodeInfo != null && (nodeInfo.getText().equals(string) || nodeInfo.getContentDescription().equals(string))) {
return longClick(nodeInfo);
}
}
}
return false;
}
//通过ID点击指定项
public static boolean clickId(AccessibilityService service, String id, int location) {
AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
if (accessibilityNodeInfo == null) return false;
List<AccessibilityNodeInfo> infoByViewId = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
List<AccessibilityNodeInfo> nodeInfoList = new ArrayList<>();
if (infoByViewId != null && !infoByViewId.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : infoByViewId) {
if (nodeInfo != null) {
nodeInfoList.add(nodeInfo);
}
}
}
if (nodeInfoList.isEmpty()) return false;
if (nodeInfoList.size() > location) {
return click(nodeInfoList.get(location));
} else {
return click(nodeInfoList.get(0));
}
}
//通过ID点击第一项
public static boolean clickId(AccessibilityService service, String id) {
AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
if (accessibilityNodeInfo == null) return false;
List<AccessibilityNodeInfo> infoByViewId = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
if (infoByViewId != null && !infoByViewId.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : infoByViewId) {
if (nodeInfo != null) {
return click(nodeInfo);
}
}
}
return false;
}
//通过ID长按指定项
public static boolean longClickId(AccessibilityService service, String id, int location) {
AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
if (accessibilityNodeInfo == null) return false;
List<AccessibilityNodeInfo> infoByViewId = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
List<AccessibilityNodeInfo> nodeInfoList = new ArrayList<>();
if (infoByViewId != null && !infoByViewId.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : infoByViewId) {
if (nodeInfo != null) {
nodeInfoList.add(nodeInfo);
}
}
}
if (nodeInfoList.isEmpty()) return false;
if (nodeInfoList.size() > location) {
return longClick(nodeInfoList.get(location));
} else {
return longClick(nodeInfoList.get(0));
}
}
//通过ID长按第一项
public static boolean longClickid(AccessibilityService service, String id) {
AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
if (accessibilityNodeInfo == null) return false;
List<AccessibilityNodeInfo> infoByViewId = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
if (infoByViewId != null && !infoByViewId.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : infoByViewId) {
if (nodeInfo != null) {
return longClick(nodeInfo);
}
}
}
return false;
}
//通过坐标点击
public static boolean coordinatesClick(AccessibilityService service, int x, int y) {
Path path = new Path();
path.moveTo(x, y);
GestureDescription.Builder builder = new GestureDescription.Builder();
GestureDescription gestureDescription = builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 200)).build();
return service.dispatchGesture(gestureDescription, new AccessibilityService.GestureResultCallback() {
@Override
public void onCompleted(GestureDescription gestureDescription) {
super.onCompleted(gestureDescription);
}
}, null);
}
//通过坐标长按
public static boolean coordinatesLongClick(AccessibilityService service, int x, int y) {
Path path = new Path();
path.moveTo(x, y);
GestureDescription.Builder builder = new GestureDescription.Builder();
GestureDescription gestureDescription = builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 1000)).build();
return service.dispatchGesture(gestureDescription, new AccessibilityService.GestureResultCallback() {
@Override
public void onCompleted(GestureDescription gestureDescription) {
super.onCompleted(gestureDescription);
}
}, null);
}
//自定义滑动
public static boolean coordinateSliding(AccessibilityService service, int x0, int y0, int x1, int y1, int ms) {
Path path = new Path();
path.moveTo(x0, y0);
path.lineTo(x1, y1);
GestureDescription.Builder builder = new GestureDescription.Builder();
GestureDescription gestureDescription = builder.addStroke(new GestureDescription.StrokeDescription(path, 0, ms)).build();
return service.dispatchGesture(gestureDescription, new AccessibilityService.GestureResultCallback() {
@Override
public void onCompleted(GestureDescription gestureDescription) {
super.onCompleted(gestureDescription);
}
}, null);
}
//点击
private static boolean click(AccessibilityNodeInfo node) {
if (node == null) return false;
if (node.isClickable()) {
return node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
} else {
click(node.getParent());
}
return false;
}
//长按
private static boolean longClick(AccessibilityNodeInfo node) {
if (node == null) return false;
if (node.isLongClickable()) {
return node.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
} else {
longClick(node.getParent());
}
return false;
}
//输入
@SuppressLint("ObsoleteSdkInt")
public static boolean InputText(AccessibilityService service, AccessibilityNodeInfo nodeInfo, String text) {
if (service == null) return false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Bundle arguments = new Bundle();
arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
return nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
ClipboardManager clipboard = (ClipboardManager) service.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("label", text);
if (clipboard != null) {
clipboard.setPrimaryClip(clip);
}
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);
}
return false;
}
//返回
@SuppressLint("ObsoleteSdkInt")
public static boolean Back(AccessibilityService service) {
if (service == null) return false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
return service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
} else {
return false;
}
}
//主页
@SuppressLint("ObsoleteSdkInt")
public static boolean Home(AccessibilityService service) {
if (service == null) return false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
return service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME);
} else {
return false;
}
}
//上滑
public static boolean ScrollForward(AccessibilityNodeInfo node) {
if (node == null || !node.isScrollable()) return false;
return node.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
}
//下滑
public static boolean ScrollBackward(AccessibilityNodeInfo node) {
if (node == null || !node.isScrollable()) return false;
return node.performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
}
//暂停
public static void Sleep(long millis) {
try {
Thread.sleep(millis);
} catch (Exception e) {
System.exit(0);
}
}
}
2.使用AccessibilityServiceUtil
,例如:
public class MyAccessibilityService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
//在服务正常的情况控制服务,isService默认为false,可在Activity中设置为true即可
if (AccessibilityUtil.isService) {
//通过坐标点击屏幕中的(500,500)位置直到成功
while (!AccessibilityUtil.coordinatesClick(this, 500, 500)) ;
//关闭服务,设置中仍然开启
AccessibilityUtil.isService = false;
}
}
@Override
public void onInterrupt() {
System.out.print("已停止服务");
}
}