1. 何为Accessibility机制


许多Android使用者因为各种情况导致他们要以不同的方式与手机交互。对于那些由于视力、听力或其它身体原因导致不能方便使用Android智能手机的用户,Android提供了Accessibility功能和服务帮助这些用户更加简单地操作设备,包括文字转语音、触觉反馈、手势操作、轨迹球和手柄操作。开发者可以搭建自己的Accessibility服务,这可以加强应用的可用性,例如声音提示,物理反馈,和其他可选的操作模式。

随着Android系统版本的迭代,Accessibility功能也越来越强大,它能实时地获取当前操作应用的窗口元素信息,并能够双向交互,既能获取用户的输入,也能对窗口元素进行操作,比如点击按钮。Accessibility功能在使用时需要经过用户授权,如果用户拒绝授权,应用将无法实现本身的功能。
需要注意的是,此机制是免Root的,并且需要API14以上。以前做过的一个微信抢红包的小项目就是基于此机制实现的。这里稍微讲一下实现过程。

本文原创,转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/51912738


2. 我们要利用Accessibility机制里的哪些功能实现模拟点击


2.1 我们要使用的Accessibility机制中最常用的三个功能:

(1)拿到用户在指定APP里完成比如点击,滑动,或是屏幕内容变化等用户不可控的情况下的一些回调方法。

拿到这些回调的目的,在本例就是作为触发条件。我们不可能写一个线程,每时每刻都去关注界面上有没有红包,这样做不仅浪费用户的电量,而且可能会造成卡顿。

(2)获取当前操作应用的窗口元素信息

说白了为了点击,我们得知道根据什么去选择点哪个View,当然要提前拿到用户当前界面的一些View的信息。(包括一些TextView,ImageButton等,图片和视频是无法获得的。)我们可以拿到TextView和Button上的文本信息。这对于我们来说很关键。并且提供了筛选的功能,有两种筛选的方式,一种是通过文字内容,即List list = nodeInfo.findAccessibilityNodeInfosByText(), 另一种是通过View的ID,List list = nodeInfo.findAccessibilityNodeInfosByViewId()。返回的都是AccessibilityNodeInfo的节点集合。

(3)模拟点击

当我们找到目标View的时候,即可实现点击。其实就是一句话。n.performAction(AccessibilityNodeInfo.ACTION_CLICK)。

当然一般的TextView点不点也没什么效果。一般Button,ImageButton,还有大部分APP里面的最下面一栏ViewGrope里的选项按钮也是可以点的。

(这里可能有人要说了,能点的东西好少啊。但毕竟是免Root的,微信红包这种还是可以完成自动点击的,其实这个机制最恐怖的是上面所说的第一个功能,获取当前界面的部分View信息。如果抢红包App里加上几句恶意代码,拿到用户通讯录,聊天记录等极其私人的信息也是可以的。后面再把获取用户隐私信息的过程写一下吧,,这篇重点是Accessibility机制下的模拟点击)。

如果想点击屏幕上的任何一个位置,是需要Root的,在这篇文章有所介绍Android开发——后台获取用户点击位置坐标(可获取用户支付宝密码)。


3. 我们如何使用Accessibility机制

3.1 首先我们需要定义自己的类,并继承AccessibilityService类

public class MyAccessibility extends AccessibilityService {
    private static final String TAG = "MyAccessibility";
    @SuppressLint("NewApi")
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        // TODO Auto-generated method stub
        int eventType = event.getEventType();
        String eventText = "";
        Log.i(TAG, "==============Start====================");
        switch (eventType) {
        case AccessibilityEvent.TYPE_VIEW_CLICKED:
            eventText = "TYPE_VIEW_CLICKED";
            break;
        case AccessibilityEvent.TYPE_VIEW_LONG_CLICKED:
            eventText = "TYPE_VIEW_LONG_CLICKED";
            break;
        case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
            eventText = "TYPE_WINDOW_STATE_CHANGED";
            break;
        case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
            eventText = "TYPE_NOTIFICATION_STATE_CHANGED";
            break;
        case AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE:
            eventText = "CONTENT_CHANGE_TYPE_SUBTREE"
            break;
        }
        Log.i(TAG, eventText);
        Log.i(TAG, "=============END=====================");
    }
 
    @Override
    public void onInterrupt() {
        // TODO Auto-generated method stub
    }
}
通过这个类的onAccessibilityEvent方法,我们可以拿到用户在指定APP里完成比如点击,滑动,或是屏幕内容变化等用户不可控的情况下的一些回调方法,这里就作为上面所说的触发条件。这里我们选择TYPE_NOTIFICATION_STATE_CHANGED作为判断红包消息通知到来的触发条件,每当通知到来,我们就拿到List texts = event.getText()通知栏上的text,再去循环判断是否含有“微信红包”字样即可。如果含有,就通过如下代码打开通知栏。

