抢红包的原理都差不多,一般是用Android的辅助功能(AccessibilityService类)先监听通知栏事件或窗口变化事件来查找红包关键字然后去模拟点击或打开红包。
下面附上源码,程序已实现自动抢红包,锁屏黑屏状态自动解锁亮屏,Android4.X测试通过。函数具体功能请看详细注释。
注:在聊天界面收到红包不会自动打开,因为通知栏没有消息提示从而监听不了,此时只需手动点一下即可。其他未知情况请自行用LogCat调试,源码已经有相关的调试信息。软件仅供学习娱乐。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
|
<pre style= "margin-top: 0px; margin-bottom: 0px;" ><span style= "font-family: Arial, Helvetica, sans-serif; color: rgb(192, 192, 192);" ></span><pre style= "margin-top: 0px; margin-bottom: 0px;" > import java.util.Calendar; import java.util.List; import android.accessibilityservice.AccessibilityService; import android.annotation.SuppressLint; import android.app.KeyguardManager; import android.app.KeyguardManager.KeyguardLock; import android.app.Notification; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.content.Context; import android.media.MediaPlayer; import android.os.PowerManager; import android.util.Log; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.Toast; public class Demo extends AccessibilityService { private boolean canGet = false ; //能否点击红包 private boolean enableKeyguard = true ; //默认有屏幕锁 //窗口状态 private static final int WINDOW_NONE = 0 ; private static final int WINDOW_LUCKYMONEY_RECEIVEUI = 1 ; private static final int WINDOW_LUCKYMONEY_DETAIL = 2 ; private static final int WINDOW_LAUNCHER = 3 ; private static final int WINDOW_OTHER = - 1 ; //当前窗口 private int mCurrentWindow = WINDOW_NONE; //锁屏、解锁相关 private KeyguardManager km; private KeyguardLock kl; //唤醒屏幕相关 private PowerManager pm; private PowerManager.WakeLock wl = null ; //播放提示声音 private MediaPlayer player; public void playSound(Context context) { Calendar cal = Calendar.getInstance(); int hour = cal.get(Calendar.HOUR_OF_DAY); //夜间不播放提示音 if (hour > 7 && hour < 22 ) { player.start(); } } //唤醒屏幕和解锁 private void wakeAndUnlock( boolean unLock) { if (unLock) { //若为黑屏状态则唤醒屏幕 if (!pm.isScreenOn()) { //获取电源管理器对象,ACQUIRE_CAUSES_WAKEUP这个参数能从黑屏唤醒屏幕 wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "bright" ); //点亮屏幕 wl.acquire(); Log.i( "demo" , "亮屏" ); } //若在锁屏界面则解锁直接跳过锁屏 if (km.inKeyguardRestrictedInputMode()) { //设置解锁标志,以判断抢完红包能否锁屏 enableKeyguard = false ; //解锁 kl.disableKeyguard(); Log.i( "demo" , "解锁" ); } } else { //如果之前解过锁则加锁以恢复原样 if (!enableKeyguard) { //锁屏 kl.reenableKeyguard(); Log.i( "demo" , "加锁" ); } //若之前唤醒过屏幕则释放之使屏幕不保持常亮 if (wl != null ) { wl.release(); wl = null ; Log.i( "demo" , "关灯" ); } } } //通过文本查找节点 public AccessibilityNodeInfo findNodeInfosByText(AccessibilityNodeInfo nodeInfo, String text) { List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(text); if (list == null || list.isEmpty()) { return null ; } return list.get( 0 ); } //模拟点击事件 public void performClick(AccessibilityNodeInfo nodeInfo) { if (nodeInfo == null ) { return ; } if (nodeInfo.isClickable()) { nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); } else { performClick(nodeInfo.getParent()); } } //模拟返回事件 public void performBack(AccessibilityService service) { if (service == null ) { return ; } service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK); } //实现辅助功能 @Override public void onAccessibilityEvent(AccessibilityEvent event) { int eventType = event.getEventType(); Log.i( "demo" , Integer.toString(eventType)); switch (eventType) { //第一步:监听通知栏消息 case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: List<CharSequence> texts = event.getText(); if (!texts.isEmpty()) { for (CharSequence text : texts) { String content = text.toString(); Log.i( "demo" , "text:" +content); //收到红包提醒 if (content.contains( "[微信红包]" )||content.contains( "[QQ红包]" )) { //模拟打开通知栏消息 if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) { //播放提示音 playSound( this ); //若是微信红包则解锁并自动打开,若是qq红包则只提示并跳转到有红包的聊天界面,暂未实现qq红包自动领取功能 if (content.contains( "[微信红包]" )) wakeAndUnlock( true ); Log.i( "demo" , "canGet=true" ); canGet = true ; try { Notification notification = (Notification) event.getParcelableData(); PendingIntent pendingIntent = notification.contentIntent; pendingIntent.send(); } catch (CanceledException e) { e.printStackTrace(); } } break ; } } } break ; //第二步:监听是否进入微信红包消息界面 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: String className = event.getClassName().toString(); if (className.equals( "com.tencent.mm.ui.LauncherUI" )) { mCurrentWindow = WINDOW_LAUNCHER; //开始抢红包 Log.i( "demo" , "准备抢红包..." ); getPacket(); } else if (className.equals( "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI" )) { mCurrentWindow = WINDOW_LUCKYMONEY_RECEIVEUI; //开始打开红包 Log.i( "demo" , "打开红包" ); openPacket(); wakeAndUnlock( false ); } else if (className.equals( "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI" )) { mCurrentWindow = WINDOW_LUCKYMONEY_DETAIL; //返回以方便下次收红包 Log.i( "demo" , "返回" ); performBack( this ); } else { mCurrentWindow = WINDOW_OTHER; } break ; case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: if (mCurrentWindow != WINDOW_LAUNCHER) { //不在聊天界面或聊天列表,不处理 return ; } if (canGet) { getPacket(); } break ; } } //找到红包并点击 @SuppressLint ( "NewApi" ) private void getPacket() { AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); if (nodeInfo == null ) { return ; } // 找到领取红包的点击事件 List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText( "领取红包" ); if (list != null ) { if (list.isEmpty()) { Log.i( "demp" , "领取列表为空" ); // 从消息列表查找红包 AccessibilityNodeInfo node = findNodeInfosByText(nodeInfo, "[微信红包]" ); if (node != null ) { canGet = true ; performClick(node); } } else { if (canGet) { //最新的红包领起 AccessibilityNodeInfo node = list.get(list.size() - 1 ); performClick(node); Log.i( "demo" , "canGet=false" ); canGet = false ; } } } } //打开红包 @SuppressLint ( "NewApi" ) private void openPacket() { AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); if (nodeInfo == null ) { return ; } Log.i( "demo" , "查找打开按钮..." ); AccessibilityNodeInfo targetNode = null ; //如果红包已经被抢完则直接返回 targetNode = findNodeInfosByText(nodeInfo, "看看大家的手气" ); if (targetNode != null ) { performBack( this ); return ; } //通过组件名查找开红包按钮,还可通过组件id直接查找但需要知道id且id容易随版本更新而变化,旧版微信还可直接搜“開”字找到按钮 if (targetNode == null ) { Log.i( "demo" , "打开按钮中..." ); for ( int i = 0 ; i < nodeInfo.getChildCount(); i++) { AccessibilityNodeInfo node = nodeInfo.getChild(i); if ( "android.widget.Button" .equals(node.getClassName())) { targetNode = node; break ; } } } //若查找到打开按钮则模拟点击 if (targetNode != null ) { final AccessibilityNodeInfo n = targetNode; performClick(n); } } @Override public void onInterrupt() { Toast.makeText( this , "抢红包服务被中断啦~" , Toast.LENGTH_LONG).show(); } @Override protected void onServiceConnected() { super .onServiceConnected(); Log.i( "demo" , "开启" ); //获取电源管理器对象 pm=(PowerManager)getSystemService(Context.POWER_SERVICE); //得到键盘锁管理器对象 km= (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE); //初始化一个键盘锁管理器对象 kl = km.newKeyguardLock( "unLock" ); //初始化音频 player = MediaPlayer.create( this , R.raw.songtip_m); Toast.makeText( this , "_已开启抢红包服务_" , Toast.LENGTH_LONG).show(); } @Override public void onDestroy() { super .onDestroy(); Log.i( "demo" , "关闭" ); wakeAndUnlock( false ); Toast.makeText( this , "_已关闭抢红包服务_" , Toast.LENGTH_LONG).show(); } } |
AndroidManifest.xml中声明相关服务和权限
1
2
3
4
5
6
7
8
9
|
<uses-permission android:name= "android.permission.WAKE_LOCK" /> <uses-permission android:name= "android.permission.DISABLE_KEYGUARD" /> <application android:allowBackup= "true" android:icon= "@drawable/ic_launcher" android:label= "@string/app_name" android:theme= "@style/AppTheme" > <pre name= "code" class = "html" ><service android:name= "com.example.test.Demo" android:enabled= "true" android:exported= "true" android:label= "@string/app_name" android:permission= "android.permission.BIND_ACCESSIBILITY_SERVICE" > <intent-filter> <action android:name= "android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name= "android.accessibilityservice" android:resource= "@layout/accessibility_config" /></service></application> |
accessibility_config.xml服务配置内容如下
1
2
3
4
5
6
7
8
|
<accessibility-service xmlns:android= "http://schemas.android.com/apk/res/android" android:accessibilityEventTypes= "typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged" android:accessibilityFeedbackType= "feedbackGeneric" android:accessibilityFlags= "flagDefault" android:canRetrieveWindowContent= "true" android:description= "@string/desc" android:notificationTimeout= "100" android:packageNames= "com.tencent.mm,com.tencent.mobileqq" /> |
其中description为辅助功能的描述内容,packageNames为监听的程序包名,此处只监听微信和QQ的accessibilityEventTypes
以上所述是针对Android辅助功能AccessibilityService与抢红包辅助的相关知识,希望对大家有所帮助。