大家好,欢迎来到IT知识分享网。
本君初入辅助功能也是一头雾水,各种百度结果还是一知半解,得到的大多都是对辅助功能类的翻译,仅仅是理论层面上,到实战上确是千差万别,在此记之。
一、原理:大致简述一下,谷歌已经在View、ViewGroup、TextView等控件的文字改变、滑动、UI变化埋下了接口,当这些状态变化时控件会回调系统API,API系统然后对这些对象的数据进行组装,为了数据的安全性,系统会重新创建一些对象(AccessibilityEvent、AccessibilityNodeInfo)来间接保存这些数据,然后通过跨进程将这些数据返回给对应的Service中。
二、使用范围:首先,辅助功能不可能直接操作外部对象,辅助功能只能在本进程调用指定系统方法,由系统再分发给指定外部对象,辅助功能做的事基本和用户能做的差不多
三、实用场景:自动抢红包、自动安装APP等模拟操作(你能做的他都能),辅助用户操作、发音(本意是这样的),后台保活(不活如何辅助😏)
四、特别注意事项:AccessibilityEvent、AccessibilityNodeInfo里面的所有set方法均无效(这些只是方便系统把数据塞进去,跟我们没关系),我们能做的只有:get、is、find等获取数据的方法,以及极少的操作performAction,dispatchGesture等
实战第一步:配置辅助功能的Service:
<service
android:name="你的service名字(例如博主的.HongBaoService)"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="res/xml目录新增一个xml(例如博主@xml/qianghongbao)"/>
</service>
qianghongbao.xml文件:对应对的象及介绍AccessibilityServiceInfo
<?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:accessibilityFlags="flagReportViewIds|flagRetrieveInteractiveWindows"
android:canPerformGestures="true"
android:canRetrieveWindowContent="true"
android:description="@string/hongbao"
android:notificationTimeout="100"
android:packageNames="com.tencent.mm,com.android.systemui"
/>
accessibilityEventTypes:响应的事件类型(单击、长按、滑动、通知等),这里当然是全部事件啦
accessibilityFeedbackType:回显给用户的方式(例如:配置TTS引擎,实现发音),辅助嘛…
accessibilityFlags:很关键,你的应用程序需要获取哪些信息:1.flagDefault默认; 2.flagIncludeNotImportantViews显示所有视图节点(主要是效率,才会有这个属性,如下图FL套LL套TV,如果LL均没有任何可交互的属性(如没点击事件),则这个LL会被当做不重要的视图来处理,没有flagIncludeNotImportantViews属性时FL.getChild时只会得到TV);3.flagReportViewIds允许得到View的ID(对应AccessibilityNodeInfo.getViewIdResourceName()方法,如果不写此属性只会得到空); 其他标志flagRequestTouchExplorationMode、flagRequestEnhancedWebAccessibility、flagRequestFilterKeyEvents、flagRetrieveInteractiveWindows的具体介绍见AccessibilityServiceInfo类中以FLAG_开头的静态常量
canPerformGestures:允许app发送手势(api24及以上才可以使用手势),肯定true了
description:描述(会在开启辅助功能页面看到这段文字)
notificationTimeout:响应时间间隔100就好了
packageNames:需要辅助的app包名(比如博主的只针对wx和系统桌面),不写表示所有app
这些注释都可以在AccessibilityServiceInfo里面搜索到,建议去看看
第二步:写Service
public class HongBaoService extends AccessibilityService {
private final String TAG = getClass().getName();
public static HongBaoService mService;
//初始化
@Override
protected void onServiceConnected() {
super.onServiceConnected();
Utils.toast("O(∩_∩)O~~\r\n红包锁定中...");
mService = this;
}
//实现辅助功能
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
Utils.toast("(;′⌒`)\r\n红包功能被迫中断");
mService = null;
}
@Override
public void onDestroy() {
super.onDestroy();
Utils.toast("%>_<%\r\n红包功能已关闭");
mService = null;
}
// 公共方法
/**
* 辅助功能是否启动
*/
public static boolean isStart() {
return mService != null;
}
}
第三步:在MainActivity中检查并跳转到辅助功能
if (!HongBaoService.isStart()) {
try {
mActivity.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
} catch (Exception e) {
mActivity.startActivity(new Intent(Settings.ACTION_SETTINGS));
e.printStackTrace();
}
}
第四步:选择你的app,打开辅助功能
应该立即就可以看到 “红包锁定中……” 的提示语
对service的额外封装已经写在博客最后的demo中了,可以很方便的进行查找控件
常用的操作(核心逻辑就是performAction和dispatchGesture,想了解更多自己看源码,注释很详细)
ddms工具:快速查看手机上的id、text、ContentDescription、控件绝对位置等必不可少的工具,studio3.0以上的打方式:你Android SDK的目录>tools>monitor.bat(这是mac的bat,windows自己瞅瞅, SDK在MAC中的默认位置:当前用户/Library(资源库)/Android/SDK)(如果没有了请下载3.0版本的as:Android Studio 下载文件归档 | Android 开发者 | Android Developers)
performAction:发送click事件、修改/设置text文本、view获取焦点、点击home键、点击back键等具体见AccessibilityNodeInfo类以ACTION_开头静态常量以及注释会告诉你如何使用
dispatchGesture:分发手势,手势可以模拟用户全部操作,如手指点击、长按、滑动、多指触摸。进行中的手势只允许有一个,多次发送手势,之前正在进行的手势会被取消
findAccessibilityNodeInfosByText:目前大部分明文文本都能find到,但类似微信将关键数据改成了自定义view,不对外开放text内容(现在聊天的文本、好友的微信昵称都无法找到并且id每次更新都变化)
findAccessibilityNodeInfosByViewId:参数是com.xxx.xxx:id/tv_main,必须要带上包名
ContentDescription:被称为描述,webview页或者imageview中经常见到,没有id或text的控件基本只能用它了(小提示:还可以通过查找邻近的控件来获得),作用很大见demo的HongBaoService.findFirst、findAll及相关抽象类
通知栏点击操作
public void onAccessibilityEvent(AccessibilityEvent event) {
//通知栏,打开红包
switch (event.getEventType()) {//先判断是否是通知栏红包和转圈圈界面,这两个任何状态都会去点击
//第一步:监听通知栏消息,拦截通知的红包
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
for (CharSequence text : event.getText()) {
String content = text.toString();
//收到红包提醒
if (content.contains("[微信红包]") || content.contains("[QQ红包]")) {
//模拟打开通知栏消息,打开后会有新的广播进入微信或者qq
if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
HongBaoService.pingUnLock();//开屏,打开屏幕
final PendingIntent contentIntent = ((Notification) event.getParcelableData()).contentIntent;
//延时的handler(因为开屏有动画)
TimeUtil.mHandler.postDelayed(new Runnable() {
@Override
public void run() {
try {
contentIntent.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}
}, 500);
}
break;
}
}
break;
}
}
设置text文本操作(只可用于EditText)
AccessibilityNodeInfo textInfo = service.findFirst(AbstractTF.newClassName(ST_EDITTEXT));//假设只有一个edittext
if (textInfo != null) {
Bundle arguments = new Bundle();
arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "文本内容");
textInfo.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
}
分发手势操作(API>=24)
//发送一个点击事件
Path mPath=new Path();//线性的path代表手势路径,点代表按下,封闭的没用
mPath.moveTo(500, 500);
service.dispatchGesture(new GestureDescription.Builder().addStroke(new GestureDescription.StrokeDescription
(mPath, 手势开始时间比如立即开始0, 手势总时长比如100)).build(), 回调函数(可以为null), 回调的线程(null表示主线程));
//这是demo里封装好的,点击屏幕(100,1000)的地方
mService.dispatchGestureClick(100, 1000);
注意:addStroke是模拟的多指触摸,add一次表示一个手指,add多次表示多个手指。如果想横向滑动后再竖向滑动只能分多次调用dispatchGesture方法,举例如下:
//此为2次事件,不连续
Path path = new Path();
path.moveTo(0, 400);
path.lineTo(400, 400);
final GestureDescription.StrokeDescription sd = new GestureDescription.StrokeDescription(path, 0, 500);
//先横滑
service.dispatchGesture(new GestureDescription.Builder().addStroke(sd).build(), new AccessibilityService.GestureResultCallback() {
@Override
public void onCompleted(GestureDescription gestureDescription) {
super.onCompleted(gestureDescription);
//也可以使用handler延时1.5秒就不用在这里回调了
Path path2 = new Path();
path2.moveTo(600, 600);
path2.lineTo(600, 800);
final GestureDescription.StrokeDescription sd2 = new GestureDescription.StrokeDescription(path2, 1000, 500);
//滑完后再过1秒竖滑
service.dispatchGesture(new GestureDescription.Builder().addStroke(sd2)/*.addStroke(sd2)*/.build(), null, null);
}
@Override
public void onCancelled(GestureDescription gestureDescription) {
super.onCancelled(gestureDescription);
}
}, null);
//此为长按后再拖动,一次事件效果
//如果想适配API24和25,就写一个path左右来回移动5像素平均下来持续约1秒,然后再滑动
//API>=26
Path path = new Path();
path.moveTo(500, 1499);
path.lineTo(500, 1500);//连续手势至少移动1像素,不然会立即调用onCompleted导致出bug
final GestureDescription.StrokeDescription sd = new GestureDescription.StrokeDescription(path, 0, 1000, true);
HongBaoService.mService.dispatchGesture(new GestureDescription.Builder().addStroke(sd).build(), new AccessibilityService.GestureResultCallback() {
@Override
public void onCompleted(GestureDescription gestureDescription) {
super.onCompleted(gestureDescription);
// Toast.makeText(MainActivity.this, "手势成功", Toast.LENGTH_SHORT).show();
Path path2 = new Path();
path2.moveTo(500, 1500);//接着上一个
path2.lineTo(10, 1500);
final GestureDescription.StrokeDescription sd2 = sd.continueStroke(path2, 0, 1000, false);
HongBaoService.mService.dispatchGesture(new GestureDescription.Builder().addStroke(sd2).build(), null, null);
}
@Override
public void onCancelled(GestureDescription gestureDescription) {
super.onCancelled(gestureDescription);
Toast.makeText(MainActivity.this, "手势失败,请重启手机再试", Toast.LENGTH_SHORT).show();
}
}, null);
调用手机的back、home、最近任务、拉出通知栏
//AccessibilityService.GLOBAL_ACTION_BACK
//GLOBAL_ACTION_HOME
//GLOBAL_ACTION_NOTIFICATIONS
//GLOBAL_ACTION_RECENTS
service.performGlobalAction(mAction);
粘贴操作(只可用于EditText,包括复制功能)
AccessibilityNodeInfo idInfo = findFirst(AbstractTF.newId(mId));
if (idInfo == null) return;
((ClipboardManager) service.getSystemService(Context.CLIPBOARD_SERVICE))
.setPrimaryClip(ClipData.newPlainText("复制", "复制内容"));
idInfo.performAction(AccessibilityNodeInfo.FOCUS_INPUT);
idInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);//粘贴
idInfo.recycle();//尽量在最后都回收掉
辅助功能的具体属性及方法我就不多说了,BD上到处都是
中国信息无障碍产品联盟的翻译:信息无障碍研究会官网-信息无障碍,信息无障碍优化,适老化,残障青少年(/知识库/技术文档)
注意:再重申一遍AccessibilityEvent、AccessibilityNodeInfo里面的所有set方法均无用
问题1:点击开启没任何反应
你的app崩溃过导致的,Android通病,目前无解,请多加点非空判断以防止崩溃。引用楼下“风吹过01”的回复:重启一下手机就好了…
问题2:开了辅助app还是经常被杀死
由于厂商各种定制,部分手机一键清理、长时间在后台的话辅助功能也会被杀死,这种情况只能参照其他保活方案了。如果仅仅是想推送的话建议直接使用厂商推送,这样就不需要保活了。如果是自己玩或者目标群体能接受的话可以开启任务栏锁定+电池优化白名单,这样肯定不会被清理。
问题3:有些控件明明在手机上却获取不到
大家用辅助功能干啥我就不多说了,那些app对辅助功能的研究不比任何人差。前面说了,辅助功能原理是View、ViewGroup等控件回调给系统服务的,所以这些app直接屏蔽了相关调用方法。无法得到控件的地方:腾讯小程序、app使用腾讯WebView并且开启屏蔽、一些可薅羊毛的app的关键地方。解决方案:无解,已经是辅助功能的极限,可以尝试截屏后进行图像识别。
demo:GitHub – weimingjue/AccessibilityExample: 辅助功能(无障碍)的使用教程,适合新手快速入门(AccessibilityService)
AccessibilityExample: 辅助功能(无障碍)的使用教程,适合新手快速入门(AccessibilityService)
由于国内第三方厂商各种奇葩定制,demo可能会出现以下问题:
1.打开微信好友页看不到提示语,解决方法以下几种:
①一键清理所有的app(包括demo),重新运行app
②上述操作无效的话,重启手机,重新运行app
③上述操作依然无效的话,可能是第三方厂商屏蔽了Toast,请开启悬浮窗权限(华为最奇葩的定制)或者直接查看Logcat的日志打印
2.手势发送失败:重启手机即可
只能帮你们到这了
转载请注明出处:王能的博客android 辅助功能(无障碍) AccessibilityService 实战入门详解_findaccessibilitynodeinfosbytext_王能的博客-CSDN博客
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/23577.html