Android微信新版全自動搶紅包助手

前言

新的一年又到了,又到了拼手速和網速的時候了,網速是硬件條件,沒有辦法了,不過手速這種東西,沒有還不能創造麼,哈哈。其實以前網上有不少老鐵已經分享過相似的插件的實現方式,可是微信其實自己也是在作對第三方插件的規避操做,因此,微信的每個新版本都會修改相同控件的id,因此以前的不少插件都不能再使用了,並且以前的有些判斷方法也不能再適用新版本的微信,因此我研究了幾天,新版全自動微信搶紅包助手就應運而生了,老規矩,給你們看下效果。php

電點紅包助手

主要功能介紹

  • 具備監聽通知欄紅包消息的功能,發現紅包自動跳轉頁面搶紅包
  • 聊天頁面實時監控私信和羣紅包
  • 一旦發現紅包,自動進入聊天頁面,從下往上依次遍歷未搶過的紅包,點擊進入搶紅包界面
  • 自動點擊「開」按鈕,完成自動收紅包動做
  • 聊天頁面紅包搶完以後,自動回到聊天列表頁面,繼續監聽下一個紅包的到來,作到紅包遺漏少,成功率高。

技能點介紹

1、核心中的核心(無障礙服務的使用)

全自動搶紅包無非也就是寫個邏輯代替你手動點擊的過程,要實現這個功能,就要用到Android提供的無障礙服務(AccessibilityService)的功能。輔助功能能夠獲得系統級別的事件和服務,經過這些事件和服務,咱們就能監控微信的紅包消息,不過第三方應用的輔助功能都須要手動開啓。java

關於AccessibilityService的使用,簡單的介紹下,不作過多的介紹,簡單的分紅三部:node

  • 第一步:自定義一個服務繼承自AccessibilityService,重寫對應的方法
package com.cretin.www.redpacketplugin.services;

import android.accessibilityservice.AccessibilityService;
import android.annotation.TargetApi;
import android.os.Build;
import android.view.accessibility.AccessibilityEvent;

/** * Created by cretin on 2018/2/9. */
public class RedPackageService extends AccessibilityService {

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    protected void onServiceConnected() {
        //系統成功鏈接到輔助功能服務時調用
        super.onServiceConnected();
    }

    @TargetApi( Build.VERSION_CODES.JELLY_BEAN_MR2 )
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        //當系統檢測到與Accessibility服務指定的事件過濾參數
        // 匹配的AccessibilityEvent時調用
    }

    @Override
    public void onInterrupt() {
        //當系統想要中斷服務提供的反饋時調用
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //當系統即將關閉輔助功能服務時調用
    }

}


複製代碼
  • 第二步:給輔助服務書寫配置文件
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeAllMask" android:accessibilityFeedbackType="feedbackSpoken" android:accessibilityFlags="flagDefault" android:canRetrieveWindowContent="true" android:description="@string/accessibility_service_description" android:notificationTimeout="100" android:packageNames="com.tencent.mm" android:settingsActivity="com.cretin.www.redpacketplugin.android.accessibility.ServiceSettingsActivity"/>
複製代碼

對屬性作一個簡單的解釋 accessibilityEventTypes:響應那種類型的事件 accessibilityFeedbackType:設置回饋給用戶的方式,有語音播出和振動 notificationTimeout:響應時間 packageNames:指定響應哪一個應用的事件。這裏填的是微信的包名,若是不填則是響應全部的應用事件 description:輔助服務的描述信息,會顯示在無障礙服務的描述那裏。android

  • 第三步:註冊服務
<service android:name=".services.PackageAccessibilityService" android:description="@string/accessibility_service_description" android:enabled="true" android:exported="true" android:label="@string/accessibility_service_label" 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>
複製代碼

屬性的簡單說明瀏覽器

//輔助功能的名稱
android:label="@string/accessibility_service_label"   
//此處必須聲明一次權限
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" 
//指定配置文件的名字和位置
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config"
複製代碼

作好上面的準備工做後,咱們就能夠在onAccessibilityEvent(AccessibilityEvent event)方法中寫咱們具體的邏輯了。微信

2、針對通知欄事件非通知欄事件分開處理

看過以前老鐵的處理方式是對AccessibilityEvent中getEventType來判斷是全部類型,通過實驗這種方式是不可靠的,通過屢次測試,最終我以爲用getEventType只判斷是不是通知欄事件比較靠譜。app

