1. 程式人生 > >微信自動回覆和自動搶紅包實現原理(三):自動搶紅包

微信自動回覆和自動搶紅包實現原理(三):自動搶紅包

經過前兩篇文章的閱讀,我相信大家應該對AccessibilityService有一定的瞭解了,是不是已經按捺不住,想自己動手試試?先別急,可以再看完我這篇文章還不遲,相信你另有收穫的。接下來我們來探索一下自動搶紅包的實現原理。

看了我第二篇微信自動回覆文章的朋友應該知道怎麼做了,只是一些操作上不同:
1. 監聽TYPE_NOTIFICATION_STATE_CHANGED事件
2. 根據Notification開啟會話人聊天介面
3. 搜尋紅包控制元件
4. 點選紅包控制元件,開啟紅包,如果紅包已被搶,跳至第6步,否則執行第5步
5. 點選紅包介面的“開”按鈕,搶紅包
6. 返回微信主介面

好吧,我們給測試手機微信發個紅包,先列印log來看看,具體資訊不貼了,直接看結果:

開啟微信的介面
-------------------------------------------------------------
PackageName:com.tencent.mm
Source Class:com.tencent.mm.ui.LauncherUI
Description:null
Event Type(int):32
-------------------------------------------------------------
紅包接收介面(不管紅包還沒搶光還是已被搶光都會開啟這個介面)
-------------------------------------------------------------
PackageName:com.tencent.mm Source Class:com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI Description:null Event Type(int):32 ------------------------------------------------------------- 紅包詳情介面(也就是搶到紅包以後的介面) ------------------------------------------------------------- PackageName:com.tencent.mm Source Class:com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI Description:null Event Type(int):32 -------------------------------------------------------------

經我測試,LauncherUI只有微信在後臺才會觸發,但微信在前臺時也會有Notification,所以有LuckyMoneyReceiveUI、LuckyMoneyDetailUI兩個介面我們已經足夠了

接下來的工作是找到相應的控制元件了。先找紅包這個控制元件,給測試機微信發多幾次紅包,找到每個紅包的相同點。

紅包.png
看到紅色框框沒,“領取紅包”這個控制元件每次都不變了,可以根據其找到其父控制元件,再點選就可以開啟紅包了!

接下來就是紅包介面的“開”按鈕了。很遺憾,因為該按鈕即每文字資訊,也沒特別的子控制元件,沒辦法,只能直接用控制元件的id了(但這種方法不好,因為據瞭解,控制元件的id經常會變,可能就會了防止這類外掛的出現吧,哈哈),下面介紹如何獲取控制元件的id。
1.開啟DDMS,連線手機,開啟一個紅包,進入紅包介面,點選下面按鈕
DDMS.png
2.選中你需要的控制元件,例如這裡我們是要檢視“開”按鈕這控制元件
開紅包.png
3.在右邊就可以檢視控制元件的資訊了,右下方可以檢視id
QQ截圖20160809224615.png

嗯,對的,紅包的控制元件也可以這樣獲取,但我說過了,id是會變的,所以能不用就最好不要用。還有如果有朋友知道不用id獲取“開”按鈕的話,請告訴我一聲哈。

好了,所有難點都解決了,接下來只要寫程式碼處理下邏輯就好了,直接上程式碼吧。

**
 * 自動搶紅包服務
 */
public class AutoOpenLuckyMoneyService extends AccessibilityService{

    private static final String TAG = AutoOpenLuckyMoneyService.class.getSimpleName();

