Android-利用AccessibilityService监听推送和窗口变化

Accessibility Service是Android上的一种辅助功能,用户帮助一些视力、身体、年龄上有问题的用户,可以更好的与手机进行交互,本文以微信为例子,利用Accessibility Service来监听微信的推送和窗口变化。

设置AndroidManifest.xml

在AndroidManifest.xml中配置AccessibilityService:

1
2
3
4
5
6
7
8
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"/>

<service android:name=".MonitorService" 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="@xml/accessible_service_config"/>
</service>

设置AndroidManifest.xml

在res文件夹下新建xml文件夹,并添加上面定义好的:

accessible_service_config.xml

内容如下:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/app_name"
android:accessibilityEventTypes="typeAllMask" //监听所有的变化,推送,窗口,View变化等
android:accessibilityFeedbackType="feedbackAllMask" //反馈所有形式的变化,点击,滑动等
android:packageNames="com.tencent.mm" //监听的Apk包名
android:notificationTimeout="100" //超时时间
android:accessibilityFlags="flagDefault" //可用于过滤一些View,例如设置成FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
android:canRetrieveWindowContent="true"/> //是否可以对Window进行递归检索View

开发Service

新建上面定义好的Service类:

public class MonitorService extends AccessibilityService {

@SuppressLint("NewApi")
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    final int eventType = event.getEventType();
    Log.e("EventType", eventType + "");
    if (eventType == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
        if (Build.VERSION.SDK_INT < 18) {
            Notification notification = (Notification) event.getParcelableData();
            List<String> textList = getText(notification);
            if (null != textList && textList.size() == 2) {
                String user = textList.get(0);
                String content = textList.get(1);
                if (!TextUtils.isEmpty(content) && content.contains("[语音]")) {
                  Log.e("Push", user + "发来语音");
                  final PendingIntent pendingIntent = notification.contentIntent;
                  try {
                      pendingIntent.send();
                  } catch (PendingIntent.CanceledException e) {
                  }  
                }else{
                    if(content.indexOf(':') != -1){
                        content = content.substring(content.indexOf(':') + 1);
                    }
                    Log.e("Push", user + "发来消息:" + content);
                }
            }
        }
    }

    else if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
        String clazzName = event.getClassName().toString();
        Log.e("ClassName", clazzName);
    }
 }
}

上面,对微信的推送以及窗口变化进行了监听,当然,也可以用于自动抢红包,例如,在窗口变化时,监听是否有“领取红包”的View,如果有,执行模拟点击事件。

if (clazzName.equals("com.tencent.mm.ui.LauncherUI")) {
    AccessibilityNodeInfo nodeInfo = event.getSource();
    List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("领取红包");
    if (null != list && list.size() > 0) {
        AccessibilityNodeInfo node = list.get(list.size() - 1);
        node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
    }
}

如果利用findByText方法找不到时,可以通过递归,利用View的一些属性来取到Node:

private void traverseNode(AccessibilityNodeInfo node) {
    if (null == node) return;

    int count = node.getChildCount();
    if (count > 0) {
         for (int i = 0; i < count; i++) {
            AccessibilityNodeInfo childNode = node.getChild(i);
            if(null != childNode){
                    traverseNode(childNode);
         }
    }
}

其中的View的属性,可以利用AndroidSDK中工具进行查看:

AndroidSDK/tools/uiautomatorviewer

鼠标移动可以随意查看,点击可以选中:

Android-Decompile-Result