自动抢红包-辅助功能的使用

最近了解了一下这方面的知识,参考网上的一些博客,自己再完善了一下,写了一个自动抢红包的app.

本应用支持QQ和微信挂后台时自动抢红包
支持在微信和QQ的消息列表页面自动抢红包
支持在QQ群和单个好友消息会话页面自动抢红包
不支持在单个微信聊天页面自动抢

其实主要是 AccessibilityService相关的一些知识,可以参考android 文档,以下是国内的链接.
http://android.xsoftlab.net/reference/android/accessibilityservice/AccessibilityService.html

具体代码如下:

一.服务的声明和权限

1.服务与权限的声明


首先需要在 AndroidManifest.xml 里面声明一下AccessibilityService,并指定对应的服务配置文为 accessibilityservice.xml.这一部分的配置可以直接在android的文档上面拷贝就可以,再根据需要调整
      
      
      
      
  1. android:name=".RobMonkeyAccessibilityService"
  2. android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
  3. android:name="android.accessibilityservice.AccessibilityService"/>
  4. android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" />
由于实际功能中涉及到熄屏时自动亮屏并解锁手机,所以还要添加几个权限:
       
       
       
       
  1. android:name="android.permission.DISABLE_KEYGUARD"/>
  2. android:name="android.permission.WAKE_LOCK" />

2.服务的配置

accessibilityservice.xml
这里定义了需要几个主要项:
a.监听的状态:
android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged"
通知的改变(可以理解为有新通知来了没有);窗口的改变(主要是activity变了没有);窗口内容是否有变(主要是微信里面有用到fargement,所以只是内容变了,Activity没有变)
b.监听的应用
android:packageNames="com.tencent.mm,com.tencent.mobileqq"
      
      
      
      
  1. xml version="1.0" encoding="utf-8"?>
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged"
  4. android:accessibilityFeedbackType="feedbackGeneric"
  5. android:accessibilityFlags="flagDefault"
  6. android:canRetrieveWindowContent="true"
  7. android:notificationTimeout="100"
  8. android:packageNames="com.tencent.mm,com.tencent.mobileqq" />

二.AccessibilityService 的继承实现

主要是AccessibilityService的一些使用,其中还有涉及到亮屏和解锁相关的一些东西.

     
     
     
     
  1. package com.doov.robmoney;
  2. import android.accessibilityservice.AccessibilityService;
  3. import android.annotation.SuppressLint;
  4. import android.app.KeyguardManager;
  5. import android.app.Notification;
  6. import android.app.PendingIntent;
  7. import android.content.Context;
  8. import android.os.PowerManager;
  9. import android.util.Log;
  10. import android.view.accessibility.AccessibilityEvent;
  11. import android.view.accessibility.AccessibilityNodeInfo;
  12. import java.util.List;
  13. public class RobMonkeyAccessibilityService extends AccessibilityService {
  14. private static final String TAG = "RobMonkeyAccessibilityService";
  15. /**
  16. * 是否可以点击红包
  17. */
  18. private boolean canGetMonkey = false;
  19. private boolean isGetMonkey = false;
  20. /*当前窗口状态*/
  21. private int mWindowStatus = WINDOW_NONE;
  22. /*各种窗口状态*/
  23. private final static int WINDOW_LANUCHRE = 1;
  24. private final static int WINDOW_RECEIVERUI = 2;
  25. private final static int WINDOW_DETAILUI = 3;
  26. private final static int WINDOW_OTHER = 4;
  27. private final static int WINDOW_NONE = 5;
  28. private final static int WINDOW_QQ_LANUCHRE = 6;
  29. private PowerManager mPowerManager;
  30. private KeyguardManager mKeyguardManager;
  31. private KeyguardManager.KeyguardLock mKeyguardLock;
  32. private PowerManager.WakeLock mWakeLock;
  33. private boolean canGetQQMonkey = true;
  34. private AccessibilityNodeInfo mLastMonkeyNode = null;
  35. package com.doov.robmoney;
  36. import android.accessibilityservice.AccessibilityService;
  37. import android.annotation.SuppressLint;
  38. import android.app.KeyguardManager;
  39. import android.app.Notification;
  40. import android.app.PendingIntent;
  41. import android.content.Context;
  42. import android.os.PowerManager;
  43. import android.util.Log;
  44. import android.view.accessibility.AccessibilityEvent;
  45. import android.view.accessibility.AccessibilityNodeInfo;
  46. import java.util.List;
  47. public class RobMonkeyAccessibilityService extends AccessibilityService {
  48. private static final String TAG = "RobMonkeyAccessibilityService";
  49. /**
  50. * 是否可以点击红包
  51. */
  52. private boolean canGetMonkey = false;
  53. private boolean isGetMonkey = false;
  54. /*当前窗口状态*/
  55. private int mWindowStatus = WINDOW_NONE;
  56. /*各种窗口状态*/
  57. private final static int WINDOW_LANUCHRE = 1;
  58. private final static int WINDOW_RECEIVERUI = 2;
  59. private final static int WINDOW_DETAILUI = 3;
  60. private final static int WINDOW_OTHER = 4;
  61. private final static int WINDOW_NONE = 5;
  62. private final static int WINDOW_QQ_LANUCHRE = 6;
  63. private PowerManager mPowerManager;
  64. private KeyguardManager mKeyguardManager;
  65. private KeyguardManager.KeyguardLock mKeyguardLock;
  66. private PowerManager.WakeLock mWakeLock;
  67. private boolean canGetQQMonkey = true;
  68. private AccessibilityNodeInfo mLastMonkeyNode = null;
  69. @Override
  70. public void onAccessibilityEvent(AccessibilityEvent event) {
  71. int eventType = event.getEventType();
  72. Log.d(TAG, "onAccessibilityEvent: eventType=" + eventType);
  73. switch (eventType) {
  74. case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED://64 有通知
  75. List<CharSequence> notifications = event.getText();
  76. if (notifications != null) {
  77. for (CharSequence c : notifications) {
  78. String s = c.toString();
  79. Log.d(TAG, "onAccessibilityEvent: s=" + s);
  80. if (s.contains("[微信红包]") || s.contains("[QQ红包]")) {
  81. Notification notification = (Notification) event.getParcelableData();
  82. PendingIntent pendingIntent = notification.contentIntent;
  83. try {
  84. wakeUpAndUnLock(true);//亮屏并解鎖
  85. canGetMonkey = true;
  86. pendingIntent.send();
  87. } catch (PendingIntent.CanceledException e) {
  88. e.printStackTrace();
  89. }
  90. }
  91. }
  92. }
  93. break;
  94. case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED://32
  95. String clazz = event.getClassName().toString();
  96. Log.d(TAG, "onAccessibilityEvent: clazz=" + clazz);
  97. if ("com.tencent.mm.ui.LauncherUI".equals(clazz)) {//微信消息列表页面
  98. mWindowStatus = WINDOW_LANUCHRE;
  99. getWeiXinPacket();
  100. } else if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI".equals(clazz)) {//微信抢红包页面
  101. mWindowStatus = WINDOW_RECEIVERUI;
  102. openWeiXinPacket();
  103. } else if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI".equals(clazz)) {//微信已领取红包列表页面
  104. mWindowStatus = WINDOW_DETAILUI;
  105. //performBackClick();
  106. } else if ("com.tencent.mobileqq.activity.SplashActivity".equals(clazz)) {//QQ消息列表頁面
  107. mWindowStatus = WINDOW_QQ_LANUCHRE;
  108. //getQQPacket();
  109. } else if ("cooperation.qwallet.plugin.QWalletPluginProxyActivity".equals(clazz)) {//QQ紅包頁面
  110. mWindowStatus = WINDOW_OTHER;
  111. //performBackClick();
  112. } else {
  113. mWindowStatus = WINDOW_OTHER;
  114. }
  115. break;
  116. case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED://2048
  117. if (mWindowStatus == WINDOW_LANUCHRE) {//在消息列表页面,其他页面不处理
  118. Log.d(TAG, "onAccessibilityEvent: canGetMonkey=" + canGetMonkey + " isGetMonkey=" + isGetMonkey);
  119. if (isGetMonkey) {
  120. return;
  121. }
  122. getWeiXinPacket();
  123. }
  124. if (mWindowStatus == WINDOW_QQ_LANUCHRE) {
  125. if (canGetQQMonkey) openQQPacket();
  126. }
  127. break;
  128. default:
  129. break;
  130. }
  131. }
  132. /**
  133. * 打开QQ红包
  134. */
  135. private void openQQPacket() {
  136. Log.i(TAG, "openQQPacket: ");
  137. //com.tencent.mobileqq:id/name TextView 点击拆开
  138. AccessibilityNodeInfo rootNote = getRootInActiveWindow();
  139. if (rootNote != null) {
  140. List<AccessibilityNodeInfo> list = rootNote.findAccessibilityNodeInfosByText("QQ红包");
  141. if (list != null && !list.isEmpty()) {
  142. Log.d(TAG, "openQQPacket() list not null");
  143. for (AccessibilityNodeInfo i : list) {
  144. AccessibilityNodeInfo parent = i.getParent();
  145. if (null != findNodeInfosByText(parent, "点击拆开")) {
  146. canGetQQMonkey = fals1e;//打开红包时不去响应其他消息的事件
  147. performClick(i.getParent());
  148. }
  149. }
  150. }
  151. }
  152. canGetQQMonkey = true;
  153. }
  154. private void getQQPacket() {
  155. Log.i(TAG, "getQQPacket: ");
  156. AccessibilityNodeInfo rootNote = getRootInActiveWindow();
  157. if (rootNote != null) {
  158. AccessibilityNodeInfo recent_chat_list = findNodeInfosById(rootNote, "com.tencent.mobileqq:id/recent_chat_list");//消息列表页面
  159. if (recent_chat_list != null) {
  160. /* List list = recent_chat_list.findAccessibilityNodeInfosByText("[有红包][QQ红包]恭喜发财");
  161. if (list != null && !list.isEmpty()) {
  162. Log.d(TAG, "getQQPacket() list not null");
  163. AccessibilityNodeInfo note = list.get(list.size() -1);//最新的有红包的会话
  164. performClick(note);
  165. }*/
  166. /*int count = recent_chat_list.getChildCount();
  167. for (int i = 0; i
  168. AccessibilityNodeInfo note = recent_chat_list.getChild(i);
  169. Log.d(TAG, "getQQPacket:" + note.getText() + "--" + note.getViewIdResourceName());
  170. }*/
  171. }
  172. }
  173. }
  174. /**
  175. * 喚醒屏幕 解锁
  176. *
  177. * @param unlock
  178. */
  179. private void wakeUpAndUnLock(boolean unlock) {
  180. Log.d(TAG, "wakeUpAndUnLock() called with: unlock = [" + unlock + "]");
  181. if (unlock) {
  182. if (!mPowerManager.isScreenOn()) {//熄屏时
  183. Log.d(TAG, "wakeUpAndUnLock: wakeup");
  184. mWakeLock.acquire();
  185. mWakeLock.release();
  186. }
  187. Log.d(TAG, "wakeUpAndUnLock: isKeyguardLocked=" + mKeyguardManager.isKeyguardLocked() + " isKeyguardSecure=" + mKeyguardManager.isKeyguardSecure());
  188. if (mKeyguardManager.isKeyguardLocked() && !mKeyguardManager.isKeyguardSecure()) {
  189. mKeyguardLock.disableKeyguard();
  190. Log.d(TAG, "wakeUpAndUnLock: disableKeyguard");
  191. }
  192. } else {
  193. if (!mKeyguardManager.isKeyguardLocked()) {
  194. mKeyguardLock.reenableKeyguard();
  195. Log.d(TAG, "wakeUpAndUnLock: reenableKeyguard");
  196. }
  197. }
  198. }
  199. /**
  200. * 打开红包
  201. */
  202. private void openWeiXinPacket() {
  203. Log.d(TAG, "openWeiXinPacket: 开始打开红包");
  204. AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
  205. if (rootNodeInfo == null) {
  206. return;
  207. }
  208. AccessibilityNodeInfo openTarget = null;
  209. //可以根据id 也可以根据文字来操作
  210. //com.tencent.mm:id/bg_ 看看大家的手气
  211. openTarget = findNodeInfosByText(rootNodeInfo, "看看大家的手气");
  212. Log.d(TAG, "openWeiXinPacket: openTarget=" + openTarget);
  213. if (openTarget != null) {//红包抢完了
  214. performGlobalAction(GLOBAL_ACTION_BACK);
  215. }
  216. if (openTarget == null) {
  217. for (int i = 0; i < rootNodeInfo.getChildCount(); i++) {
  218. AccessibilityNodeInfo note = rootNodeInfo.getChild(i);
  219. //可以根据id 也可以根据是否是Button.这些信息可以通过DDMS 来获取
  220. //android.widget.Button com.tencent.mm:id/bg7
  221. if ("android.widget.Button".equals(note.getClassName())) {
  222. openTarget = note;
  223. break;
  224. }
  225. }
  226. }
  227. //如果有"开"这个按钮图标就点击
  228. if (openTarget != null) {
  229. performClick(openTarget);
  230. }
  231. }
  232. /**
  233. * 准备打开红包:打开红包弹窗
  234. */
  235. @SuppressLint("NewApi")
  236. private void getWeiXinPacket() {
  237. Log.d(TAG, "getWeiXinPacket");
  238. AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
  239. if (rootNodeInfo == null) {
  240. return;
  241. }
  242. List<AccessibilityNodeInfo> nodes = rootNodeInfo.findAccessibilityNodeInfosByText("领取红包");
  243. Log.d(TAG, "getWeiXinPacket nodes is null? " + (nodes == null));
  244. if (nodes != null) {
  245. if (nodes.isEmpty()) {//在好友会话列表页面
  246. AccessibilityNodeInfo info = findNodeInfosByText(rootNodeInfo, "[微信红包]");
  247. if (info != null) {
  248. canGetMonkey = true;
  249. performClick(info);
  250. Log.d(TAG, "getWeiXinPacket canGetMonkey= true");
  251. }
  252. } else {//在单个群会话页面
  253. if (canGetMonkey) {
  254. //点击最新的红包
  255. AccessibilityNodeInfo newMoney = nodes.get(nodes.size() - 1);
  256. performClick(newMoney);
  257. Log.d(TAG, "getWeiXinPacket canGetMonkey= false");
  258. canGetMonkey = false;
  259. } else {//单个会话里面接到红包时
  260. //单个会话:ListView
  261. Log.d(TAG, "getWeiXinPacket 对话中有红包项");
  262. //单个会话列表:ListView
  263. /*AccessibilityNodeInfo messages = findNodeInfosById(rootNodeInfo,"com.tencent.mm:id/a25");
  264. if(null != messages){
  265. AccessibilityNodeInfo monkeyNode = null;
  266. AccessibilityNodeInfo monkeyopendMessNote = null;
  267. AccessibilityNodeInfo mextNote = null;
  268. isGetMonkey = true;
  269. int messageCount = messages.getChildCount();
  270. Log.d(TAG, "getWeiXinPacket: messageCount=" + messageCount);
  271. for (int i=0; i
  272. monkeyNode = messages.getChild(i);
  273. if(null != findNodeInfosByText(monkeyNode,"领取红包")){
  274. monkeyopendMessNote = i==messageCount-1 ?null:messages.getChild(i+1);
  275. mextNote = findNodeInfosByText(monkeyopendMessNote,"领取红包");//下一个记录还是红包项时,不领取,只领最新的那个
  276. //红包项下一项不是"你领取xx的红包"就表示当前红包没有领取
  277. monkeyopendMessNote = findNodeInfosById(monkeyopendMessNote,"com.tencent.mm:id/a3g");//头像
  278. Log.d(TAG, "getWeiXinPacket: monkeyopendMessNote=" +monkeyopendMessNote +
  279. " \n mextNote=" +mextNote);
  280. //当前记录的下一个记录不是"已领取的消息" 也不是红包消息
  281. if ( monkeyopendMessNote == null && mextNote == null) {
  282. Log.d(TAG, "getWeiXinPacket: 有未领取的红包");
  283. performClick(findNodeInfosByText(monkeyNode,"领取红包"));
  284. }else{
  285. Log.d(TAG, "getWeiXinPacket: 红包已经领取了或者不是最新的红包");
  286. }
  287. }
  288. }
  289. isGetMonkey = false;
  290. }*/
  291. //保存最新的红包,在单个会话页面,只判断最新的红包是否可以抢(是否已经抢过了)
  292. isGetMonkey = true;
  293. //单个会话列表:ListView
  294. //AccessibilityNodeInfo messages = findNodeInfosById(rootNodeInfo,"com.tencent.mm:id/a25");
  295. //mLastMonkeyNode = nodes.get(nodes.size()-1);
  296. //int index = nodes.indexOf(mLastMonkeyNode);
  297. for (int i = 0; i < nodes.size(); i++) {
  298. Log.i(TAG, "getWeiXinPacket: i=" + i + " " + nodes.get(i));
  299. }
  300. isGetMonkey = false;
  301. }
  302. }
  303. }
  304. }
  305. /**
  306. * 点击指定的节点(不可以点击时点击其父节点)
  307. *
  308. * @param info
  309. */
  310. private void performClick(AccessibilityNodeInfo info) {
  311. Log.d(TAG, "performClick");
  312. if (info != null) {
  313. if (info.isClickable()) {
  314. info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
  315. } else {
  316. performClick(info.getParent());
  317. }
  318. }
  319. }
  320. /**
  321. * 模拟点击back 键返回一次
  322. */
  323. private void performBackClick() {
  324. Log.i(TAG, "performBackClick: ");
  325. performGlobalAction(GLOBAL_ACTION_BACK);
  326. }
  327. /**
  328. * 通过指定的文本查找节点
  329. *
  330. * @param rootNodeInfo
  331. * @param s
  332. * @return
  333. */
  334. private AccessibilityNodeInfo findNodeInfosByText(AccessibilityNodeInfo rootNodeInfo, String s) {
  335. //Log.d(TAG, "findNodeInfosByText() called with: s = [" + s + "]");
  336. if (rootNodeInfo != null) {
  337. List<AccessibilityNodeInfo> list = rootNodeInfo.findAccessibilityNodeInfosByText(s);
  338. if (list != null && !list.isEmpty()) {
  339. return list.get(0);
  340. }
  341. }
  342. return null;
  343. }
  344. /**
  345. * 通过指定的id查找节点
  346. *
  347. * @param rootNodeInfo
  348. * @param id
  349. * @return
  350. */
  351. private AccessibilityNodeInfo findNodeInfosById(AccessibilityNodeInfo rootNodeInfo, String id) {
  352. Log.d(TAG, "findNodeInfosById() called with: id = [" + id + "]");
  353. if (rootNodeInfo != null) {
  354. List<AccessibilityNodeInfo> list = rootNodeInfo.findAccessibilityNodeInfosByViewId(id);
  355. if (list != null && !list.isEmpty()) {
  356. return list.get(0);
  357. }
  358. }
  359. return null;
  360. }
  361. @Override
  362. public void onInterrupt() {
  363. Log.i(TAG, "onInterrupt: ");
  364. }
  365. @Override
  366. protected void onServiceConnected() {
  367. super.onServiceConnected();
  368. Log.d(TAG, "onServiceConnected: 抢红包服务以及开启");
  369. mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
  370. mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "rob_monkey_wakeup");
  371. mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
  372. mKeyguardLock = mKeyguardManager.newKeyguardLock("rob_monkey_keyguard_lock");
  373. }
  374. @Override
  375. public void onDestroy() {
  376. super.onDestroy();
  377. Log.i(TAG, "onDestroy: ");
  378. if (null != mWakeLock) {
  379. mWakeLock.release();
  380. mWakeLock = null;
  381. }
  382. }
  383. }

你可能感兴趣的:(Android)