Android】中微信抢红包助手的实现(代码整理)

实现原理

  通过利用AccessibilityService辅助服务,监测屏幕内容,如监听状态栏的信息,屏幕跳转等,以此来实现自动拆红包的功能。关于AccessibilityService辅助服务,可以自行百度了解更多。

 

代码基础:

1.首先声明一个RedPacketService继承自AccessibilityService,该服务类有两个方法必须重写,如下:

 

 
  1. /**

  2. * Created by Yemon on 2017/2/3.

  3. * email:879509347@qq.com

  4. *

  5. * 抢红包服务类

  6. */

  7.  
  8. public class RedPacketService extends AccessibilityService {

  9.  
  10.  
  11. /**

  12. * 必须重写的方法:此方法用了接受系统发来的event。在你注册的event发生是被调用。在整个生命周期会被调用多次。

  13. */

  14. @Override

  15. public void onAccessibilityEvent(AccessibilityEvent event) {

  16.  
  17. }

  18.  
  19. /**

  20. * 必须重写的方法:系统要中断此service返回的响应时会调用。在整个生命周期会被调用多次。

  21. */

  22. @Override

  23. public void onInterrupt() {

  24. Toast.makeText(this, "我快被终结了啊-----", Toast.LENGTH_SHORT).show();

  25. }

  26.  
  27. /**

  28. * 服务已连接

  29. */

  30. @Override

  31. protected void onServiceConnected() {

  32. Toast.makeText(this, "抢红包服务开启", Toast.LENGTH_SHORT).show();

  33. super.onServiceConnected();

  34. }

  35.  
  36. /**

  37. * 服务已断开

  38. */

  39. @Override

  40. public boolean onUnbind(Intent intent) {

  41. Toast.makeText(this, "抢红包服务已被关闭", Toast.LENGTH_SHORT).show();

  42. return super.onUnbind(intent);

  43. }

  44. }

 

2.对我们的RedPacketService进行一些配置,这里配置方法可以选择代码动态配置(onServiceConnected里配置),也可以直接在res/xml下新建.xml文件,没有xml文件夹就新建。这里我们将文件命名为redpacket_service_config.xml,代码如下:

 

 
  1. <?xml version="1.0" encoding="utf-8"?>

  2. <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"

  3. android:accessibilityEventTypes="typeAllMask"

  4. android:accessibilityFeedbackType="feedbackGeneric"

  5. android:accessibilityFlags="flagDefault"

  6. android:canRetrieveWindowContent="true"

  7. android:description="@string/desc"

  8. android:notificationTimeout="100"

  9. android:packageNames="com.tencent.mm" />

 

accessibilityEventTypes:   

响应哪一种类型的事件,typeAllMask就是响应所有类型的事件了,另外还有单击、长按、滑动等。

accessibilityFeedbackType:  

用什么方式反馈给用户,有语音播出和振动。可以配置一些TTS引擎,让它实现发音。

packageNames:

指定响应哪个应用的事件。这里我们是写抢红包助手,就写微信的包名:com.tencent.mm,这样就可以监听微信产生的事件了。

notificationTimeout:

响应时间

description:

辅助服务的描述信息。

 

3.service是四大组件之一,需要在AndroidManifest进行配置,注意这里稍微有些不同:

 

 
  1. <!--抢红包服务-->

  2. <service

  3. android:name=".RedPacketService"

  4. android:enabled="true"

  5. android:exported="true"

  6. android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">

  7. <intent-filter>

  8. <action android:name="android.accessibilityservice.AccessibilityService" />

  9. </intent-filter>

  10. <meta-data

  11. android:name="android.accessibilityservice"

  12. android:resource="@xml/redpacket_service_config"></meta-data>

  13. </service>

 

android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"  权限申请
android:resource="@xml/redpacket_service_config"  引用刚才的配置文件


核心代码:
我们的红包助手,核心思路分为三步走:
监听通知栏微信消息,如果弹出[微信红包]字样,模拟手指点击状态栏跳转到微信聊天界面→在微信聊天界面查找红包,如果找到则模拟手指点击打开,弹出打开红包界面→模拟手指点击红包“開”

1.监听通知栏消息,查看是否有[微信红包]字样,代码如下:

 

 
  1. @Override

  2. public void onAccessibilityEvent(AccessibilityEvent event) {

  3. int eventType = event.getEventType();

  4. switch (eventType) {

  5. //通知栏来信息,判断是否含有微信红包字样,是的话跳转

  6. case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:

  7. List<CharSequence> texts = event.getText();

  8. for (CharSequence text : texts) {

  9. String content = text.toString();

  10. if (!TextUtils.isEmpty(content)) {

  11. //判断是否含有[微信红包]字样

  12. if (content.contains("[微信红包]")) {

  13. //如果有则打开微信红包页面

  14. openWeChatPage(event);

  15. }

  16. }

  17. }

  18. break;

  19.     }

  20. }

  21.  
  22. /**

  23. * 开启红包所在的聊天页面

  24. */

  25. private void openWeChatPage(AccessibilityEvent event) {

  26. //A instanceof B 用来判断内存中实际对象A是不是B类型,常用于强制转换前的判断

  27. if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {

  28. Notification notification = (Notification) event.getParcelableData();

  29. //打开对应的聊天界面

  30. PendingIntent pendingIntent = notification.contentIntent;

  31. try {

  32. pendingIntent.send();

  33. } catch (PendingIntent.CanceledException e) {

  34. e.printStackTrace();

  35. }

  36. }

  37. }

 

2.判断当前是否在微信聊天页面,是的话遍历当前页面各个控件,找到含有微信红包或者领取红包的textview控件,然后逐层找到他的可点击父布局(图中绿色部分),模拟点击跳转到含有“開”的红包界面,代码如下:

 

 
  1. @Override

  2. public void onAccessibilityEvent(AccessibilityEvent event) {

  3. int eventType = event.getEventType();

  4. switch (eventType) {

  5. //窗口发生改变时会调用该事件

  6. case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:

  7. String className = event.getClassName().toString();

  8. //判断是否是微信聊天界面

  9. if ("com.tencent.mm.ui.LauncherUI".equals(className)) {

  10. //获取当前聊天页面的根布局

  11. AccessibilityNodeInfo rootNode = getRootInActiveWindow();

  12. //开始找红包

  13. findRedPacket(rootNode);

  14. }

  15. }

  16. }

  17. /**

  18. * 遍历查找红包

  19. */

  20. private void findRedPacket(AccessibilityNodeInfo rootNode) {

  21. if (rootNode != null) {

  22. //从最后一行开始找起

  23. for (int i = rootNode.getChildCount() - 1; i >= 0; i--) {

  24. AccessibilityNodeInfo node = rootNode.getChild(i);

  25. //如果node为空则跳过该节点

  26. if (node == null) {

  27. continue;

  28. }

  29. CharSequence text = node.getText();

  30. if (text != null && text.toString().equals("领取红包")) {

  31. AccessibilityNodeInfo parent = node.getParent();

  32. //while循环,遍历"领取红包"的各个父布局,直至找到可点击的为止

  33. while (parent != null) {

  34. if (parent.isClickable()) {

  35. //模拟点击

  36. parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);

  37. //isOpenRP用于判断该红包是否点击过

  38. isOpenRP = true;

  39. break;

  40. }

  41. parent = parent.getParent();

  42. }

  43. }

  44. //判断是否已经打开过那个最新的红包了,是的话就跳出for循环,不是的话继续遍历

  45. if (isOpenRP) {

  46. break;

  47. } else {

  48. findRedPacket(node);

  49. }

  50.  
  51. }

  52. }

  53. }

 

3.点击红包后,在模拟手指点击“開”以此开启红包,跳转到红包详情界面,方法与步骤二类似:

 

 
  1. @Override

  2. public void onAccessibilityEvent(AccessibilityEvent event) {

  3. int eventType = event.getEventType();

  4. switch (eventType) {

  5. //窗口发生改变时会调用该事件

  6. case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:

  7. String className = event.getClassName().toString();

  8.  
  9. //判断是否是显示‘开’的那个红包界面

  10. if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI".equals(className)) {

  11. AccessibilityNodeInfo rootNode = getRootInActiveWindow();

  12. //开始抢红包

  13. openRedPacket(rootNode);

  14. }

  15. break;

  16. }

  17. }

  18.  
  19. /**

  20. * 开始打开红包

  21. */

  22. private void openRedPacket(AccessibilityNodeInfo rootNode) {

  23. for (int i = 0; i < rootNode.getChildCount(); i++) {

  24. AccessibilityNodeInfo node = rootNode.getChild(i);

  25. if ("android.widget.Button".equals(node.getClassName())) {

  26. node.performAction(AccessibilityNodeInfo.ACTION_CLICK);

  27. }

  28. openRedPacket(node);

  29. }

  30. }

 

结合以上三步,下面是完整代码,注释已经写的很清楚,直接看代码:

 

 

 
  1. package com.cxk.redpacket;

  2. import android.accessibilityservice.AccessibilityService;

  3. import android.app.KeyguardManager;

  4. import android.app.Notification;

  5. import android.app.PendingIntent;

  6. import android.app.Service;

  7. import android.content.Context;

  8. import android.content.Intent;

  9. import android.os.IBinder;

  10. import android.os.PowerManager;

  11. import android.text.TextUtils;

  12. import android.util.Log;

  13. import android.view.accessibility.AccessibilityEvent;

  14. import android.view.accessibility.AccessibilityNodeInfo;

  15. import android.widget.Toast;

  16.  
  17. import java.util.List;

  18.  
  19. /**

  20. * 抢红包Service,继承AccessibilityService

  21. */

  22. public class RedPacketService extends AccessibilityService {

  23. /**

  24. * 微信几个页面的包名+地址。用于判断在哪个页面 LAUCHER-微信聊天界面,LUCKEY_MONEY_RECEIVER-点击红包弹出的界面

  25. */

  26. private String LAUCHER = "com.tencent.mm.ui.LauncherUI";

  27. private String LUCKEY_MONEY_DETAIL = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI";

  28. private String LUCKEY_MONEY_RECEIVER = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI";

  29.  
  30. /**

  31. * 用于判断是否点击过红包了

  32. */

  33. private boolean isOpenRP;

  34.  
  35. @Override

  36. public void onAccessibilityEvent(AccessibilityEvent event) {

  37. int eventType = event.getEventType();

  38. switch (eventType) {

  39. //通知栏来信息,判断是否含有微信红包字样,是的话跳转

  40. case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:

  41. List<CharSequence> texts = event.getText();

  42. for (CharSequence text : texts) {

  43. String content = text.toString();

  44. if (!TextUtils.isEmpty(content)) {

  45. //判断是否含有[微信红包]字样

  46. if (content.contains("[微信红包]")) {

  47. //如果有则打开微信红包页面

  48. openWeChatPage(event);

  49.  
  50. isOpenRP=false;

  51. }

  52. }

  53. }

  54. break;

  55. //界面跳转的监听

  56. case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:

  57. String className = event.getClassName().toString();

  58. //判断是否是微信聊天界面

  59. if (LAUCHER.equals(className)) {

  60. //获取当前聊天页面的根布局

  61. AccessibilityNodeInfo rootNode = getRootInActiveWindow();

  62. //开始找红包

  63. findRedPacket(rootNode);

  64. }

  65.  
  66. //判断是否是显示‘开’的那个红包界面

  67. if (LUCKEY_MONEY_RECEIVER.equals(className)) {

  68. AccessibilityNodeInfo rootNode = getRootInActiveWindow();

  69. //开始抢红包

  70. openRedPacket(rootNode);

  71. }

  72.  
  73. //判断是否是红包领取后的详情界面

  74. if(LUCKEY_MONEY_DETAIL.equals(className)){

  75. //返回桌面

  76. back2Home();

  77. }

  78. break;

  79. }

  80. }

  81.  
  82. /**

  83. * 开始打开红包

  84. */

  85. private void openRedPacket(AccessibilityNodeInfo rootNode) {

  86. for (int i = 0; i < rootNode.getChildCount(); i++) {

  87. AccessibilityNodeInfo node = rootNode.getChild(i);

  88. if ("android.widget.Button".equals(node.getClassName())) {

  89. node.performAction(AccessibilityNodeInfo.ACTION_CLICK);

  90. }

  91. openRedPacket(node);

  92. }

  93. }

  94.  
  95. /**

  96. * 遍历查找红包

  97. */

  98. private void findRedPacket(AccessibilityNodeInfo rootNode) {

  99. if (rootNode != null) {

  100. //从最后一行开始找起

  101. for (int i = rootNode.getChildCount() - 1; i >= 0; i--) {

  102. AccessibilityNodeInfo node = rootNode.getChild(i);

  103. //如果node为空则跳过该节点

  104. if (node == null) {

  105. continue;

  106. }

  107. CharSequence text = node.getText();

  108. if (text != null && text.toString().equals("领取红包")) {

  109. AccessibilityNodeInfo parent = node.getParent();

  110. //while循环,遍历"领取红包"的各个父布局,直至找到可点击的为止

  111. while (parent != null) {

  112. if (parent.isClickable()) {

  113. //模拟点击

  114. parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);

  115. //isOpenRP用于判断该红包是否点击过

  116. isOpenRP = true;

  117. break;

  118. }

  119. parent = parent.getParent();

  120. }

  121. }

  122. //判断是否已经打开过那个最新的红包了,是的话就跳出for循环,不是的话继续遍历

  123. if (isOpenRP) {

  124. break;

  125. } else {

  126. findRedPacket(node);

  127. }

  128.  
  129. }

  130. }

  131. }

  132.  
  133. /**

  134. * 开启红包所在的聊天页面

  135. */

  136. private void openWeChatPage(AccessibilityEvent event) {

  137. //A instanceof B 用来判断内存中实际对象A是不是B类型,常用于强制转换前的判断

  138. if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {

  139. Notification notification = (Notification) event.getParcelableData();

  140. //打开对应的聊天界面

  141. PendingIntent pendingIntent = notification.contentIntent;

  142. try {

  143. pendingIntent.send();

  144. } catch (PendingIntent.CanceledException e) {

  145. e.printStackTrace();

  146. }

  147. }

  148. }

  149.  
  150.  
  151. /**

  152. * 服务连接

  153. */

  154. @Override

  155. protected void onServiceConnected() {

  156. Toast.makeText(this, "抢红包服务开启", Toast.LENGTH_SHORT).show();

  157. super.onServiceConnected();

  158. }

  159.  
  160. /**

  161. * 必须重写的方法:系统要中断此service返回的响应时会调用。在整个生命周期会被调用多次。

  162. */

  163. @Override

  164. public void onInterrupt() {

  165. Toast.makeText(this, "我快被终结了啊-----", Toast.LENGTH_SHORT).show();

  166. }

  167.  
  168. /**

  169. * 服务断开

  170. */

  171. @Override

  172. public boolean onUnbind(Intent intent) {

  173. Toast.makeText(this, "抢红包服务已被关闭", Toast.LENGTH_SHORT).show();

  174. return super.onUnbind(intent);

  175. }

  176.  
  177. /**

  178. * 返回桌面

  179. */

  180. private void back2Home() {

  181. Intent home=new Intent(Intent.ACTION_MAIN);

  182. home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

  183. home.addCategory(Intent.CATEGORY_HOME);

  184. startActivity(home);

  185. }

  186.  
  187. }

 

 

使用方法:

  设置-辅助功能-无障碍-点击RedPacket开启即可

 

已知问题:

1.聊天列表或者聊天界面中无法直接自动抢红包

2.未做熄屏自动抢红包处理,想要熄屏能自动抢红包的同学直接把开屏代码写在第一步即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值