    private static final int MSG_BACK_HOME = 0;
    private static final int MSG_BACK_ONCE = 1;
    boolean hasNotify = false;
    boolean hasLuckyMoney = true;


    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        int eventType = event.getEventType(); // 事件型別
        switch (eventType) {
            case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: // 通知欄事件
                Log.i(TAG, "TYPE_NOTIFICATION_STATE_CHANGED");
                if(PhoneController.isLockScreen(this)) { // 鎖屏
                    PhoneController.wakeAndUnlockScreen(this);   // 喚醒點亮螢幕
                }

                openAppByNotification(event); // 開啟微信
                hasNotify = true;
                break;


            default:
                Log.i(TAG, "DEFAULT");
                if(hasNotify) {
                    AccessibilityNodeInfo rootNode = getRootInActiveWindow();
                    clickLuckyMoney(rootNode); // 點選紅包

                    String className = event.getClassName().toString();
                    if (className.equals(UI.LUCKY_MONEY_RECEIVE_UI)) { //紅包接收介面
                        if(!openLuckyMoney()) { // 如果紅包被搶光了,就返回主介面
                            backToHome();
                            hasNotify = false;
                        }
                        hasLuckyMoney = true;
                    } else if (className.equals(UI.LUCKY_MONEY_DETAIL_UI)) { // 搶到紅包
                        backToHome();
                        hasNotify = false;
                        hasLuckyMoney = true;
                    } else { // 處理沒紅包的情況,直接返回主介面
                        if(!hasLuckyMoney) {
                            handler.sendEmptyMessage(MSG_BACK_ONCE);
                            hasLuckyMoney = true;   // 防止後退多次
                        }
                    }
                }
                break;
        }
    }

    @Override
    public void onInterrupt() {
    }


    /**
     * 開啟微信
     * @param event 事件
     */
    private void openAppByNotification(AccessibilityEvent event) {
        if (event.getParcelableData() != null  && event.getParcelableData() instanceof Notification) {
            Notification notification = (Notification) event.getParcelableData();
            try {
                PendingIntent pendingIntent = notification.contentIntent;
                pendingIntent.send();
            } catch (PendingIntent.CanceledException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 搜尋並點選紅包
     */
    private void clickLuckyMoney(AccessibilityNodeInfo rootNode) {
        if(rootNode != null) {
            int count = rootNode.getChildCount();
            for (int i = count - 1; i >= 0; i--) {  // 倒序查詢最新的紅包
                AccessibilityNodeInfo node = rootNode.getChild(i);
                if (node == null)
                    continue;

                CharSequence text = node.getText();
                if (text != null && text.toString().equals("領取紅包")) {
                    AccessibilityNodeInfo parent = node.getParent();
                    while (parent != null) {
                        if (parent.isClickable()) {
                            parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                            break;
                        }
                        parent = parent.getParent();
                    }
                }

                clickLuckyMoney(node);
            }
        }
    }

    /**
     * 開啟紅包
     */
    private boolean openLuckyMoney() {
        AccessibilityNodeInfo rootNode = getRootInActiveWindow();
        if(rootNode != null) {
            List<AccessibilityNodeInfo> nodes =
                    rootNode.findAccessibilityNodeInfosByViewId(UI.OPEN_LUCKY_MONEY_BUTTON_ID);
            for(AccessibilityNodeInfo node : nodes) {
                if(node.isClickable()) {
                    Log.i(TAG, "open LuckyMoney");
                    node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                    return true;
                }
            }
        }

        return false;
    }

    private void backToHome() {
        if(handler.hasMessages(MSG_BACK_HOME)) {
            handler.removeMessages(MSG_BACK_HOME);
        }
        handler.sendEmptyMessage(MSG_BACK_HOME);
    }

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if(msg.what == MSG_BACK_HOME) {
                performGlobalAction(GLOBAL_ACTION_BACK);
                postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        performGlobalAction(GLOBAL_ACTION_BACK);
                        hasLuckyMoney = false;
                    }
                }, 1500);
            } else if(msg.what == MSG_BACK_ONCE) {
                postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        Log.i(TAG, "click back");
                        performGlobalAction(GLOBAL_ACTION_BACK);
                        hasLuckyMoney = false;
                        hasNotify = false;
                    }
                }, 1500);
            }
        }
    };
}

ok,到這裡就全部講完了,小夥伴們可以自己去實現更多更有趣、更新奇的功能了。這裡我只是作為技術探索,容我再囉嗦兩點:
1. 朋友是很重要的,有空的話還是好好回覆吧
2. 紅包只是一種噱頭,一種娛樂方式,別當作謀財之道喔

原始碼下載