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

android微信的抢红包插件

2015-12-21 01:07 573 查看

前言

  之前看到了微信抢红包的插件,觉得这个功能实在强大了,这和之前我想实现的模拟点击事件基本相似,可以完美的触发一个view的点击事件,当然静默安装的原理也和抢红包的原理是一样的。

  小米有开源小米的抢红包的demo,之后有童鞋在此之上增加了自己的逻辑,使其完善。现在为了学习,我也加入抢红包的demo大军哈哈,主要在于学习。

原理

  原理主要是通过辅助功能AccessibilityService来完成的,AccessibilityService是Google用来帮助肢体不便的人所开发的一个功能,能够触发相应的用户事件比如点击,滑动等等。

  抢红包的功能也是基于此能力之上,去进行模拟点击事件。当然,AccessibilityService的功能需要得到用户的许可,通过它的功能的介绍,已经可以知道它的强大,其实这个的能力和root相当,甚至更强,毕竟可以模拟一切的用户事件。

  所以,在实现的方法前,需要了解

  1. 如何获取特定能够点击的view的节点

  2. 触发点击事件,进行判断

逻辑

需要注意:保持屏幕常亮,需要把每个群的消息免打扰取消,主要为了能够接收到通知Notification,在一开始请回到Home

接收到Notification之后,存入List中作为待处理红包.

逐个处理List中的PendingIntent,开启微信的聊天界面,获取可以点击的红包列表,逐个点击。

进入红包处理,两种情况,

一、进入接收红包的界面,获取抢红包的按钮,触发点击。进入红包detail界面,结束。

二、已经抢过,进入红包detail界面,或者被抢完了,停留在receive界面,结束,回到主界面。

回到步骤1

工程源码

首先新建一个PluginService继承AccessibilityService,然后再Manifest.xml中进行注册:

<service
android:name=".PluginService"
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>


  其中的android:resource=…的是对于Service的参数配置,文件的位置在于res下的xml文件下,名字定为accessible_service_config.xml

各个参数的定义:

  android:accessibilityEventTypes:允许接收的事件,这里定义为notification变化事件,窗口切换变化事件,窗口内容变化事件。

  android:accessibilityFeedbackType:设置返回给用户的形式

  android:packageNames:指定响应某个指定应用的事件。需要知道某个应用的包名的时候可以通过DDMS中的hierarchy view进行查看。

  android:notificationTimeout:设置响应的时间

<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/app_name"
android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowContentChanged|typeWindowStateChanged"
android:accessibilityFeedbackType="feedbackAllMask"
android:packageNames="com.tencent.mm"
android:notificationTimeout="0"
android:accessibilityFlags=""
android:canRetrieveWindowContent="true"/>


2.在PluginService中进行逻辑的处理

在onAccessibilityEvent中进行事件的监听,监听包括三种类型的,一种是通知栏的变化,一种是窗口内容的变化,一种是窗口切换的变化。

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
int eventType = event.getEventType();
switch (eventType) {
// 监听通知栏消息
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
handlerNotification(event);
break;
// 监听窗口变化消息
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
handlerWindowStateChange(event);
break;
// 监听窗口内容的动态变化
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
handlerWindowContentChange(event);
break;
default:
break;
}
}


3.我们回到了主界面,有群发了红包,那么进入notification变化的事件中去,在其中判断是否是含有红包的消息,如果是的话则模拟打开notification,这里多做了一个步奏就是将多个notification存储到一个list里面,进行足一的打开。

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public void handlerNotification(AccessibilityEvent event) {
if (event == null) return;
List<CharSequence> contents = event.getText();
if (contents != null && !contents.isEmpty()) {
for (CharSequence content : contents) {
String text = content.toString();
if (!text.contains("[微信红包]")) continue;
if (event.getParcelableData() != null &&
event.getParcelableData() instanceof Notification) {
Notification notification = (Notification) event.getParcelableData();
PendingIntent pendingIntent = notification.contentIntent;
Log.e("pendingintent", pendingIntent.getCreatorPackage().toString());
if (pendingIntent.getCreatorPackage().toString().equals("com.tencent.mm")) {
if ((curStatus != Status.ON_CHAT_ROOM || curStatus != Status.ON_LUCKY_MONEY_RECEIVED) &&
untreatedLuckyMoneyList.size() == 0) {
openNotification(pendingIntent);
} else {
untreatedLuckyMoneyList.add(0, pendingIntent);
}
}
}
}
}
}

