功能:兼容多种情况下抢到微信红包:锁屏状态下、不在微信界面下、在微信主界面下、群屏蔽下、聊天界面出现连续的红包雨等情况;抢红包速度:2-3秒/个(小米、魅族手机只能跳到红包界面,无法实现自动领取,其他手机型号均可)
设计思路:调用Android系统自带的AccessibilityService服务监控手机消息状态(为了防止受其他非微信类消息的干扰,设置只监控“com.tencent.mm”的包名;为了保证可以兼容多种情况下可以抢到到红包,如锁屏状态下、不在微信界面下、在微信主界面下、群屏蔽下、聊天界面出现连续的红包雨等情况,需设置监控状态有TYPE_NOTIFICATION_STATE_CHANGED (通知栏变化)、TYPE_WINDOW_CONTENT_CHANGED(界面内容变化)、TYPE_WINDOW_STATE_CHANGED(窗口体变化)然后根据状态变化出现“[微信红包]”的文字,则调用AccessibilityService的ACTION_CLICK模拟点击进入红包界面并领取,完成自动抢红包的操作;最后执行GLOBAL_ACTION_BACK返回聊天界面。
程序流程图:
代码解析:
(1)在MainActivity跳转到手机无障碍模式界面,提示用户打开AccessibilityService服务
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
//打开系统设置中辅助功能
Intent intent = new Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivity(intent);
Toast.makeText(this, "group04_抢红包APP->开启", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
(2)重写AccessibilityService的继承类,并在里面重写onAccessibilityEvent函数,实时监控微信的动态。红包消息变化状态依次分为三类:
- 以消息栏形式弹出(该情况一般出现于手机锁屏、正在使用其他APP、处于与非发红包者聊天界面)当红包消息以Notification的形式出现,先判断是否处于锁屏状态,调用wakeAndUnlock()函数(自己写的函数)解锁;然后用PendingIntent跳转到微信聊天界面,这时手机的state变化了,跳转到窗口体变化状态
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:// 通知栏事件
Log.d("eventType","notification event");
List<CharSequence> texts = event.getText();
if (!texts.isEmpty()) {
for (CharSequence text : texts) {
String content = text.toString();
Log.v("content",content);
if(content.contains("[微信红包]")){
//如果锁定,先解锁
if(isScreenLocked()){
wakeAndUnlock();
Log.d("i","解锁");
}
hasNotification = true;
//打开微信界面
Notification notification =(Notification)event.getParcelableData();
if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
PendingIntent pendingIntent = notification.contentIntent;
try {
pendingIntent.send();
Log.d("i","打开微信界面");
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}
}
}
}
break;
- 窗口体变化(只有打开微信界面时,才会出现窗口界面的变化
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED://窗口体状态,事件源:跳转到新的页面,或者弹出了window,dialog等 Log.d("eventType","Window event"); String className = event.getClassName().toString(); Log.d("窗口体变化",className); if(hasNotification){//先判断hasNotification为true,说明是进入有红包的聊天界面,以免所有的通知信息都进入聊天界面 if(className.equals("com.tencent.mm.ui.LauncherUI")){ Log.d("e","进入微信界面"); openPacket(); } else if(className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI") ){ Log.d("i","进入点击红包界面"); receivePacket(); } else if(className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")){ Log.d("e","进入红包记录界面"); release(); //可以在这里用handler进行抢红包延时 performGlobalAction(GLOBAL_ACTION_BACK); } } break;
- 窗口内容变化(这种情况用来检测聊天界面变化如连续发红包、微信主界面出现微信红包的消息(此时不会以通知栏的形式出现,而是发生窗口view变化))
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED://某个view的内容发生的变化,对于红包而言:出现在打开微信却不是在com.tencent.mm.ui.LauncherUI界面或者出现红包雨的情况下或者只要微信有消息就行
Log.d("eventType","View event");
String className_view = event.getClassName().toString();
Log.d("view变化",className_view);
hasPacketInWXforground();//过滤掉微信主界面不是微信红包的信息,只进入有微信红包的聊天界面
openPacket();
hasNotification = true;
break;
(3)打开红包界面:获取该界面的包含“领取红包”Text的所有节点,然后getParent()得到父节点,点击父节点即可进入领取红包界面。
private void openPacket(){
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
if(nodeInfo == null) return ;
//获取该界面的包含“领取红包”Text的所有节点
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("领取红包");
if(list.size()==0) return;
Log.d("i",list.size()+"个红包可以领取");
//最新的红包领起
for (int i = list.size() - 1; i >= 0; i--) {
AccessibilityNodeInfo parent = list.get(i).getParent();//获取该节点的父节点
if (parent != null) {
parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);//模拟点击
break;
}
}
}
(4)领取红包:利用Android studio里面Uiautomator获取微信“开”图片的id为com.tencent.mm:id/c31,所以只要找出所有含该id的节点,模拟点击即可领取。
PS:这里的id在最新的微信版本/2018.6.2可能会不同,如果不是以上的id,可以通过AS的UIutomator获取新的id
最新微信6.6.7版本id为:com.tencent.mm:id/c85
private void receivePacket() {
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
if (nodeInfo == null) {
Log.d("node","数量为null");
return;
}
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/c85");//打开红包按钮的id
Log.d("i", list.size() + "个红包可以领取");
for (AccessibilityNodeInfo n : list) {
if (n.isClickable()) {
n.performAction(AccessibilityNodeInfo.ACTION_CLICK);
Log.d("i", "领取成功");
break;
}
}
//说明已经没有红包领了,让通知栏判断为false
if (list.size()==0) {
hasNotification = false;
Log.d("d", "没有通知了");
}
}
(5)当处于微信主界面时,判断有新的红包消息,并进入领取
//当我们在微信主界面时,有新消息接收,不会以通知栏的形式出现,需要自行判断是否有红包(判断id:com.tencent.mm:id/apx)
private void hasPacketInWXforground(){
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
if(rootNode==null) return ;
List<AccessibilityNodeInfo> list = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/apx");//该id判断栏目的第二小行的第二列,如新消息里的XX:[微信红包]大吉大利,其中[微信红包]..就是
for(int i=0;i<list.size();i++){
String content = list.get(i).getText().toString();
if(content.contains("[微信红包]")){
AccessibilityNodeInfo parent = list.get(i).getParent();
List<AccessibilityNodeInfo> list_new = parent.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/jj");//新消息的数量,含数字的小红球
List<AccessibilityNodeInfo> list_mute = parent.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/apu");//已经屏蔽的群信息
//过滤掉已阅览的群节点
if((list_new.size()>0 || list_mute.size()>0) && parent.isClickable()){
Log.d("i","进入聊天界面");
parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
break;
}
}
}
}
(6)红包记录界面:该界面在窗口体state判断,需要返回上一级,调用返回键GLOBAL_ACTION_BACK退出红包记录界面,继续执行抢红包操作
else if(className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")){
Log.d("e","进入红包记录界面");
release();
//可以在这里用handler进行抢红包延时
performGlobalAction(GLOBAL_ACTION_BACK);
}
测试结果:
微信昵称为:益智 的手机号安装编写好的抢红包APP,其他三个人同时发红包到实验群,并以最快的速度开抢,所得结果如下:
由图可知:安装APP的微信号保持每两秒抢到一个红包的速度,时间分别为1:12:50->1:12:52->1:12:54;而没有APP的微信号每抢到一个红包至少需要3秒,如微信昵称为:做实验的小号,抢到红包时间分别为:1:12:50->1:12:54->1:12:57,每次间隔为3秒
代码下载: