Android自动化之AccessibilityService

摘要:
随着Android版本的不断升级,Android无障碍功能越来越强大。在Android 4.0之前,系统辅助服务功能相对简单,只能单向访问窗口元素信息,例如访问输入框用户输入内容。其他学生已经使用Accessibility Service实现了红包抓取和自动安装。你可以自己百度。演示示例显示,我们将使用AccessibilityService来实现单击元素后所单击内容的语言反馈。测试助手包捕获应用程序是下载地址链接:http://pan.baidu.com/s/1pJVyo0z密码:00i6Manifest语句˂?

简介
demo示例说明
Manifest声明
AccessibilityService的XML配置文件
创建继承自AccessibilityService的服务类
MainActivity检测服务是否开启
UiAutomatorViewer
参考

简介

由于许多Android用户由于某些原因(视力,身体,年龄)要求他们以不同的方式与手机设备交互。

安卓提供了辅助功能特性和服务来帮助这些用户更容易的操作他们的设备,包括文字转语音(原生不支持中文,国内ROM可能会有,我的测试OPPO自带中文哟!),触觉反馈,手势操作,轨迹球和手柄操作。开发者可以利用这些服务使得程序更好用,也可以创建自己的辅助服务。

随着Android版本的不断升级,Android Accessibility功能也越来越强大,Android 4.0版本以前,系统辅助服务功能比较单一,仅仅能过单向获取窗口元素信息,比如获取输入框用户输入内容。到Android 4.1版本以后(现在主流厂商的版本都在KITKAT4.4),系统辅助服务增加了与窗口元素的双向交互,此时可以通过辅助功能服务操作窗口元素,比如点击按钮等。
由于系统辅助服务能够实时获取您当前操作应用的窗口元素信息,这有可能给你带来隐私信息的泄露风险,比如获取非密码输入框的输入内容等。同时通过辅助功能也可以模拟用户自动化点击应用内元素,也会带来一定的安全风险。

已经有其他同学使用AccessibilityService实现了抢红包~以及自动安装等,大家可以自行百度。

demo示例说明

接下来我们将通过AccessibilityService实现点击元素之后语言反馈所点击的内容。
测试辅助抓包应用为【微信】

下载地址
链接:http://pan.baidu.com/s/1pJVyo0z 密码:00i6

Manifest声明
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.leestar.example.accessibilityservice"
  4. android:versionCode="1"
  5. android:versionName="1.0" >
  6. <uses-sdk
  7. android:minSdkVersion="19"
  8. android:targetSdkVersion="21" />
  9. <application
  10. android:allowBackup="true"
  11. android:icon="@drawable/ic_launcher"
  12. android:label="@string/app_name"
  13. android:theme="@style/AppTheme" >
  14. <activity
  15. android:name=".MainActivity"
  16. android:label="@string/app_name" >
  17. <intent-filter>
  18. <action android:name="android.intent.action.MAIN" />
  19. <category android:name="android.intent.category.LAUNCHER" />
  20. </intent-filter>
  21. </activity>
  22. <service
  23. android:name=".AccessibilityServiceExample"
  24. android:label="@string/accessibility_service_label"
  25. android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >
  26. <intent-filter>
  27. <action android:name="android.accessibilityservice.AccessibilityService" />
  28. </intent-filter>
  29. <meta-data
  30. android:name="android.accessibilityservice"
  31. android:resource="@xml/accessibility_service_config" />
  32. </service>
  33. </application>
  34. </manifest>
AccessibilityService的XML配置文件

保存路径为<project_dir>/res/xml/accessibility_service_config.xml

  1. <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:description="@string/accessibility_service_description"
  3. android:packageNames="com.tencent.mm"
  4. android:accessibilityEventTypes="typeAllMask"
  5. android:accessibilityFlags="flagDefault"
  6. android:accessibilityFeedbackType="feedbackSpoken"
  7. android:notificationTimeout="100"
  8. android:canRetrieveWindowContent="true"
  9. android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"
  10. />

其中
android:packageNames为需要捕获的包名
android:accessibilityEventTypes为需要回调的事件
android:notificationTimeout为事件回调的延迟事件
其他具体含义请查询官方文档~

创建继承自AccessibilityService的服务类

具体见代码以及注释,里面也实现了语言反馈功能。

  1. package com.leestar.example.accessibilityservice;
  2. import android.accessibilityservice.AccessibilityService;
  3. import android.annotation.TargetApi;
  4. import android.content.Intent;
  5. import android.os.Build;
  6. import android.speech.tts.TextToSpeech;
  7. import android.util.Log;
  8. import android.view.accessibility.AccessibilityEvent;
  9. import android.view.accessibility.AccessibilityNodeInfo;
  10. public class AccessibilityServiceExample extends AccessibilityService {
  11. private AccessibilityNodeInfo rootNodeInfo;
  12. private TextToSpeech tts = null;
  13. private boolean ttsIsInit = false;
  14. /**
  15. * 可选。系统会在成功连接上你的服务的时候调用这个方法,在这个方法里你可以做一下初始化工作,例如设备的声音震动管理,
  16. * 也可以调用setServiceInfo()进行配置工作。
  17. */
  18. @Override
  19. protected void onServiceConnected() {
  20. // TODO Auto-generated method stub
  21. super.onServiceConnected();
  22. tts = new TextToSpeech(getApplicationContext(), new android.speech.tts.TextToSpeech.OnInitListener() {
  23. @Override
  24. public void onInit(int status) {
  25. if (status == TextToSpeech.SUCCESS) {
  26. ttsIsInit = true;
  27. }
  28. }
  29. });
  30. }
  31. /**
  32. * 可选。在系统将要关闭这个AccessibilityService会被调用。在这个方法中进行一些释放资源的工作。
  33. */
  34. @Override
  35. public boolean onUnbind(Intent intent) {
  36. // TODO Auto-generated method stub
  37. return super.onUnbind(intent);
  38. }
  39. /**
  40. * 根据XML的android:accessibilityEventTypes来进行事件回调,所有的业务逻辑都要在回调中完成
  41. */
  42. @TargetApi(Build.VERSION_CODES.KITKAT)
  43. @Override
  44. public void onAccessibilityEvent(AccessibilityEvent event) {
  45. rootNodeInfo = event.getSource();
  46. if (rootNodeInfo == null)
  47. return;
  48. switch (event.getEventType()) {
  49. case AccessibilityEvent.TYPE_VIEW_CLICKED:
  50. // 语音反馈
  51. if (ttsIsInit) {
  52. // event.getContentDescription根据触发事件节点来自动查找子树的ContentDescription
  53. //所以你会发现UIAutomatorViewer上的相关节点信息为空,但是event里赋值了。
  54. // event.getText同上
  55. if (event.getContentDescription() != null) {
  56. tts.speak(event.getContentDescription().toString(), TextToSpeech.QUEUE_ADD, null);
  57. } else if (event.getText() != null) {
  58. tts.speak(event.getText().toString(), TextToSpeech.QUEUE_ADD, null);
  59. }
  60. }
  61. // 获取精确节点信息,可以通过UIAutomatorViewer检索,实现相关模拟操作
  62. //AccessibilityNodeInfo提供了2种自动查找字节点的函数
  63. //rootNodeInfo.findAccessibilityNodeInfosByText
  64. //rootNodeInfo.findAccessibilityNodeInfosByViewId(viewId)
  65. if (rootNodeInfo.getChild(0) != null
  66. && rootNodeInfo.getChild(0).getClassName().equals("android.widget.TextView")
  67. && rootNodeInfo.getChild(0).getText() != null
  68. && rootNodeInfo.getChild(0).getText().toString().equals("我")) {
  69. // 如果发现按的是【我】,模拟全局返回按钮,即退出微信
  70. performGlobalAction(GLOBAL_ACTION_BACK);
  71. // 模拟点击元素
  72. // if (rootNodeInfo.isClickable()) {
  73. // rootNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
  74. // }
  75. }
  76. break;
  77. default:
  78. break;
  79. }
  80. Log.i("leestar", AccessibilityEvent.eventTypeToString(event.getEventType()));
  81. Log.i("leestar", rootNodeInfo.getClassName().toString());
  82. }
  83. /**
  84. * 这个在系统想要中断AccessibilityService返给的响应时会调用。在整个生命周期里会被调用多次。
  85. */
  86. @Override
  87. public void onInterrupt() {
  88. Log.i("leestar", "onInterrupt");
  89. }
  90. }