/**
* 开启Notification中所指向的微信聊天页面
*
* @param pendingIntent
*/
public void openNotification(PendingIntent pendingIntent) {
if (pendingIntent == null) return;
// 模拟点击了notification
try {
pendingIntent.send();
curStatus = Status.ON_CHAT_ROOM;
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}


4.一下方法通过判定窗口变化的具体的类来判定进入了哪一个界面。从而进行对应的操作。

  a 进入了聊天界面,则进行领取红包的字样遍历,同时进行模拟点击开启。

  b 进入领取红包界面,则遍历得到拆红包字样的节点,同时模拟点击

  c 进入红包详情界面,则模拟返回键退出。

要知道具体的类是什么的时候,可以通过测试输出消息,通过event.getClassName().toString()的输出查看具体的类名。

public void handlerWindowStateChange(AccessibilityEvent event) {
if (event == null || event.getSource() == null) return;
String className = event.getClassName().toString();

// 微信主界面 com.tencent.mm.ui.LauncherUI
if (className.equals("com.tencent.mm.ui.LauncherUI")) {
curStatus = Status.ON_CHAT_ROOM;
if (canOpenNode == null) {
canOpenNode = event.getSource().findAccessibilityNodeInfosByText("领取红包");
trashOpenNode = new ArrayList<AccessibilityNodeInfo>();
}
openLuckyMoney();
// 红包接收界面 com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI
} else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI")) {
curStatus = Status.ON_LUCKY_MONEY_RECEIVED;
unpackLuckyMoney(event.getSource());
// 红包的detail界面 com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI
} else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")) {
curStatus = Status.ON_LUCKY_MONEY_DETAIL;
back();
}
}


5.进入到微信的聊天界面,这时候会触发窗口变化消息,这时候通过view找到具有领取红包字样的节点,如果有,则进行模拟点击,如果没有回到主界面。这里其中一个步奏也是将多个具有领取红包的节点存储到list中,等待开启。

/**
* 在聊天界面中打开红包
*/
public void openLuckyMoney() {
if (canOpenNode != null && canOpenNode.size() == 0) {
backToHome();
disposeLuckyMoneyList();
canOpenNode = null;
trashOpenNode = null;
}

if (canOpenNode == null) {
backToHome();
return;
}

AccessibilityNodeInfo node = canOpenNode.remove(0);
trashOpenNode.add(node);
if (node.getParent() != null && node.getParent().isClickable()) {
node.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
}

}


6.假如进入到了红包领取界面,则需要先判断是否被领取完了,或者是过期了,如果都不是,则找到拆红包字样的节点进行模拟点击

public void unpackLuckyMoney(AccessibilityNodeInfo nodeInfo) {
if (nodeInfo == null) {
back();
return;
}
// 打开红包,如果红包已被抢完,遍历节点, 如果匹配“红包详情”、“手慢了”和“过期”,则返回
// 继续打开其他红包
List<AccessibilityNodeInfo> packedLists = new ArrayList<>();
packedLists.addAll(nodeInfo.findAccessibilityNodeInfosByText("红包详情"));
packedLists.addAll(nodeInfo.findAccessibilityNodeInfosByText("手慢了"));
packedLists.addAll(nodeInfo.findAccessibilityNodeInfosByText("过期"));

if (!packedLists.isEmpty()) {
back();
return;
}

List<AccessibilityNodeInfo> unPackedLists = nodeInfo.findAccessibilityNodeInfosByText("拆红包");
if (unPackedLists.isEmpty()) {
back();
} else {
AccessibilityNodeInfo node = unPackedLists.get(0);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}


7.当进入到了红包详情页面时候,则进行返回,返回到聊天界面之后,会继续进行红包列表进行拆红包,知道拆完了所有的红包之后,就会退回到主界面,然后继续拆开notification list中的消息,如果没有消息,则等待。

改进

通过遍历节点去找到能点击的事件是比较浪费效率的,其实通过DDMS中的hierarchy view可以看到每个控件的ID,通过ID可以直接获取该控件,然后模拟点击,就不用进行遍历了。

项目源码,github网址

参考文档网址

参考工程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: