微信自动回复和自动抢红包实现原理(二):自动回复

阅读这篇文章前,你需要先了解AccessibilityService,可以先阅读我上一篇文章 
微信自动回复和自动抢红包实现原理(一):AccessibilityService的介绍和配置 
已经了解的朋友可以直接阅读该文章


完成AccessibilityService的配置后,好像无从下手。先别急,先打印一些log看看吧。把下面的方法放在onAccessibilityEvent()里:

    private void printEventLog(AccessibilityEvent event) {
        Log.i(TAG, "-------------------------------------------------------------");
        int eventType = event.getEventType(); //事件类型
        Log.i(TAG, "PackageName:" + event.getPackageName() + ""); // 响应事件的包名
        Log.i(TAG, "Source Class:" + event.getClassName() + ""); // 事件源的类名
        Log.i(TAG, "Description:" + event.getContentDescription()+ ""); // 事件源描述
        Log.i(TAG, "Event Type(int):" + eventType + "");

        switch (eventType) {
            case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:// 通知栏事件
                Log.i(TAG, "event type:TYPE_NOTIFICATION_STATE_CHANGED");
                break;
            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED://窗体状态改变
                Log.i(TAG, "event type:TYPE_WINDOW_STATE_CHANGED");
                break;
            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED://View获取到焦点
                Log.i(TAG, "event type:TYPE_VIEW_ACCESSIBILITY_FOCUSED");
                break;
            case AccessibilityEvent.TYPE_GESTURE_DETECTION_START:
                Log.i(TAG, "event type:TYPE_VIEW_ACCESSIBILITY_FOCUSED");
                break;
            case AccessibilityEvent.TYPE_GESTURE_DETECTION_END:
                Log.i(TAG, "event type:TYPE_GESTURE_DETECTION_END");
                break;
            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
                Log.i(TAG, "event type:TYPE_WINDOW_CONTENT_CHANGED");
                break;
            case AccessibilityEvent.TYPE_VIEW_CLICKED:
                Log.i(TAG, "event type:TYPE_VIEW_CLICKED");
                break;
            case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
                Log.i(TAG, "event type:TYPE_VIEW_TEXT_CHANGED");
                break;
            case AccessibilityEvent.TYPE_VIEW_SCROLLED:
                Log.i(TAG, "event type:TYPE_VIEW_SCROLLED");
                break;
            case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED:
                Log.i(TAG, "event type:TYPE_VIEW_TEXT_SELECTION_CHANGED");
                break;
            default:
                Log.i(TAG, "no listen event");
        }

        for (CharSequence txt : event.getText()) {
            Log.i(TAG, "text:" + txt);
        }

        Log.i(TAG, "-------------------------------------------------------------");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

向安装了服务的手机发微信信息,查看打印的log:

非锁屏(在后台):
-------------------------------------------------------------
packageName:com.tencent.mm
source:null
source class:android.app.Notification
event type(int):64
event type:TYPE_NOTIFICATION_STATE_CHANGED
text:[联系人]: 哦
-------------------------------------------------------------
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
非锁屏(在前台主界面):
-------------------------------------------------------------
packageName:com.tencent.mm
source:android.view.accessibility.AccessibilityNodeInfo@8009b539; boundsInParent: Rect(0, 0 - 38, 38); boundsInScreen: Rect(103, 1181 - 141, 1219); packageName: com.tencent.mm; className: android.widget.TextView; text: 1; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: false; longClickable: false; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_NEXT_AT_MOVEMENT_GRANULARITY - null, AccessibilityAction: ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY - null, AccessibilityAction: ACTION_SET_SELECTION - null]
source class:android.widget.TextView
event type(int):2048
event type:TYPE_WINDOW_CONTENT_CHANGED
-------------------------------------------------------------
-------------------------------------------------------------
packageName:com.tencent.mm
source:null
source class:android.app.Notification
event type(int):64
event type:TYPE_NOTIFICATION_STATE_CHANGED
text:[联系人]: 呵呵
-------------------------------------------------------------
-------------------------------------------------------------
packageName:com.tencent.mm
source:android.view.accessibility.AccessibilityNodeInfo@80043582; boundsInParent: Rect(0, 0 - 38, 38); boundsInScreen: Rect(96, 153 - 134, 191); packageName: com.tencent.mm; className: android.widget.TextView; text: 1; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: false; longClickable: false; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_NEXT_AT_MOVEMENT_GRANULARITY - null, AccessibilityAction: ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY - null, AccessibilityAction: ACTION_SET_SELECTION - null]
source class:android.widget.TextView
event type(int):2048
event type:TYPE_WINDOW_CONTENT_CHANGED
-------------------------------------------------------------
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
非锁屏(在前台打开会话人的界面)(没Notification)
-------------------------------------------------------------
packageName:com.tencent.mm
source:android.view.accessibility.AccessibilityNodeInfo@8009cbbf; boundsInParent: Rect(0, 0 - 720, 1038); boundsInScreen: Rect(0, 146 - 720, 1184); packageName: com.tencent.mm; className: android.widget.ListView; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: true; focused: false; selected: false; clickable: true; longClickable: true; enabled: true; password: false; scrollable: true; actions: [AccessibilityAction: ACTION_FOCUS - null, AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_CLICK - null, AccessibilityAction: ACTION_LONG_CLICK - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_SCROLL_BACKWARD - null]
source class:android.widget.ListView
event type(int):2048
event type:TYPE_WINDOW_CONTENT_CHANGED
-------------------------------------------------------------
-------------------------------------------------------------
packageName:com.tencent.mm
source:android.view.accessibility.AccessibilityNodeInfo@801086ca; boundsInParent: Rect(0, 0 - 415, 80); boundsInScreen: Rect(201, 146 - 616, 150); packageName: com.tencent.mm; className: android.widget.TextView; text: 我在敲代码,稍后回复哈~; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: true; longClickable: true; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_CLICK - null, AccessibilityAction: ACTION_LONG_CLICK - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_NEXT_AT_MOVEMENT_GRANULARITY - null, AccessibilityAction: ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY - null, AccessibilityAction: ACTION_SET_SELECTION - null]
source class:android.widget.TextView
event type(int):2048
event type:TYPE_WINDOW_CONTENT_CHANGED
-------------------------------------------------------------
-------------------------------------------------------------
packageName:com.tencent.mm
source:android.view.accessibility.AccessibilityNodeInfo@8009cbbf; boundsInParent: Rect(0, 0 - 720, 1038); boundsInScreen: Rect(0, 146 - 720, 1184); packageName: com.tencent.mm; className: android.widget.ListView; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: true; focused: false; selected: false; clickable: true; longClickable: true; enabled: true; password: false; scrollable: true; actions: [AccessibilityAction: ACTION_FOCUS - null, AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_CLICK - null, AccessibilityAction: ACTION_LONG_CLICK - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_SCROLL_BACKWARD - null]
source class:android.widget.ListView
event type(int):4096
event type:TYPE_VIEW_SCROLLED
-------------------------------------------------------------
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
非锁屏(在前台打开非会话人的界面):
-------------------------------------------------------------
packageName:com.tencent.mm
source:android.view.accessibility.AccessibilityNodeInfo@800ce011; boundsInParent: Rect(0, 0 - 95, 80); boundsInScreen: Rect(104, 851 - 199, 931); packageName: com.tencent.mm; className: android.widget.TextView; text: [白眼]; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: true; longClickable: true; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_CLICK - null, AccessibilityAction: ACTION_LONG_CLICK - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_NEXT_AT_MOVEMENT_GRANULARITY - null, AccessibilityAction: ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY - null, AccessibilityAction: ACTION_SET_SELECTION - null]
source class:android.widget.TextView
event type(int):2048
event type:TYPE_WINDOW_CONTENT_CHANGED
-------------------------------------------------------------
-------------------------------------------------------------
packageName:com.tencent.mm
source:null
source class:android.app.Notification
event type(int):64
event type:TYPE_NOTIFICATION_STATE_CHANGED
text:[联系人]: 呵呵
-------------------------------------------------------------
-------------------------------------------------------------
packageName:com.tencent.mm
source:android.view.accessibility.AccessibilityNodeInfo@8012f5f0; boundsInParent: Rect(0, 0 - 371, 60); boundsInScreen: Rect(174, 591 - 545, 651); packageName: com.tencent.mm; className: android.widget.TextView; text: "?彣????" 撤回了一条消息; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: true; longClickable: false; enabled: true; password: false; scrollable: false; actions: [AccessibilityAction: ACTION_SELECT - null, AccessibilityAction: ACTION_CLEAR_SELECTION - null, AccessibilityAction: ACTION_CLICK - null, AccessibilityAction: ACTION_ACCESSIBILITY_FOCUS - null, AccessibilityAction: ACTION_NEXT_AT_MOVEMENT_GRANULARITY - null, AccessibilityAction: ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY - null, AccessibilityAction: ACTION_SET_SELECTION - null]
source class:android.widget.TextView
event type(int):2048
event type:TYPE_WINDOW_CONTENT_CHANGED
-------------------------------------------------------------
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

