您的位置:首页 > 移动开发 > 微信开发

Android开发——Accessibility机制实现模拟点击(微信自动抢红包实现)

2016-11-10 21:48 525 查看
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<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(), 另一种是通过View的ID,List<AccessibilityNodeInfo> 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类[java] view plain copy 



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<CharSequence> texts = event.getText()通知栏上的text,再去循环判断是否含有“微信红包”字样即可。如果含有,就通过如下代码打开通知栏。[java] view plain copy 



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



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

3.3 最后要在Manifest.xml中配置我们的服务。[java] view plain copy 



<service  
   android:name=".MyAccessibility <span style="font-family: 'Microsoft YaHei';">"</span>  
   android:enabled="true"  
   android:exported="true"  
   android:label="@string/app_name"  
   android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >  
   <intent-filter>  
       <action android:name="android.accessibilityservice.AccessibilityService" />  
   </intent-filter>  
   //这个声明是对这个AccessibilityService的配置  
   <meta-data  
       android:name="android.accessibilityservice"  
       android:resource="@xml/qianghongbao_service_config" />  
lt;/service>  
3.4 其中xml/qianghongbao_service_config是做了初始化的工作,具体实现如下。[java] view plain copy 



<?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="flagDefault"  
    android:canRetrieveWindowContent="true"  
    android:description="@string/accessibility_description"  
    android:notificationTimeout="50"  
    android:packageNames="com.tencent.mm" />  
  
    <!--typeAllMask是设置响应事件的类型,typeAllMask当然就是响应所有类型的事件-->  
    <!--feedbackGeneric是设置回馈给用户的方式,有语音播出和振动。可以配置一些TTS引擎,让它实现发音。-->  
    <!--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的,这个后面会写文介绍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: