Accessibilityservice学习
来源:互联网 发布:java输出到正三角形 编辑:程序博客网 时间:2024/05/21 15:51
accessibilityservice是android上用来做辅助功能的一个API,主要用来相应用户发送AccessibilityEvent的事件,具体可以google或者baidu一下,不过只支持Android 4.1之后的手机这里我写下具体的实现流程。
实现步骤
如果我们自己写一个基于Accessibility的应用,主要分为下面几步:
编写服务类
- 编写自己的服务类,需要继承自AccessibilityService
MyPocketService.java
public class MyPocketService extends AccessibilityService { private static final String TAG = "POCKET"; @Override protected void onServiceConnected() { super.onServiceConnected(); } public MyPocketService() { } @Override public void onAccessibilityEvent(AccessibilityEvent event) { int eventType = event.getEventType();// Log.d(TAG,"the eventType is :"+eventType); // AccessibilityNodeInfo accessibilityNodeInfo = event.getSource(); //得到被点击的对象 // getRootInActiveWindow(); 获得整个窗口对象 if (eventType== AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { //当当前窗口发生变化时候,重新遍历,否则后面的控件找不到 AccessibilityNodeInfo rootInActiveWindow = getRootInActiveWindow(); if (rootInActiveWindow == null) { Log.d(TAG,"the rootInActiveWindow is null"); } else { recycle(rootInActiveWindow); } } } public void recycle(AccessibilityNodeInfo rootInActiveWindow) { //只有一个子元素 if (rootInActiveWindow.getChildCount() == 0) { Log.d(TAG,"the rootInActiveWindow count is : 0"); } else { // 遍历所有子元素 for (int i = 0; i < rootInActiveWindow.getChildCount(); i++) { AccessibilityNodeInfo nodeInfo = rootInActiveWindow.getChild(i); Log.d(TAG,"the child "+i+"is :"+nodeInfo+"\n"); } } } @Override public void onInterrupt() { }}
上面的MyPocketService主要实现了下面的一些方法:
//响应AccessibilityEvent的事件public void onAccessibilityEvent(AccessibilityEvent event)// 打断获取事件的过程,可以用来关闭资源 public void onInterrupt() // 用来绑定服务的方法,可以做一些关于AccessibilityServiceInfo 的初始配置protected void onServiceConnected()
注册当前Service
在manifest里注册当前Service
<service android:name=".MyPocketService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" android:label="get pocket" > <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /> </service>
上面的写法基本上是固定的。必须添加权限android.permission.BIND_ACCESSIBILITY_SERVICE, 另外action的name也必须是android.accessibilityservice.AccessibilityService ,这里通过meta-data配置accessibility-service需要相应的事件以及其他属性。
添加配置文件
这里的配置文件名称需要和meta-data中配置的一样,在res/xml中新建一个accessibilityservice.xml,如下:
<?xml version="1.0" encoding="utf-8"?><accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/pocket_description" android:accessibilityEventTypes="typeAllMask" android:packageNames="com.android.settings" android:accessibilityFeedbackType="feedbackGeneric" android:notificationTimeout="100" android:accessibilityFlags="flagDefault" android:canRetrieveWindowContent="true" android:canRequestTouchExplorationMode="true" android:canRequestEnhancedWebAccessibility="true" />
上面的属性简单说明一下:
- packageNames: 指定要监听的应用的包名
- description: 这里的description是显示给用户看的提示语,即打开服务的时候的提示
- accessibilityEventTypes
指定在监听窗口中可以模拟那些事件,这里我填写的是typeAllMask表示所有事件,有如下事件:
/** * Mask for {@link AccessibilityEvent} all types. * * @see #TYPE_VIEW_CLICKED * @see #TYPE_VIEW_LONG_CLICKED * @see #TYPE_VIEW_SELECTED * @see #TYPE_VIEW_FOCUSED * @see #TYPE_VIEW_TEXT_CHANGED * @see #TYPE_WINDOW_STATE_CHANGED * @see #TYPE_NOTIFICATION_STATE_CHANGED * @see #TYPE_VIEW_HOVER_ENTER * @see #TYPE_VIEW_HOVER_EXIT * @see #TYPE_TOUCH_EXPLORATION_GESTURE_START * @see #TYPE_TOUCH_EXPLORATION_GESTURE_END * @see #TYPE_WINDOW_CONTENT_CHANGED * @see #TYPE_VIEW_SCROLLED * @see #TYPE_VIEW_TEXT_SELECTION_CHANGED * @see #TYPE_ANNOUNCEMENT * @see #TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY * @see #TYPE_GESTURE_DETECTION_START * @see #TYPE_GESTURE_DETECTION_END * @see #TYPE_TOUCH_INTERACTION_START * @see #TYPE_TOUCH_INTERACTION_END * @see #TYPE_WINDOWS_CHANGED * @see #TYPE_VIEW_CONTEXT_CLICKED */ public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
- accessibilityFlags: 可以指定一些附加参数
- canRetrieveWindowContent: 是否允许当前程序读取窗口节点的内容
- notificationTimeout: 设置响应时间
这里记录一下小插曲,Android studio中设置快捷键提示:
ctrl+alt+s
在左侧的导航框中点击 KeyMap。
接着在右边的树型框中选择 Main menu –> Code –> Completion.
接着需要做两件事:
1. 移除原来的Cycle Expand Word 的 Alt+斜杠 快捷键绑定。
2. 在 Basic 上点击右键,去除原来的 Ctrl+空格 绑定,然后添加 Alt + 斜杠 快捷键。
此时运行我们的程序,在setting下打开辅助功能,找到我们的程序,允许其使用辅助功能。
允许使用辅助功能之后,此时运行程序,由于我们上面监听的是setting应用,所以此时打开setting,控制台就会打印出当前AccessibilityNodeInfo的信息,如下:
使用Accessibility实现静默安装
可以看到360和UC都是使用Accessibility来实现的自动安装功能,我们也来模仿实现一个自动安装的功能,在正式开始之前,先看下AccessibilityNodeInfo主要的方法,AccessibilityNodeInfo就是界面当中的每一个节点。
AccessibilityNodeInfo rootInActiveWindowrootInActiveWindow.getChildCount(); //获得子元素的个数rootInActiveWindow.getChild(0); //根据索引获取子元素rootInActiveWindow.getText(); // 获得当前元素的文本内容rootInActiveWindow.canOpenPopup(); //是否可以打开dialogrootInActiveWindow.describeContents(); rootInActiveWindow.findAccessibilityNodeInfosByText(); //根据文字内容获得当前AccessibilityNodeInfo对象 rootInActiveWindow.getClassName();rootInActiveWindow.getPackageName(); rootInActiveWindow.findAccessibilityNodeInfosByViewId(); // 根据view的id获取当前AccessibilityNodeInfo对象
这里由于我们监听的是packageinstall的应用,因此需要将配置文件进行更改如下:
android:packageNames="com.android.packageinstaller"
先来看下点击install的时候,使用UIAutomator找到需要点击按钮的id,如下图:
ok,先关id找到以后,开始实现啦:首先要做的就是使当前界面跳转到安装应用的界面,使用下面的代码即可:
//APK_PATH为当前apk的全路径Uri uri = Uri.fromFile(new File(APK_PATH));Intent localIntent = new Intent(Intent.ACTION_VIEW);localIntent.setDataAndType(uri, "application/vnd.android.package-archive");startActivity(localIntent);
当当前界面显示的是“com.android.packageinstaller”包所在的界面,此时就会回调onAccessibilityEvent方法,我们在该方法里实现自动点击需要点击的button
if (eventType== AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { //当当前窗口发生变化时候,重新遍历,否则后面的控件找不到 AccessibilityNodeInfo rootInActiveWindow = getRootInActiveWindow(); if (rootInActiveWindow == null) { Log.d(TAG,"the rootInActiveWindow is null"); } else { recycle(rootInActiveWindow); } }
可以看到上面的代码主要使用recycle方法来不断遍历当前界面的view找到需要点击的button,在进行模拟人为点击,recycle方法如下:
public void recycle(AccessibilityNodeInfo rootInActiveWindow) { if (rootInActiveWindow.getChildCount() == 0) { //只有一个子元素 Log.d(TAG,"the rootInActiveWindow count is : 0"); } else { for (int i = 0; i < rootInActiveWindow.getChildCount(); i++) { AccessibilityNodeInfo nodeInfo = rootInActiveWindow.getChild(i); // 这里根据UIAutomator来判断,我们当前需要点击的是button if (nodeInfo.getClassName().equals("android.widget.Button")) { String content = nodeInfo.getText().toString(); List<AccessibilityNodeInfo> okList = nodeInfo.findAccessibilityNodeInfosByViewId("com.android.packageinstaller:id/ok_button"); List<AccessibilityNodeInfo> openList = nodeInfo.findAccessibilityNodeInfosByViewId("com.android.packageinstaller:id/launch_button"); // if ((null != okList && okList.size() > 0)) { Log.d(TAG, "the node content is :" + content + "==the okbutton is :" + okList.get(0)); // 通过performAction来实现模拟点击事件 okList.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK); } // 安装完成之后点击open按钮,打开当前应用 if (null != openList && openList.size() > 0) { Log.d(TAG, "the node content is :" + content + "==the openButton is :" + openList.get(0)); openList.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK); } String cancelDelete = nodeInfo.getText().toString(); //这里的取消是我的荣耀手机上的是否删除软件安装包的,这里选择取消 if ("取消".equals(cancelDelete)) { nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); } } } } }
此时运行效果如下:
源码下载
- Accessibilityservice学习
- AccessibilityService
- AccessibilityService
- AccessibilityService
- AccessibilityService
- AccessibilityService
- AccessibilityService
- AccessibilityService
- Android系统学习-android.accessibilityservice(辅助服务)
- Accessibilityservice(辅助服务开发)学习笔记
- Android AccessibilityService(辅助服务)学习
- AccessibilityService的学习,抢红包实现
- 微信抢红包插件之Accessibilityservice学习
- 学习AccessibilityService实现微信抢红包插件
- android AccessibilityService的学习和应用
- [Android进阶]学习AccessibilityService实现微信抢红包插件
- AccessibilityService ClassNotFoundExcepiton
- AccessibilityService简介
- hdu 1598 find the most comfortable road 枚举+并查集
- Linux 下摄像头驱动支持情况
- python_简单的ssh客户端
- BestCoder Round #71 (div.2)-KK's Steel(斐波那契数列)
- [Spring实战系列](12)Bean的自动装配
- Accessibilityservice学习
- hdu KK's Steel【思维】【斐波那契数列应用】
- 大数据IMF 传奇 8台设备如何实现免密码的SSH登录呢 ?脚本分发 解决方案
- 自学帝国CMS---网站搬家
- 宿主机-开发板设置NFS共享目录(1)
- 学习Hibernate源码——配置文件解析,SessionFactory创建
- Leetcode 322. Coin Change
- 1238 Substrings
- android:qq的欢迎界面