从打印的log和我们平时使用微信应该就知道的了,除了在打开了会话人的聊天界面,否则都会有Notification的,可以以此作为切入点,那么接下来我们的工作就简单了。步骤如下: 
1. 监听TYPE_NOTIFICATION_STATE_CHANGED事件 
2. 根据Notification打开会话人聊天界面 
3. 搜索输入框控件 
4. 在输入框输入回复文本 
5. 点击发送按钮 
6. 返回微信主界面

思路很清晰了,难点是如何找到相应的控件。放心,Android也为我们提供了一个类来帮助我们——AccessibilityNodeInfo,其包含一些控件的信息,可用其找到相应的控件,并做出相应的操作。常用方法:

- CharSequence getClassName () // 获取控件类名,如按钮会返回android.widget.Button
- CharSequence getText () // 获取控件的文本,如微信的发送按钮会返回“发送”
- String getViewIdResourceName () // 获取控件的id
  • 1
  • 2
  • 3

代码注释很详细了,就不一一解释。源码后面有贴。

/**
 * 自动回复服务
 */
public class AutoReplyService extends AccessibilityService{

    private static final String TAG = AutoReplyService.class.getSimpleName();

    private Handler handler = new Handler();
    private boolean hasNotify = false;

    /**
     * 必须重写的方法,响应各种事件。
     */
    @Override
    public void onAccessibilityEvent(final AccessibilityEvent event) { 
        int eventType = event.getEventType(); // 事件类型
        switch (eventType) {
            case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: // 通知栏事件
                Log.i(TAG, "TYPE_NOTIFICATION_STATE_CHANGED");
                if(PhoneController.isLockScreen(this)) { // 锁屏
                    PhoneController.wakeAndUnlockScreen(this);   // 唤醒点亮屏幕
                }
                openAppByNotification(event);
                hasNotify = true;
                break;

            default:
                Log.i(TAG, "DEFAULT");
                if (hasNotify) { // 如果有通知
                    try {
                        Thread.sleep(1000); // 停1秒, 否则在微信主界面没进入聊天界面就执行了fillInputBar
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (fillInputBar("我在敲代码,稍后回复哈~")) { // 找到输入框,即EditText
                        findAndPerformAction(UI.BUTTON, "发送"); // 点击发送
                        handler.postDelayed(new Runnable() { // 返回主界面,这里延迟执行,为了有更好的交互
                            @Override
                            public void run() {
                                performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);   // 返回
                            }
                        }, 1500);

                    }
                    hasNotify = false;
                }
                break;
        }
    }

