教你一步步實現(xiàn)Android微信自動搶紅包
本文介紹微信自動搶紅包的實現(xiàn)方法,主要實現(xiàn)以下幾個功能:
1.自動拆開屏幕上出現(xiàn)的紅包
2.處于桌面或聊天列表時接收到紅包信息時自動進入聊天界面并拆紅包
3.日志功能,記錄搶紅包的詳細日志
實現(xiàn)原理
1.利用AccessibilityService輔助服務(wù),監(jiān)測屏幕內(nèi)容,實現(xiàn)自動拆紅包的目的。
2.利用ActiveAndroid數(shù)據(jù)庫簡單記錄紅包日志
3.利用preference實現(xiàn)監(jiān)控選項紀錄
最終界面
搶紅包核心代碼
AccessibilityService配置
android:accessibilityEventTypes
設(shè)置觸發(fā)監(jiān)聽回調(diào)的事件類型;
android:packageNames
設(shè)置監(jiān)聽的應(yīng)用,這里監(jiān)聽的是微信,因此填上微信的包名com.tencent.mm
<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/accessibility_description" android:notificationTimeout="100" android:packageNames="com.tencent.mm" android:settingsActivity="com.oden.annotations.app.activity.ManActivity" />
在AndroidManifest.xml中聲明:
<service android:name=".app.service.HongbaoService_" 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="@xml/accessibility_service_config" /> </service>
搶紅包實現(xiàn)代碼
接收系統(tǒng)發(fā)送來的AccessibilityEvent
private static final String GET_RED_PACKET = "領(lǐng)取紅包"; private static final String CHECK_RED_PACKET = "查看紅包"; private static final String RED_PACKET_PICKED = "手慢了,紅包派完了"; private static final String RED_PACKET_PICKED2 = "手氣"; private static final String RED_PACKET_PICKED_DETAIL = "紅包詳情"; private static final String RED_PACKET_SAVE = "已存入零錢"; private static final String RED_PACKET_NOTIFICATION = "[微信紅包]"; @Override public void onAccessibilityEvent(AccessibilityEvent event) { L.d("RECEIVE EVENT!"); if (watchedFlags == null) return; /* 檢測通知消息 */ if (!mMutex) { if (watchedFlags.get("pref_watch_notification") && watchNotifications(event)) return; if (watchedFlags.get("pref_watch_list") && watchList(event)) return; } if (!watchedFlags.get("pref_watch_chat")) return; this.rootNodeInfo = event.getSource(); if (rootNodeInfo == null) return; mReceiveNode = null; mUnpackNode = null; checkNodeInfo(); /* 如果已經(jīng)接收到紅包并且還沒有戳開 */ if (mLuckyMoneyReceived && !mLuckyMoneyPicked && (mReceiveNode != null)) { mMutex = true; AccessibilityNodeInfo cellNode = mReceiveNode; cellNode.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK); mLuckyMoneyReceived = false; mLuckyMoneyPicked = true; L.d("正在打開!"); } /* 如果戳開但還未領(lǐng)取 */ if (mNeedUnpack && (mUnpackNode != null)) { AccessibilityNodeInfo cellNode = mUnpackNode; cellNode.performAction(AccessibilityNodeInfo.ACTION_CLICK); mNeedUnpack = false; L.d("正在領(lǐng)??!"); } if (mNeedBack) { performGlobalAction(GLOBAL_ACTION_BACK); mMutex = false; mNeedBack = false; L.d("正在返回!"); //總次數(shù)和金額統(tǒng)計 if (isGetMoney) { T.showShort(this, "搶到一個紅包: " + gotMoney + "元!"); totalMoney = totalMoney + gotMoney; totalSuccessNum++; myPrefs.totalMoney().put(totalMoney); myPrefs.successNum().put(totalSuccessNum); L.d("totalMoney: " + totalMoney); L.d("totalSuccessNum: " + totalSuccessNum); saveToLog(hongbaoInfo); isGetMoney = false; } } }
檢測監(jiān)聽事件的節(jié)點信息
private void checkNodeInfo() { L.d("checkNodeInfo!"); if (this.rootNodeInfo == null) return; /* 聊天會話窗口,遍歷節(jié)點匹配“領(lǐng)取紅包”和"查看紅包" */ List<AccessibilityNodeInfo> nodes1 = this.findAccessibilityNodeInfosByTexts(this.rootNodeInfo, new String[]{ GET_RED_PACKET, CHECK_RED_PACKET}); if (!nodes1.isEmpty()) { L.d("!nodes1.isEmpty()"); AccessibilityNodeInfo targetNode = nodes1.get(nodes1.size() - 1); if ("android.widget.LinearLayout".equals(targetNode.getParent().getClassName()))//避免被文字干擾導(dǎo)致外掛失效 { if (this.signature.generateSignature(targetNode)) { mLuckyMoneyReceived = true; mReceiveNode = targetNode; L.d("signature:" + this.signature.toString()); } } else { L.d("this is text"); } return; } List<AccessibilityNodeInfo> nodes2 = this.findAccessibilityNodeInfosByTexts(this.rootNodeInfo, new String[]{ "拆紅包"}); if (!nodes2.isEmpty()) { L.d("node2 != null"); for (AccessibilityNodeInfo nodeInfo : nodes2) { if (nodeInfo.getClassName().equals("android.widget.Button")) nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); } } else { /* 戳開紅包,紅包還沒搶完,遍歷節(jié)點匹配“拆紅包” */ AccessibilityNodeInfo node2 = (this.rootNodeInfo.getChildCount() > 3) ? this.rootNodeInfo.getChild(3) : null; if (node2 != null && node2.getClassName().equals("android.widget.Button")) { mUnpackNode = node2; mNeedUnpack = true; isToGetMoney = true; L.d("find red packet!"); return; } } /* 戳開紅包,紅包已被搶完,遍歷節(jié)點匹配“已存入零錢”和“手慢了” */ if (mLuckyMoneyPicked) { List<AccessibilityNodeInfo> nodes3 = this.findAccessibilityNodeInfosByTexts(this.rootNodeInfo, new String[]{ RED_PACKET_PICKED, RED_PACKET_SAVE, RED_PACKET_PICKED2, RED_PACKET_PICKED_DETAIL}); if (!nodes3.isEmpty()) { L.d("!nodes3.isEmpty()"); if (rootNodeInfo.getChildCount() > 1) { L.d("RED_PACKET_PICKED!"); } else { L.d("nodes3.get(0).toString(): " + nodes3.get(0).getText().toString()); if (!nodes3.get(0).getText().toString().equals(RED_PACKET_PICKED_DETAIL)) { AccessibilityNodeInfo targetNode = nodes3.get(nodes3.size() - 1); hongbaoInfo.getInfo(targetNode); if (isToGetMoney) { isGetMoney = true; isToGetMoney = false; gotMoney = hongbaoInfo.getMoney(); L.d("gotMoney: " + gotMoney); } L.d("RED_PACKET_SAVE!"); L.d("hongbaoInfo: " + hongbaoInfo.toString()); } else { L.d("this packet is myself!"); } } mNeedBack = true; mLuckyMoneyPicked = false; } } }
主要通過檢測“領(lǐng)取紅包”等關(guān)鍵文字信息來判斷是否有新紅包
檢測收到紅包時判斷是否"android.widget.LinearLayout
",屏蔽聊天信息中的文字干擾
拆紅包時,由于微信版本可能不同,同時進行兩種判斷,以兼容部分版本
拆完紅包需自動返回,有以下幾種情況:搶到了,手慢了,以及該紅包是自己發(fā)出的紅包
下面是監(jiān)聽聊天列表的代碼:
private boolean watchList(AccessibilityEvent event) { // Not a message if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || event.getSource() == null) return false; List<AccessibilityNodeInfo> nodes = event.getSource().findAccessibilityNodeInfosByText(RED_PACKET_NOTIFICATION); if (!nodes.isEmpty()) { AccessibilityNodeInfo nodeToClick = nodes.get(0); CharSequence contentDescription = nodeToClick.getContentDescription(); if (contentDescription != null && !lastContentDescription.equals(contentDescription)) { nodeToClick.performAction(AccessibilityNodeInfo.ACTION_CLICK); lastContentDescription = contentDescription.toString(); return true; } } return false; }
下面是監(jiān)聽通知信息的代碼:
private boolean watchNotifications(AccessibilityEvent event) { // Not a notification if (event.getEventType() != AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) return false; // Not a hongbao String tip = event.getText().toString(); if (!tip.contains(RED_PACKET_NOTIFICATION)) return true; Parcelable parcelable = event.getParcelableData(); if (parcelable instanceof Notification) { Notification notification = (Notification) parcelable; try { notification.contentIntent.send(); } catch (PendingIntent.CanceledException e) { e.printStackTrace(); } } return true; }
紅包信息的獲取,及日志的存儲
通過獲取節(jié)點的子信息,分別獲得紅包發(fā)送者及搶到的金額、搶紅包時間等信息,建立簡單的表單分別記錄該信息。
@Table(name = "HongbaoInfos") public class HongbaoInfo extends Model { private int month; private int day; private int hour; private int min; private int sec; @Column(name = "sender") public String sender; @Column(name = "money") public String money; @Column(name = "time") public String time; public void getInfo(AccessibilityNodeInfo node) { AccessibilityNodeInfo hongbaoNode = node.getParent(); sender = hongbaoNode.getChild(0).getText().toString(); money = hongbaoNode.getChild(2).getText().toString(); time = getStringTime(); } private String getStringTime() { Calendar c = Calendar.getInstance(); month = c.get(Calendar.MONTH) + 1; day = c.get(Calendar.DAY_OF_MONTH); hour = c.get(Calendar.HOUR_OF_DAY); min = c.get(Calendar.MINUTE); sec = c.get(Calendar.SECOND); return month+"月"+day+"日 "+hour+":"+min+":"+sec; } @Override public String toString() { return "HongbaoInfo [sender=" + sender + ", money=" + money + ", time=" + time + "]"; } public static List<HongbaoInfo> getAll() { return new Select() .from(HongbaoInfo.class) .orderBy("Id ASC") .execute(); } public static void deleteALL() { new Delete().from(HongbaoInfo.class).execute(); } public float getMoney() { return Float.parseFloat(money); } public String getSender() { return sender; } public String getTime() { return time; } }
存儲操作:
private void saveToLog(HongbaoInfo hongbaoInfo) { if (watchedFlags.get("pref_etc_log")) { HongbaoInfo hongbaoInfo1 = new HongbaoInfo(); hongbaoInfo1 = hongbaoInfo; hongbaoInfo1.save(); } else { L.d("log closed!"); } }
總結(jié)
主要的代碼到這里基本結(jié)束,目前在微信最新版上測試ok,尚還存在以下幾個問題:
1.同一個人連續(xù)發(fā)的不能自動搶,因為為了防止重復(fù)點擊做了過濾,同一個人的紅包搶了后不會再次點擊
2.AccessibilityService
開啟時間長后有時會被系統(tǒng)關(guān)掉
結(jié)束語
以上就是本文的全部內(nèi)容了,希望對大家的學(xué)習(xí)和工作能有所幫助。
相關(guān)文章
Android跨進程傳遞大數(shù)據(jù)的方法實現(xiàn)
這篇文章主要介紹了Android跨進程傳遞大數(shù)據(jù)的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03Android中HttpURLConnection類使用介紹
早些時候其實我們都習(xí)慣性使用HttpClient,但是后來Android6.0之后不再支持HttpClient,需要添加Apache的jar才行,所以,就有很多開發(fā)者放棄使用HttpClient了,HttpURLConnection畢竟是標準Java接口(java.net) ,適配性還是很強的2022-12-12