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

2017-01-14 10:46:53来源:http://www.jianshu.com/p/5b4a4a5aca7e作者:LeoExer人点击

阅读这篇文章前,你需要先了解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, "-------------------------------------------------------------");
}

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


非锁屏(在后台):
-------------------------------------------------------------
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@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
-------------------------------------------------------------

非锁屏(在前台打开会话人的界面)(没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
-------------------------------------------------------------

非锁屏(在前台打开非会话人的界面):
-------------------------------------------------------------
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
-------------------------------------------------------------

从打印的log和我们平时使用微信应该就知道的了,除了在打开了会话人的聊天界面,否则都会有Notification的,可以以此作为切入点,那么接下来我们的工作就简单了。步骤如下:


监听TYPE_NOTIFICATION_STATE_CHANGED事件
根据Notification打开会话人聊天界面
搜索输入框控件
在输入框输入回复文本
点击发送按钮
返回微信主界面

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


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

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


/**
* 自动回复服务
*/
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); // 执行粘贴
}
}
}

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


源码下载




最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台