    @Override
    public void onInterrupt() {
    }

    /**
     * 查找UI控件并点击
     * @param widget 控件完整名称, 如android.widget.Button, android.widget.TextView
     * @param text 控件文本
     */
    private void findAndPerformAction(String widget, String text) {
        // 取得当前激活窗体的根节点
        if (getRootInActiveWindow() == null) {
            return;
        }

        // 通过文本找到当前的节点
        List<AccessibilityNodeInfo> nodes = getRootInActiveWindow().findAccessibilityNodeInfosByText(text);
        if(nodes != null) {
            for (AccessibilityNodeInfo node : nodes) {
                if (node.getClassName().equals(widget) && node.isEnabled()) {
                    node.performAction(AccessibilityNodeInfo.ACTION_CLICK); // 执行点击
                    break;
                }
            }
        }
    }

    /**
     * 打开微信
     * @param event 事件
     */
    private void openAppByNotification(AccessibilityEvent event) {
        if (event.getParcelableData() != null  && event.getParcelableData() instanceof Notification) {
            Notification notification = (Notification) event.getParcelableData();
            try {
                PendingIntent pendingIntent = notification.contentIntent;
                pendingIntent.send();
            } catch (PendingIntent.CanceledException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 填充输入框
     */
    private boolean fillInputBar(String reply) {
        AccessibilityNodeInfo rootNode = getRootInActiveWindow();
        if (rootNode != null) {
            return findInputBar(rootNode, reply);
        }
        return false;
    }

    /**
     * 查找EditText控件
     * @param rootNode 根结点
     * @param reply 回复内容
     * @return 找到返回true, 否则返回false
     */
    private boolean findInputBar(AccessibilityNodeInfo rootNode, String reply) {
        int count = rootNode.getChildCount();
        for (int i = 0; i < count; i++) {
            AccessibilityNodeInfo node = rootNode.getChild(i);

            if (UI.EDITTEXT.equals(node.getClassName())) {   // 找到输入框并输入文本
                setText(node, reply);
                return true;
            }

            if (findInputBar(node, reply)) {    // 递归查找
                return true;
            }
        }
        return false;
    }

    /**
     * 设置文本
     */
    private void setText(AccessibilityNodeInfo node, String reply) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Bundle args = new Bundle();
            args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
                    reply);
            node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
        } else {
            ClipData data = ClipData.newPlainText("reply", reply);
            ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
            clipboardManager.setPrimaryClip(data);
            node.performAction(AccessibilityNodeInfo.ACTION_FOCUS); // 获取焦点
            node.performAction(AccessibilityNodeInfo.ACTION_PASTE); // 执行粘贴
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145

这里我没有处理打开了会话人聊天界面的情况,我觉得你都打开了会话人的聊天界面,就证明你想与他聊天,也就不需要自动回复了。当然,如果你遍要(不带你这样敷衍朋友的),只要加点简单逻辑就可以实现了~到这里微信自动回复功能就完成了,怎样,是不是很简单!感兴趣的朋友可以继续看我下一篇文章: 
微信自动回复和自动抢红包实现原理(三):自动抢红包

猜你喜欢

转载自blog.csdn.net/qq_21051503/article/details/80582949
今日推荐