Notification notification = (Notification) event.getParcelableData();
notification.contentIntent.send();
3.2 接着我们监听CONTENT_CHANGE_TYPE_SUBTREE或TYPE_WINDOW_STATE_CHANGED作为进入聊天界面的触发条件。接着根据View上的内容找到一组可以点击的View的集合。再通过for循环去选择点击最后一个红包,这里必须点一个,因为不能做到循环点击的话,因为我们无法点击返回的按钮,所以最好选择点最后一个,如果界面上不只有一个红包的话。这样点进去之后,继续调用getRootInActiveWindow()并拿到含有“拆红包”字样的节点点击即可。点击之后会停顿在领取成功的界面,这时,是不用管的,因为下一个微信红包到来,会继续从点击通知栏进入循环。


AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
List wxList = nodeInfo.findAccessibilityNodeInfosByText("领取红包");

3.3 最后要在Manifest.xml中配置我们的服务。

                      android:name=".MyAccessibility "
            android:enabled="true"
            android:exported="true"
            android:label="@string/app_name"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >
           
               
           

            //这个声明是对这个AccessibilityService的配置
                            android:name="android.accessibilityservice"
                android:resource="@xml/qianghongbao_service_config" />
       
3.4 其中xml/qianghongbao_service_config是做了初始化的工作,具体实现如下。

<?xml version="1.0" encoding="utf-8"?>
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_description"
    android:notificationTimeout="50"
    android:packageNames="com.tencent.mm" />
 
   
   
   

最后需要注意的是:

(1)微信必须开通知栏的设置。

(2)微信高版本测试失败,会卡在拆红包的地方。如果不介意可以尝试比较低的微信版本。我的百度网盘里有一个备份的比较低版本的微信apk包,亲测有效。http://pan.baidu.com/s/1skTw7yH

(3)手机必须是API14以上,一般是都满足的。

(4)很明显,在getRootInActiveWindow()时,遍历节点,再循环打印其getText()信息,是可以拿到用户通讯录以及聊天记录等信息的。但是拿到隐私需要“偷偷地”发送给作为“监听者”的我们,后面会专门写文介绍这个过程。

(5)nodeInfo.findAccessibilityNodeInfosByViewId()这个功能我们在本例中没有用到,其实也是很有用的,有些ImageButton上可能没有text内容,但是可以通过反编译apk文件拿到View的ID即可获取到这个节点。(这里需要注意的是,如果你想点击百度云的文件列表上的View,通过反编译是无法拿到他的ID的,因为如果你有开发经验,列表的适配器都是通过getView()去加载一个子布局,这样具体的某一行Item是不存在id这个概念的。)

(6)如果想点击屏幕上的任何一个位置,是需要Root的,这个后面会写文介绍。
--------------------- 
作者:SEU_Calvin 
来源:CSDN 
原文:https://blog.csdn.net/seu_calvin/article/details/51912738 
版权声明:本文为博主原创文章,转载请附上博文链接!

更多相关文章

  1. Qt 5.2正式版发布 全面支持移动平台
  2. Android中menu使用详解
  3. 总结】Android辅助功能(一)-AccessibilityEvent的分发
  4. android 4.2的新特性layoutRtl,让布局自动从右往左显示
  5. Android(安卓)培训计划:如何从功能机时代的 C/C++ 工程师转型至 A
  6. 保护你的隐私,五种控制Android应用的权限的方法
  7. Android(安卓)RIL的java框架
  8. android6.0M系统在代码中实现请求以及检查权限。
  9. android实现简单音乐播放器

随机推荐

  1. 在Android Studio中,如何在app中打开链接
  2. android listview单击事件
  3. android 关闭外音
  4. Android 之使用LocalBroadcastManager解
  5. Android检测系统中是否存在某进程
  6. 分享:android之hardwareAccelerated你不知
  7. android   点击屏幕让软件盘消失
  8. Android xmpp 资源列表
  9. Android 之如何删除eclipse自动生成的//T
  10. android - Jni 接口函数表