Android輔助功能AccessibilityService與搶紅包輔助
推薦閱讀:Android中微信搶紅包插件原理解析及開發(fā)思路
搶紅包的原理都差不多,一般是用Android的輔助功能(AccessibilityService類)先監(jiān)聽通知欄事件或窗口變化事件來查找紅包關(guān)鍵字然后去模擬點(diǎn)擊或打開紅包。
下面附上源碼,程序已實(shí)現(xiàn)自動(dòng)搶紅包,鎖屏黑屏狀態(tài)自動(dòng)解鎖亮屏,Android4.X測(cè)試通過。函數(shù)具體功能請(qǐng)看詳細(xì)注釋。
注:在聊天界面收到紅包不會(huì)自動(dòng)打開,因?yàn)橥ㄖ獧跊]有消息提示從而監(jiān)聽不了,此時(shí)只需手動(dòng)點(diǎn)一下即可。其他未知情況請(qǐng)自行用LogCat調(diào)試,源碼已經(jīng)有相關(guān)的調(diào)試信息。軟件僅供學(xué)習(xí)娛樂。
<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;//能否點(diǎn)擊紅包 private boolean enableKeyguard = true;//默認(rèn)有屏幕鎖 //窗口狀態(tài) 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; //當(dāng)前窗口 private int mCurrentWindow = WINDOW_NONE; //鎖屏、解鎖相關(guān) private KeyguardManager km; private KeyguardLock kl; //喚醒屏幕相關(guān) 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) { //若為黑屏狀態(tài)則喚醒屏幕 if(!pm.isScreenOn()) { //獲取電源管理器對(duì)象,ACQUIRE_CAUSES_WAKEUP這個(gè)參數(shù)能從黑屏喚醒屏幕 wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "bright"); //點(diǎn)亮屏幕 wl.acquire(); Log.i("demo", "亮屏"); } //若在鎖屏界面則解鎖直接跳過鎖屏 if(km.inKeyguardRestrictedInputMode()) { //設(shè)置解鎖標(biāo)志,以判斷搶完紅包能否鎖屏 enableKeyguard = false; //解鎖 kl.disableKeyguard(); Log.i("demo", "解鎖"); } } else { //如果之前解過鎖則加鎖以恢復(fù)原樣 if(!enableKeyguard) { //鎖屏 kl.reenableKeyguard(); Log.i("demo", "加鎖"); } //若之前喚醒過屏幕則釋放之使屏幕不保持常亮 if(wl != null) { wl.release(); wl = null; Log.i("demo", "關(guān)燈"); } } } //通過文本查找節(jié)點(diǎn) public AccessibilityNodeInfo findNodeInfosByText(AccessibilityNodeInfo nodeInfo, String text) { List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(text); if(list == null || list.isEmpty()) { return null; } return list.get(0); } //模擬點(diǎn)擊事件 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); } //實(shí)現(xiàn)輔助功能 @Override public void onAccessibilityEvent(AccessibilityEvent event) { int eventType = event.getEventType(); Log.i("demo", Integer.toString(eventType)); switch (eventType) { //第一步:監(jiān)聽通知欄消息 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); //若是微信紅包則解鎖并自動(dòng)打開,若是qq紅包則只提示并跳轉(zhuǎn)到有紅包的聊天界面,暫未實(shí)現(xiàn)qq紅包自動(dòng)領(lǐng)取功能 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; //第二步:監(jiān)聽是否進(jìn)入微信紅包消息界面 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", "準(zhǔn)備搶紅包..."); 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; } } //找到紅包并點(diǎn)擊 @SuppressLint("NewApi") private void getPacket() { AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); if (nodeInfo == null) { return; } // 找到領(lǐng)取紅包的點(diǎn)擊事件 List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("領(lǐng)取紅包"); if(list != null ) { if(list.isEmpty()) { Log.i("demp", "領(lǐng)取列表為空"); // 從消息列表查找紅包 AccessibilityNodeInfo node = findNodeInfosByText(nodeInfo, "[微信紅包]"); if(node != null) { canGet = true; performClick(node); } } else { if(canGet) { //最新的紅包領(lǐng)起 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; //如果紅包已經(jīng)被搶完則直接返回 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; } } } //若查找到打開按鈕則模擬點(diǎn)擊 if(targetNode != null) { final AccessibilityNodeInfo n = targetNode; performClick(n); } } @Override public void onInterrupt() { Toast.makeText(this, "搶紅包服務(wù)被中斷啦~", Toast.LENGTH_LONG).show(); } @Override protected void onServiceConnected() { super.onServiceConnected(); Log.i("demo", "開啟"); //獲取電源管理器對(duì)象 pm=(PowerManager)getSystemService(Context.POWER_SERVICE); //得到鍵盤鎖管理器對(duì)象 km= (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE); //初始化一個(gè)鍵盤鎖管理器對(duì)象 kl = km.newKeyguardLock("unLock"); //初始化音頻 player = MediaPlayer.create(this, R.raw.songtip_m); Toast.makeText(this, "_已開啟搶紅包服務(wù)_", Toast.LENGTH_LONG).show(); } @Override public void onDestroy() { super.onDestroy(); Log.i("demo", "關(guān)閉"); wakeAndUnlock(false); Toast.makeText(this, "_已關(guān)閉搶紅包服務(wù)_", Toast.LENGTH_LONG).show(); } }
AndroidManifest.xml中聲明相關(guān)服務(wù)和權(quán)限
<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服務(wù)配置內(nèi)容如下
<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為輔助功能的描述內(nèi)容,packageNames為監(jiān)聽的程序包名,此處只監(jiān)聽微信和QQ的accessibilityEventTypes
以上所述是針對(duì)Android輔助功能AccessibilityService與搶紅包輔助的相關(guān)知識(shí),希望對(duì)大家有所幫助。
相關(guān)文章
Android 自定義view實(shí)現(xiàn)TopBar效果
這篇文章主要為大家詳細(xì)介紹了Android 自定義view實(shí)現(xiàn)TopBar效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09Android 6.0權(quán)限申請(qǐng)?jiān)斀饧皺?quán)限資料整理
這篇文章主要介紹了Android 6.0權(quán)限申請(qǐng)?jiān)斀饧皺?quán)限資料整理的相關(guān)資料,需要的朋友可以參考下2016-10-10mac系統(tǒng)下載、安裝、使用AndroidStudio
本文給大家介紹的是在Mac系統(tǒng)中下載安裝以及使用AndroidStudio的詳細(xì)教程,非常的實(shí)用,有需要的小伙伴可以參考下2017-10-10Android通過XListView實(shí)現(xiàn)上拉加載下拉刷新功能
這篇文章主要為大家詳細(xì)介紹了Android通過XListView實(shí)現(xiàn)上拉加載下拉刷新功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Android實(shí)現(xiàn)GridView中ImageView動(dòng)態(tài)變換的方法
這篇文章主要介紹了Android實(shí)現(xiàn)GridView中ImageView動(dòng)態(tài)變換的方法,以實(shí)例形式較為詳細(xì)的分析了GridView中ImageView動(dòng)態(tài)變換的頁(yè)面布局及功能實(shí)現(xiàn)相關(guān)技巧,需要的朋友可以參考下2015-10-10android socket聊天室功能實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了android socket聊天室功能實(shí)現(xiàn)方法,不單純是聊天室,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03