- 抢红包插件
先回顾一下抢红包的的流程:
- 状态栏出现"[微信红包]"的消息提示,点击进入聊天界面
- 点击相应的红包信息,弹出抢红包界面
- 在抢红包界面点击"开",打开红包
- 在红包详情页面,查看详情,点击返回按钮返回微信聊天界面.
以上是不在微信聊天界面时的流程.如果你所在的微信聊天窗口出现红包,则不会执行步骤1,而是直接执行2,3,4.如果是在微信好友列表时,收到红包,则会在列表项中显示[微信红包],需要点即该列表项,进入聊天界面,随后执行2,3,4.为了方便演示,这里我们暂时不考虑好友列表时出现红包的情况.
明白了抢红包流程,之后我们通过AccessibilityService获取通知栏信息及微信聊天窗口界面,继而通过模拟点击实现打开红包,抢红包等操作.
AccessibilityService配置如下:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|
typeWindowContentChanged"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="true"
android:notificationTimeout="100"
android:packageNames="com.tencent.mm" />
具体实现代码如下:
public class RobService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
int eventType = event.getEventType();
switch (eventType) {
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
handleNotification(event);
break;
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
String className = event.getClassName().toString();
if (className.equals("com.tencent.mm.ui.LauncherUI")) {
getPacket();
} else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI")) {
openPacket();
} else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")) {
close();
}
break;
}
}
/**
* 处理通知栏信息
*
* 如果是微信红包的提示信息,则模拟点击
*
* @param event
*/
private void handleNotification(AccessibilityEvent event) {
List<CharSequence> texts = event.getText();
if (!texts.isEmpty()) {
for (CharSequence text : texts) {
String content = text.toString();
//如果微信红包的提示信息,则模拟点击进入相应的聊天窗口
if (content.contains("[微信红包]")) {
if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
Notification notification = (Notification) event.getParcelableData();
PendingIntent pendingIntent = notification.contentIntent;
try {
pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* 关闭红包详情界面,实现自动返回聊天窗口
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private void close() {
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
if (nodeInfo != null) {
//为了演示,直接查看了关闭按钮的id
List<AccessibilityNodeInfo> infos = nodeInfo.findAccessibilityNodeInfosByViewId("@id/ez");
nodeInfo.recycle();
for (AccessibilityNodeInfo item : infos) {
item.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
}
/**
* 模拟点击,拆开红包
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private void openPacket() {
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
if (nodeInfo != null) {
//为了演示,直接查看了红包控件的id
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId("@id/b9m");
nodeInfo.recycle();
for (AccessibilityNodeInfo item : list) {
item.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
}
/**
* 模拟点击,打开抢红包界面
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void getPacket() {
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
AccessibilityNodeInfo node = recycle(rootNode);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
AccessibilityNodeInfo parent = node.getParent();
while (parent != null) {
if (parent.isClickable()) {
parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
break;
}
parent = parent.getParent();
}
}
/**
* 递归查找当前聊天窗口中的红包信息
*
* 聊天窗口中的红包都存在"领取红包"一词,因此可根据该词查找红包
*
* @param node
*/
public AccessibilityNodeInfo recycle(AccessibilityNodeInfo node) {
if (node.getChildCount() == 0) {
if (node.getText() != null) {
if ("领取红包".equals(node.getText().toString())) {
return node;
}
}
} else {
for (int i = 0; i < node.getChildCount(); i++) {
if (node.getChild(i) != null) {
recycle(node.getChild(i));
}
}
}
return node;
}
@Override
public void onInterrupt() {
}
@Override
protected void onServiceConnected() {
super.onServiceConnected();
}
}
上面的代码简单演示了抢红包的原理,为了方便起见,我直接通过findAccessibilityNodeInfosByViewId()
获取制定id控件.在实际中,这种方法不太可靠,到目前为止,微信已经改过几次相关控件的id了.
有童鞋问,怎么样知道该控件的id呢.其实很简单,android中已经为我们提供了相关的工具:在Android Studio中开启Android Device Monitor,选择设备后点击Dump View Hierarchy for UI Automator,如下:
这里写图片描述
稍等片刻之后,便会出现当前设备的窗口,在该窗口中点击相关控件,便会显示该控件的属性.借助该工具,可以帮我们快速的分析界面结构,帮助我们从其他app布局策略中学习
这里写图片描述
我们用Dump View Hierarchy for UI Automator分析聊天界面微信红包信息:
这里写图片描述
抢红包界面:
这里写图片描述