查看: 1394|回复: 0

[手机开发] Android中微信抢红包助手的实现详解

发表于 2017-7-11 08:00:00

实现原理

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

代码基础:

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

  1. /**
  2. * Created by cxk on 2017/2/3.
  3. *
  4. * 抢红包服务类
  5. */
  6. public class RedPacketService extends AccessibilityService {
  7. /**
  8. * 必须重写的方法:此方法用了接受系统发来的event。在你注册的event发生是被调用。在整个生命周期会被调用多次。
  9. */
  10. @Override
  11. public void onAccessibilityEvent(AccessibilityEvent event) {
  12. }
  13. /**
  14. * 必须重写的方法:系统要中断此service返回的响应时会调用。在整个生命周期会被调用多次。
  15. */
  16. @Override
  17. public void onInterrupt() {
  18. Toast.makeText(this, "我快被终结了啊-----", Toast.LENGTH_SHORT).show();
  19. }
  20. /**
  21. * 服务已连接
  22. */
  23. @Override
  24. protected void onServiceConnected() {
  25. Toast.makeText(this, "抢红包服务开启", Toast.LENGTH_SHORT).show();
  26. super.onServiceConnected();
  27. }
  28. /**
  29. * 服务已断开
  30. */
  31. @Override
  32. public boolean onUnbind(Intent intent) {
  33. Toast.makeText(this, "抢红包服务已被关闭", Toast.LENGTH_SHORT).show();
  34. return super.onUnbind(intent);
  35. }
  36. }
复制代码

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. private void openWeChatPage(AccessibilityEvent event) {
  25. //A instanceof B 用来判断内存中实际对象A是不是B类型,常用于强制转换前的判断
  26. if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
  27. Notification notification = (Notification) event.getParcelableData();
  28. //打开对应的聊天界面
  29. PendingIntent pendingIntent = notification.contentIntent;
  30. try {
  31. pendingIntent.send();
  32. } catch (PendingIntent.CanceledException e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. }
复制代码

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. }
复制代码

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. if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI".equals(className)) {
  10. AccessibilityNodeInfo rootNode = getRootInActiveWindow();
  11. //开始抢红包
  12. openRedPacket(rootNode);
  13. }
  14. break;
  15. }
  16. }
  17. /**
  18. * 开始打开红包
  19. */
  20. private void openRedPacket(AccessibilityNodeInfo rootNode) {
  21. for (int i = 0; i < rootNode.getChildCount(); i++) {
  22. AccessibilityNodeInfo node = rootNode.getChild(i);
  23. if ("android.widget.Button".equals(node.getClassName())) {
  24. node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
  25. }
  26. openRedPacket(node);
  27. }
  28. }