MainActivity检测服务是否开启
  1. package com.leestar.example.accessibilityservice;
  2. import java.util.List;
  3. import android.accessibilityservice.AccessibilityServiceInfo;
  4. import android.app.Activity;
  5. import android.content.Context;
  6. import android.content.Intent;
  7. import android.os.Bundle;
  8. import android.provider.Settings;
  9. import android.view.View;
  10. import android.view.WindowManager;
  11. import android.view.accessibility.AccessibilityManager;
  12. import android.widget.Button;
  13. public class MainActivity extends Activity {
  14. private Button button_switch;
  15. @Override
  16. protected void onCreate(Bundle savedInstanceState) {
  17. // TODO Auto-generated method stub
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.activity_main);
  20. button_switch = (Button) findViewById(R.id.button_switch);
  21. }
  22. @Override
  23. protected void onResume() {
  24. super.onResume();
  25. updateServiceStatus();
  26. }
  27. @Override
  28. protected void onDestroy() {
  29. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  30. super.onDestroy();
  31. }
  32. public void onButtonClick(View view) {
  33. startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
  34. }
  35. private void updateServiceStatus() {
  36. boolean ServiceEnabled = false;
  37. // 循环遍历所有服务,查看是否开启
  38. AccessibilityManager accessibilityManager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
  39. List<AccessibilityServiceInfo> accessibilityServices = accessibilityManager
  40. .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
  41. for (AccessibilityServiceInfo info : accessibilityServices) {
  42. if (info.getId().equals(getPackageName() + "/.AccessibilityServiceExample")) {
  43. ServiceEnabled = true;
  44. break;
  45. }
  46. }
  47. if (ServiceEnabled) {
  48. button_switch.setText("关闭测试服务");
  49. // Prevent screen from dimming
  50. getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  51. } else {
  52. button_switch.setText("开启测试服务");
  53. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  54. }
  55. }
  56. }
UiAutomatorViewer

在需要精确元素节点操作检索的时候,就需要用到UiAutomatorViewer工具,一般情况下,Android SDK下会自带这个工具
Android自动化之AccessibilityService第1张
找到uiautomatorviewer.bat即可,打开之后,点击看起来是这个样子的
Android自动化之AccessibilityService第2张
我想你应该知道怎么做了,树形结构UI,AccessibilityNodeInfo通过getChild、getParent尽情的识别吧~!

参考

http://www.android-doc.com/guide/topics/ui/accessibility/services.html

免责声明:文章转载自《Android自动化之AccessibilityService》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇DataGuard主备归档存在gap的处理办法iOS -转载-使用Navicat查看数据表的ER关系图下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

二维码(android)

我们都知道一般的二微码都是以不同形式显示有的是登入,有的是网页,其实二维码只是一个字符串。到这里我们就有一个疑问那二维码是这样判断是网站或者是登入功能的呢? 其实这些判断部分,二维码生成器早就给你完成了,我们只需导入一个core-3.2.1.jar包就行。那接下来我们来做一个简单的小案例,进一步了解二维码。 一、新建项目 1、我们建一个名为"weima"的...

Android——悬浮窗+侧边弹框+淡入淡出+背景shape+SeekBar调节手机亮度

悬浮窗实现原理:  悬浮窗要实现,需要用到几个关键的类。  WindowManager:声明了 addView() 、updateViewLayout()、removeView()三个方法的接口     要创建出悬浮窗:那就得使用addView(布局对象,布局参数)方法     要更新悬浮窗的数据,如实现拖动悬浮框:那就得使用updateViewLayou...

Android仿微信朋友圈图片查看器

转载请注明出处:http://blog.csdn.net/allen315410/article/details/40264551 看博文之前,希望大家先打开自己的微信点到朋友圈中去,细致观察是不是发现朋友圈里的有个“九宫格”的图片区域,点击图片又会跳到图片的具体查看页面,而且支持图片的滑动和缩放?这个功能是不是非经常常使用呢?!那么我今天正好做了这个D...

Android_动态壁纸介绍

Android的动态壁纸主要用到三个类: android.app.WallpaperManager:这个类主要用于获取系统壁纸的信息。再写动态壁纸的时候用不到。 android.service.wallpaper.WallpaperService:这个类作为服务在运行,同时负责产生一个Engine。 同时,在此类中有一个必须继承的方法为:nCreateE...

Android菜单详解(五)——使用XML生成菜单

回顾前面的几篇,我们都是直接在代码中添加菜单项,给菜单项分组等,这是比较传统的做法,它存在着一些不足。比如说,为了响应每个菜单项,我们需要用常量来保存每个菜单项的ID等。为此,Android提供了一种更好的方式,就是把menu也定义为应用程序的资源,通过android对资源的本地支持,使我们可以更方便地实现菜单的创建与响应。这一篇就介绍如何使用XML文件来...

实现Android Studio JNI开发C/C++使用__android_log_print输出Log

相信很多人在刚开始学习Android JNI编程的时候,需要输出Log,在百度Google搜索的时候都是说需要在Android.mk中加入LOCAL_LDLIBS+= -L$(SYSROOT)/usr/lib -llog ,其实这是在eclipse开发上的方式,Android Studio并不是这么使用。 Android Studio的Android.mk...