if ( event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED ) {
      //通知欄事件
} else {
      //非通知欄事件 處理其餘事件
}
複製代碼

3、對非通知欄事件作細分處理

由於通知欄事件比較簡單,直接點擊通知欄就行了,點擊通知欄後會本身跳轉到聊天頁面,剩下的事情也是交給對非通知欄事件來處理。ide

那麼如今須要考慮的事情有如下幾點: 第一:如何獲取咱們但願處理的控件並操控它。 第二:如何判斷當前在哪一個頁面,是聊天列表頁面,是聊天頁面,是打開紅包的頁面仍是打開紅包後的詳情頁面。 第三:在不一樣的頁面咱們須要作什麼事情,點擊哪一個控件。工具


3.一、如何獲取咱們但願處理的控件並操控它。

獲取一個有文本的控件有兩種方式,一種是根據文本找控件,一種是根據id找控件,對於沒有文本的控件,就使用id找控件。找到控件以後能夠對控件主動觸發必定的事件,好比最經常使用的點擊事件。測試

//獲取整個窗口根節點
AccessibilityNodeInfo nodeInfo = getService().getRootInActiveWindow();
//根據id獲取全部使用這個id的控件節點集合
List<AccessibilityNodeInfo> idNodes =
                        nodeInfo.findAccessibilityNodeInfosByViewId(VIEW_ID_RECEIVE_BTN_OPEN);