复制代码

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

  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. import java.util.List;
  17. /**
  18. * 抢红包Service,继承AccessibilityService
  19. */
  20. public class RedPacketService extends AccessibilityService {
  21. /**
  22. * 微信几个页面的包名+地址。用于判断在哪个页面 LAUCHER-微信聊天界面,LUCKEY_MONEY_RECEIVER-点击红包弹出的界面
  23. */
  24. private String LAUCHER = "com.tencent.mm.ui.LauncherUI";
  25. private String LUCKEY_MONEY_DETAIL = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI";
  26. private String LUCKEY_MONEY_RECEIVER = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI";
  27. /**
  28. * 用于判断是否点击过红包了
  29. */
  30. private boolean isOpenRP;
  31. @Override
  32. public void onAccessibilityEvent(AccessibilityEvent event) {
  33. int eventType = event.getEventType();
  34. switch (eventType) {
  35. //通知栏来信息,判断是否含有微信红包字样,是的话跳转
  36. case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
  37. List<CharSequence> texts = event.getText();
  38. for (CharSequence text : texts) {
  39. String content = text.toString();
  40. if (!TextUtils.isEmpty(content)) {
  41. //判断是否含有[微信红包]字样
  42. if (content.contains("[微信红包]")) {
  43. //如果有则打开微信红包页面
  44. openWeChatPage(event);
  45. isOpenRP=false;
  46. }
  47. }
  48. }
  49. break;
  50. //界面跳转的监听
  51. case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
  52. String className = event.getClassName().toString();
  53. //判断是否是微信聊天界面
  54. if (LAUCHER.equals(className)) {
  55. //获取当前聊天页面的根布局
  56. AccessibilityNodeInfo rootNode = getRootInActiveWindow();
  57. //开始找红包
  58. findRedPacket(rootNode);
  59. }
  60. //判断是否是显示‘开'的那个红包界面
  61. if (LUCKEY_MONEY_RECEIVER.equals(className)) {
  62. AccessibilityNodeInfo rootNode = getRootInActiveWindow();
  63. //开始抢红包
  64. openRedPacket(rootNode);
  65. }
  66. //判断是否是红包领取后的详情界面
  67. if(LUCKEY_MONEY_DETAIL.equals(className)){
  68. //返回桌面
  69. back2Home();
  70. }
  71. break;
  72. }
  73. }
  74. /**
  75. * 开始打开红包
  76. */
  77. private void openRedPacket(AccessibilityNodeInfo rootNode) {
  78. for (int i = 0; i < rootNode.getChildCount(); i++) {
  79. AccessibilityNodeInfo node = rootNode.getChild(i);
  80. if ("android.widget.Button".equals(node.getClassName())) {
  81. node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
  82. }
  83. openRedPacket(node);
  84. }
  85. }
  86. /**
  87. * 遍历查找红包
  88. */
  89. private void findRedPacket(AccessibilityNodeInfo rootNode) {
  90. if (rootNode != null) {
  91. //从最后一行开始找起
  92. for (int i = rootNode.getChildCount() - 1; i >= 0; i--) {
  93. AccessibilityNodeInfo node = rootNode.getChild(i);
  94. //如果node为空则跳过该节点
  95. if (node == null) {
  96. continue;
  97. }
  98. CharSequence text = node.getText();
  99. if (text != null && text.toString().equals("领取红包")) {
  100. AccessibilityNodeInfo parent = node.getParent();
  101. //while循环,遍历"领取红包"的各个父布局,直至找到可点击的为止
  102. while (parent != null) {
  103. if (parent.isClickable()) {
  104. //模拟点击
  105. parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
  106. //isOpenRP用于判断该红包是否点击过
  107. isOpenRP = true;
  108. break;
  109. }
  110. parent = parent.getParent();
  111. }
  112. }
  113. //判断是否已经打开过那个最新的红包了,是的话就跳出for循环,不是的话继续遍历
  114. if (isOpenRP) {
  115. break;
  116. } else {
  117. findRedPacket(node);
  118. }
  119. }
  120. }
  121. }
  122. /**
  123. * 开启红包所在的聊天页面
  124. */
  125. private void openWeChatPage(AccessibilityEvent event) {
  126. //A instanceof B 用来判断内存中实际对象A是不是B类型,常用于强制转换前的判断
  127. if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
  128. Notification notification = (Notification) event.getParcelableData();
  129. //打开对应的聊天界面
  130. PendingIntent pendingIntent = notification.contentIntent;
  131. try {
  132. pendingIntent.send();
  133. } catch (PendingIntent.CanceledException e) {
  134. e.printStackTrace();
  135. }
  136. }
  137. }
  138. /**
  139. * 服务连接
  140. */
  141. @Override
  142. protected void onServiceConnected() {
  143. Toast.makeText(this, "抢红包服务开启", Toast.LENGTH_SHORT).show();
  144. super.onServiceConnected();
  145. }
  146. /**
  147. * 必须重写的方法:系统要中断此service返回的响应时会调用。在整个生命周期会被调用多次。
  148. */
  149. @Override
  150. public void onInterrupt() {
  151. Toast.makeText(this, "我快被终结了啊-----", Toast.LENGTH_SHORT).show();
  152. }
  153. /**
  154. * 服务断开
  155. */
  156. @Override
  157. public boolean onUnbind(Intent intent) {
  158. Toast.makeText(this, "抢红包服务已被关闭", Toast.LENGTH_SHORT).show();
  159. return super.onUnbind(intent);
  160. }
  161. /**
  162. * 返回桌面
  163. */
  164. private void back2Home() {
  165. Intent home=new Intent(Intent.ACTION_MAIN);
  166. home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  167. home.addCategory(Intent.CATEGORY_HOME);
  168. startActivity(home);
  169. }
  170. }
复制代码

使用方法:

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

已知问题:

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持程序员之家。



回复

使用道具 举报