1. 程式人生 > >【Android】中微信搶紅包助手的實現(程式碼整理)

【Android】中微信搶紅包助手的實現(程式碼整理)

實現原理

  通過利用AccessibilityService輔助服務,監測螢幕內容,如監聽狀態列的資訊,螢幕跳轉等,以此來實現自動拆紅包的功能。關於AccessibilityService輔助服務,可以自行百度瞭解更多。

程式碼基礎:

1.首先宣告一個RedPacketService繼承自AccessibilityService,該服務類有兩個方法必須重寫,如下:

  1. /**

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

  3. * email:[email protected]

  4. *

  5. * 搶紅包服務類

  6. */

  7. public class RedPacketService extends AccessibilityService {

  8. /**

  9. * 必須重寫的方法:此方法用了接受系統發來的event。在你註冊的event發生是被呼叫。在整個生命週期會被呼叫多次。

  10. */

  11. @Override

  12. public void onAccessibilityEvent(AccessibilityEvent event) {

  13. }

  14. /**

  15. * 必須重寫的方法:系統要中斷此service返回的響應時會呼叫。在整個生命週期會被呼叫多次。

  16. */

  17. @Override

  18. public void onInterrupt() {

  19. Toast.makeText(this, "我快被終結了啊-----", Toast.LENGTH_SHORT).show();

  20. }

  21. /**

  22. * 服務已連線

  23. */

  24. @Override

  25. protected void onServiceConnected() {

  26. Toast.makeText(this, "搶紅包服務開啟", Toast.LENGTH_SHORT).show();

  27. super.onServiceConnected();

  28. }

  29. /**

  30. * 服務已斷開

  31. */

  32. @Override

  33. public boolean onUnbind(Intent intent) {

  34. Toast.makeText(this, "搶紅包服務已被關閉", Toast.LENGTH_SHORT).show();

  35. return super.onUnbind(intent);

  36. }

  37. }

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.未做熄屏自動搶紅包處理,想要熄屏能自動搶紅包的同學直接把開屏程式碼寫在第一步即可。