//根據內容獲取全部這個有這個文本的控件節點集合
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(TEXT_LINGQUHONGBAO);
//對控件主動觸發事件(這裏觸發的是點擊事件,其餘事件類型可自行研究 AccessibilityNodeInfo)
if(!idNodes.isEmpty()){
    idNodes.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
複製代碼

?問題:那麼圖和獲取控件的id呢? 找到uiautomatorviewer後點擊運行。

找到uiautomatorviewer
按以下操做就能夠獲取到控件id(記得插上手機或開啓模擬器,手機或模擬器開啓調試模式)
獲取控件id


3.二、如何判斷當前在哪一個頁面

就目前來看,咱們須要區分聊天列表頁面(就是微信的首頁),聊天頁面(包括私信和羣聊天),點擊紅包後的紅包頁面(這裏包括兩種狀況,一種是紅包尚未被別人搶,點「開」按鈕會進入到詳情頁面,還有一種是紅包被別人搶了,此時點擊「開」出現的是「手慢了,紅包派完了」的頁面)和開紅包後的詳情頁面。

3.2.一、判斷聊天列表頁面

看過以前老鐵判斷首頁的方式是判斷className,由於回到首頁的時候className是com.tencent.mm.ui.LauncherUI(這個值也不是永恆不變的,要根據微信版原本),可是通過屢次測試,當不在微信首頁,在其餘頁面的時候,也會觸發這個className,因此不靠譜。

後來通過屢次測試,發現獲取首頁listview的item列表項的id,這個id只會在首頁聊天列表頁面出現,因此我就按照這個方式來肯定當前頁面是否是首頁。

List<AccessibilityNodeInfo> listItemNodes =
                            nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/apr");
if ( listItemNodes.isEmpty() ) {
       //反正不是在首頁 不理會
       return;
} else {
      //在首頁
}
複製代碼

listview的列表項id

3.2.二、判斷聊天頁面

其實判斷在哪一個頁面,最主要的就是找其餘頁面沒有的特徵控件,好比在聊天頁面中,右下角那個「+」按鈕纔是最獨特的,因此能夠根據是否有這個按鈕來判斷是不是聊天頁面。可是這個只能判斷是不是聊天頁面,不能判斷是私信頁面仍是羣聊頁面。在對比了私信和羣聊的頁面以後,沒有找到特別穩的方式來判斷聊天類型,只能根據標題來判斷,羣聊的標題後面必定會有一個括號,括號裏面是羣成員人數。因此咱們只須要來判斷標題最後是否有一個括號裏面是數字,固然這種方式不是特別準,不過夠用了,通常用戶也不會這個起暱稱,萬一這樣起了也只是判斷類型出錯,也不會影響搶紅包的功能。

List<AccessibilityNodeInfo> chatNodes =
                        nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/aak");
 if ( chatNodes.isEmpty() ) {
        //不在聊天頁面 很差說在哪兒
}else{
        //在聊天頁面
        List<AccessibilityNodeInfo> titleNodes =
                            nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ha");
        if ( titleNodes.isEmpty() ) {
                 //沒法判斷類型
        } else {
                //判斷標題最後是不是一個括號,括號中是數字,固然最好是用正則
                String title = titleNodes.get(0).getText().toString();
                        if ( !TextUtils.isEmpty(title) ) {
                            if ( title.contains("(") ) {
                                int indexLeft = title.lastIndexOf("(");
                                String end = title.substring(indexLeft);
                                end = end.substring(1, end.length() - 1);
                                try {
                                    Integer.parseInt(end);
                                    //羣聊
                                } catch ( Exception e ) {
                                    //私聊
                                }
                            } else {
                                //私聊 默認私聊
                            }
                        }
        }
}
複製代碼

添加按鈕
標題

3.2.三、判斷打開紅包的頁面

還記得以前提到過的className嗎,打開紅包和紅包詳情頁面就能夠用這個了,別問我爲何知道啥時候用className,啥時候本身判斷控件,這都是幾十次調試和實驗獲得的。%>_<% 通過實現,咱們發現了,彈出紅包頁面的className是com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI,因此咱們只須要判斷當前的className是這個就能夠判斷出當前是打開紅包的頁面。可是還有一種狀況就是打開紅包後有多是紅包已經被別人搶完了,因此此時會顯示「手慢了,紅包派完了」頁面,這個頁面的className也是這個,因此單單靠這個是不能準確判斷的。咱們依然須要找這兩個頁面的特徵控件。

在「開」紅包頁面,特徵元素是「開」按鈕,在「手慢了,紅包派完了」頁面,特徵元素是「手慢了,紅包派完了」所在的控件。

String className = event.getClassName().toString();
        if ( "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI".equals(className) ) {
                //點中了紅包 有兩種操做 一種是點開紅包 一種是手慢了
                /** * 一種是點開紅包 */
                //獲取開按鈕
                List<AccessibilityNodeInfo> kaiNodes =
                        nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/c2i");
                //獲取 手慢了 提示語句的控件
                List<AccessibilityNodeInfo> slowNodes =
                        nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/c2h");
                //獲取關閉按鈕
                List<AccessibilityNodeInfo> closeNodes =
                        nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/c07");
                if ( !kaiNodes.isEmpty() ) {
                    //獲取到開按鈕 點擊此按鈕 
                    NotifyHelper.playEffect(getContext(), getConfig());
                    AccessibilityHelper.performClick(kaiNodes.get(0));
                } else {
                    if ( !slowNodes.isEmpty() && !closeNodes.isEmpty() )
                        //手慢了 提示語句的控件 關閉對話框
                        AccessibilityHelper.performClick(closeNodes.get(0));
                }
            } 
複製代碼

開紅包

手慢了

返回按鈕

3.2.四、判斷打開紅包後的詳情頁面

這個頁面是最簡單的,根據className來判斷,若是是這個頁面,直接點擊返回按鈕就行了。className值爲com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI。

String className = event.getClassName().toString();
     if ( "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI".equals(className) ) {
                //拆完紅包後看詳細的紀錄界面 這裏退出就好
                //獲取關閉按鈕
                List<AccessibilityNodeInfo> closeNodes =
                        nodeInfo.findAccessibilityNodeInfosByViewId(VIEW_ID_DETAIL_CLOSE);
                if ( !closeNodes.isEmpty() ) {
                    //關掉
                    AccessibilityHelper.performClick(closeNodes.get(0));
                    return;
                } else {
                    AccessibilityHelper.performBack(getService());
                }
            } 
複製代碼

3.三、判斷打開紅包的頁面在不一樣的頁面咱們須要作什麼事情,點擊哪一個控件。

其實上面已經捎帶分析了一些。咱們從首頁開始分析

  • 首頁 這個頁面只須要作一件事,就是監聽列表信息的變化,當聊天列表中的消息出現了」[微信紅包]「字樣,說明有人發紅包,那麼此時點擊那條消息,進入到聊天頁面。可是這裏須要注意一點,若是你沒有搶到紅包,紅包被別人搶完了,那麼你的聊天列表依然顯示的是"[微信紅包]",若是不處理這種狀況,你就會進入到一種死循環的狀況,首頁說有紅包,跳轉聊天頁面,聊天頁面說沒有,返回來,首頁又說有......但其實,這些紅包早就已經不能搶了,因此這樣的消息就須要屏蔽掉,不能跳轉頁面,那麼有什麼好的辦法嗎?答案是並無。 那怎麼辦?咱們只能經過消息的未讀數來判斷,若是當前列表項的未讀數不爲0,並且聊天內容中有」[微信紅包]「字樣是,才跳轉頁面,這樣就能夠防止上面的狀況發生。
//獲取首頁的listview 的 item 的 列表
                    List<AccessibilityNodeInfo> listItemNodes =
                            nodeInfo.findAccessibilityNodeInfosByViewId(VIEW_ID_HOME_LV_ITEM);
                    if ( listItemNodes.isEmpty() ) {
                        //反正不是在首頁 不理會
                        return;
                    } else {
                        //在首頁
                        List<AccessibilityNodeInfo> nodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/apr");
                        if ( nodes != null ) {
                            for ( AccessibilityNodeInfo node :
                                    nodes ) {
                                if ( node.getText().toString().contains("[微信紅包]") ) {
                                    //還要判斷是否有未讀消息
                                    AccessibilityNodeInfo parent = node.getParent();
                                    if ( parent != null ) {
                                        List<AccessibilityNodeInfo> numsNodes =
                                                parent.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/iu");
                                        if ( !numsNodes.isEmpty() ) {
                                            CharSequence text = numsNodes.get(0).getText();
                                            if ( text != null ) {
                                                if ( Integer.parseInt(text.toString()) != 0 ) {
                                                    //此時才能跳轉
                                                    AccessibilityHelper.performClick(parent);
                                                }
                                            }
                                        }
                                    }
                                    return;
                                }
                            }
                        }
                    }
複製代碼
  • 聊天列表 在這個頁面中,咱們須要判斷遍歷當前的listview的item項,找出當前頁面中全部含有「領取紅包」的listview的列表項,並判斷當前的這個item是否是紅包,若是是紅包,則模擬用戶點擊點開這個頁面,進到收紅包的邏輯中。這裏在判斷是否是紅包的過程當中,咱們使用雙重保險的方式,先找到"領取紅包"的控件節點,再查看該節點對應的id是否是正確的,二者同時知足才能保證他是紅包。防止用戶發送純文字「領取紅包」來影響程序的判斷。
//在聊天頁面
                    List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("領取紅包");
                    if ( list == null )
                        return;
                    if ( list.isEmpty() ) {
                        //沒有 直接返回
                        List<AccessibilityNodeInfo> backNodes =
                                    nodeInfo.findAccessibilityNodeInfosByViewId(VIEW_ID_CHATTING_TV_BACK);
                        if ( !backNodes.isEmpty() ) {
                                AccessibilityHelper.performClick(backNodes.get(0));
                         }
                    } else {
                        //有 可是要檢查是否是紅包
                        for ( int i = list.size() - 1; i >= 0; i-- ) {
                            AccessibilityNodeInfo node = list.get(i);
                            AccessibilityNodeInfo parent = node.getParent();
                            if ( parent != null ) {
                                List<AccessibilityNodeInfo> wxhbNodes =
                                        parent.findAccessibilityNodeInfosByViewId(VIEW_ID_HOME_LV_ITEM_LABEL_WXHB);
                                if ( !wxhbNodes.isEmpty() ) {
                                    if ( TEXT_LV_ITEM_TIPS.equals(wxhbNodes.get(0).getText()) ) {
                                        //是的 沒錯 領取紅包
                                        AccessibilityHelper.performClick(node);
                                        return;
                                    }
                                }
                            }
                        }
                    }
複製代碼
  • 搶紅包頁面和詳情頁面的處理在上面已經說明了,在此就再也不贅述了。

結語

基本上有了上面這些踩坑的經歷,一個紅包助手的架子基本也就齊全了。本身再加一些邏輯上的判斷和功能上的私人訂製,一個過年的工具就誕生了。

因爲微信每一個版本對於同一個控件的id都會作改變,因此,咱們須要對不一樣的微信版本作適配,不然在使用過程當中可能會出現意想不到的問題。如下是我整理的微信不一樣版本的咱們所須要的控件的id的彙總,您看着是密密麻麻,我整理起來也是很辛苦的,小當心意,祝你們新年快樂。

微信版本 微信版本號 打開紅包的CLASSNAME 點開紅包的開按鈕ID 紅包詳情的CLASSNAME 首頁列表未讀數ID 手慢了ID 聊天標題ID 聊天右下角添加ID 首頁聊天內容ID 點開紅包的返回按鈕ID 聊天頁面返回按鈕ID 紅包詳情返回按鈕ID
v6.6.2 1240 com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI com.tencent.mm:id/c4j com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI com.tencent.mm:id/j4 com.tencent.mm:id/c4i com.tencent.mm:id/hj com.tencent.mm:id/aag com.tencent.mm:id/apt com.tencent.mm:id/c28 com.tencent.mm:id/hi com.tencent.mm:id/hy
v6.6.1 1220 com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI com.tencent.mm:id/c2i com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI com.tencent.mm:id/iu com.tencent.mm:id/c2h com.tencent.mm:id/ha com.tencent.mm:id/aak com.tencent.mm:id/apv com.tencent.mm:id/c07 com.tencent.mm:id/h_ com.tencent.mm:id/hp
v6.6.0 1200 com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI com.tencent.mm:id/c22 com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI com.tencent.mm:id/iu com.tencent.mm:id/c21 com.tencent.mm:id/ha com.tencent.mm:id/aa4 com.tencent.mm:id/apf com.tencent.mm:id/bzq com.tencent.mm:id/h_ com.tencent.mm:id/hp
v6.5.23 1180 com.tencent.mm.plugin.luckymoney.ui.En_fba4b94f com.tencent.mm:id/bx4 com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI com.tencent.mm:id/io com.tencent.mm:id/bx3 com.tencent.mm:id/h5 com.tencent.mm:id/aa6 com.tencent.mm:id/aol com.tencent.mm:id/bus com.tencent.mm:id/h4 com.tencent.mm:id/hj
v6.5.22 1160 com.tencent.mm.plugin.luckymoney.ui.En_fba4b94f com.tencent.mm:id/bwn com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI com.tencent.mm:id/io com.tencent.mm:id/bwm com.tencent.mm:id/h5 com.tencent.mm:id/aa6 com.tencent.mm:id/aol com.tencent.mm:id/bub com.tencent.mm:id/h4 com.tencent.mm:id/hj
v6.5.19 1140 com.tencent.mm.plugin.luckymoney.ui.En_fba4b94f com.tencent.mm:id/bv8 com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI com.tencent.mm:id/il com.tencent.mm:id/bv7 com.tencent.mm:id/h2 com.tencent.mm:id/a9t com.tencent.mm:id/an9 com.tencent.mm:id/bsv com.tencent.mm:id/h1 com.tencent.mm:id/hg
v6.5.16 1120 com.tencent.mm.plugin.luckymoney.ui.En_fba4b94f com.tencent.mm:id/brt com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI com.tencent.mm:id/il com.tencent.mm:id/brs com.tencent.mm:id/h2 com.tencent.mm:id/a76 com.tencent.mm:id/ak3 com.tencent.mm:id/bph com.tencent.mm:id/h1 com.tencent.mm:id/hg
v6.5.13 1100 com.tencent.mm.plugin.luckymoney.ui.En_fba4b94f com.tencent.mm:id/bp6 com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI com.tencent.mm:id/ie com.tencent.mm:id/bp5 com.tencent.mm:id/gz com.tencent.mm:id/a6l com.tencent.mm:id/aje com.tencent.mm:id/bmu com.tencent.mm:id/gy com.tencent.mm:id/hd

源碼相關

最上面提供的動態圖是我給周圍朋友作的一個全自動紅包插件,因爲項目中有後臺接口,是爲了動態加載一些配置文件,讓app體驗更好,省得每次微信有新版本都要更新app,並且加了不少其餘方面的判斷,比較複雜,因此源碼就再也不放出來了。相信通過上面的分析,本身擼一個也不困難。

最後也爲我上面的app打一個小廣告,名稱:電點紅包助手,適配了大部分機型,我相信在過年的這段時間,有不少人也仍是須要這麼一款app的,畢竟在家要多陪陪家人,老玩手機很差,但又不想錯過幾百萬的紅包,那麼這個助手正適合你,哈哈。

下面是app的下載連接,微信掃一掃或者直接用瀏覽器掃一掃都行,不過用微信掃一掃記得點擊右上角的在瀏覽器中打開才能下載,畢竟這種東西任何的應用市場都是不讓上傳的。

最後,祝你們新的一年,賺大錢。

我叫Cretin,一個可愛的小男孩。

電點紅包助手下載地址
相關文章
相關標籤/搜索