1. 程式人生 > >一步一步實現微信搶紅包

一步一步實現微信搶紅包

  聖誕過後,又到了搶紅包的季節。各個公司的年會將逐漸展開,各個紅包群就熱鬧了起來。為了應對領導在群裡時不時的一個紅包,寫一個搶紅包的應用迫在眉睫了。之前由於沒有自動搶紅包錯失了100RMB+的紅包啊!
  先來整理下思路。要實現搶紅包,那麼就要在紅包來的時候去開啟微信,執行點選的動作。被點選的控制元件肯定是帶有紅包關鍵字的。開啟紅包後,還需要去點選一下開啟。
  為了實現上面的一系列步驟,方法有兩種。
第一從framwork進行修改。這種方式適合於自制rom。如手機廠商多采用這種方法。有個同事就通過這方法實現了紅包功能。
第二就只能通過google 提供了一個輔助服務類了。該類可以監聽通知、監聽視窗變化,模擬點選等功能。該文就採用輔助服務類。

STEP1 輔助服務類的使用。

通過配置manifest檔案就可以使用輔助服務了。

        <service
            android:name=".RedService"
            android:enabled="true"
            android:exported="true"
            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/red_service_config" /> </service>

該服務可以進行配置,配置如下:

<
accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged"//監聽型別 訊息通知和視窗變化 android:accessibilityFeedbackType="feedbackGeneric"//反饋方式 android:accessibilityFlags="" android:canRetrieveWindowContent="true"//是否允許我們的程式讀取視窗中的節點和內容 android:description="@string/accessibility_description" android:notificationTimeout="100" android:packageNames="com.tencent.mm" />//監聽的包名

配置好後,該服務就可以使用了。可以通過一個按鈕引導使用者去開啟這個服務

 Intent intent = new Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS);
                startActivity(intent);
                Log.d(TAG,"開啟系統設定");

因為這個服務在系統設定裡,所以通過點選去開啟系統設定。然後就可以開啟次服務。
接下來就是重點了。
首先需要去繼承一個AccessibilityService 。

public class RedService extends AccessibilityService {

    public void onAccessibilityEvent(AccessibilityEvent event){
    ......
    }
    public void onInterrupt() {

    }
}

需要去重寫他的兩個方法。onAccessibilityEvent方法用來接收服務監聽的事件,此處為通知和視窗變化。
現在先來看看 通知來的時候如何處理:

public void onAccessibilityEvent(AccessibilityEvent event){
        final int eventType = event.getEventType();
        Log.d(TAG,"event = "+event);
        //notifycation
        if (eventType == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {// 監聽到通知
            List<CharSequence> texts = event.getText();

            //解析通知內容
            if (!texts.isEmpty()) {
                for (CharSequence t : texts) {
                //如果有WECHAT_KEYNAME = "[微信紅包]"
                    if (text.contains(WECHAT_KEYNAME)) {
                        if(!isopen) {
                        //根據通知開啟應用
                            openNotification(event);
                            setOpenredbagState(true);
                        }

                        break;
                    }
                }
            }
        }
 private void openNotification(AccessibilityEvent event) {
        if (event.getParcelableData() == null || !(event.getParcelableData() instanceof Notification)) {
            return;
        }
        //通過通知去開啟微信
        Notification notification = (Notification) event.getParcelableData();
        PendingIntent pendingIntent = notification.contentIntent;
        try {
            pendingIntent.send();
        } catch (PendingIntent.CanceledException e) {
            e.printStackTrace();
        }
    }

在開啟微信後,會發生視窗狀態變化。這個時候該事件將會被監聽。
此處要說的是微信的聊天介面和聊天列表的介面其實是在一個activity。都為
com.tencent.mm.ui.LauncherUI。
而且聊天介面是整個Activity contentView的父VIEW。通過解析頁面可以輕易的發現。
再新開啟的頁面上,通過輔助類提供的方法對節點進行遍歷。

private void clickRedBag() {

        //獲取跟節點
        AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
        if (nodeInfo == null) {
            Log.w(TAG, "rootWindow為空");
            return;
        }
        //獲取該節點下有 領取紅包 關鍵字的節點
        List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("領取紅包");
        if (list.isEmpty()) {
            list = nodeInfo.findAccessibilityNodeInfosByText(WECHAT_KEYNAME);
            for (AccessibilityNodeInfo n : list) {
                Log.i(TAG, "-->微信紅包:" + n);
                n.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                break;
            }
        } else {
            //點選紅包
            for (int i = list.size() - 1; i >= 0; i--) {
                AccessibilityNodeInfo parent = list.get(i).getParent();
                Log.i(TAG, "-->點選紅包:" + parent);
                if (parent != null) {
                    parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                    break;
                }
            }
        }
    }

通過 AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); 獲取跟節點,同時也可以通過getchirld 方法來看看他的子節點。列印如下:

nodeInfo = android.view.accessibility.AccessibilityNodeInfo@8000744e;   packageName: com.tencent.mm; className: android.widget.FrameLayout; text: null; error: null; maxTextLength: -1; contentDescription: 當前所在頁面,與走向通往康莊的大道上(4)的聊天; 

nodeInfo.getChild(i) = android.view.accessibility.AccessibilityNodeInfo@8000780f; boundsInParent: Rect(0, 0 - 1080, 1398); boundsInScreen: Rect(0, 216 - 1080, 1614); packageName: com.tencent.mm; className: com.tencent.mm.ui.mogic.WxViewPager; 

......

在android.widget.FrameLayout 下一共有8個子Node,不一一列舉。上面列舉了一個叫com.tencent.mm.ui.mogic.WxViewPager的view這個view 就是我們的聊天列表。
其餘7個分別是搜尋、更多、微信、通訊錄等。
 &emsp;當我們在聊天頁面點選紅包後,會彈出一個為開啟的紅包或者可能提示紅包已到期。重點說說正常情況下紅彈出紅包的情況。彈出紅包將會觸發一個event,該event為視窗變化,由之前的視窗變為了com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI。
這時候,我們可以繼續利用對節點的遍歷來實現點選的動作。程式碼如下:

 AccessibilityNodeInfo openRedbagbtn = findFuctionButtonNode(nodeInfo,OPENBUTTON);
        if(openRedbagbtn==null) {
            AccessibilityNodeInfo closeButton = findFuctionButtonNode(nodeInfo,CLOSEBUTTON);
            return;
        }

        openRedbagbtn.performAction(AccessibilityNodeInfo.ACTION_CLICK);
        setOpenredbagState(false);
......

 private AccessibilityNodeInfo findFuctionButtonNode(AccessibilityNodeInfo nodeInfo,int fun) {
        AccessibilityNodeInfo FuctionButton;
        int chirdCount = nodeInfo.getChildCount();
        switch (fun) {
            case OPENBUTTON:
                for(int i=0;i<chirdCount;i++) {
                //因為開啟的頁面只有一個開啟按鈕,所以根據button來找就可。                  if(nodeInfo.getChild(i).getClassName().equals("android.widget.Button"))
                        return  nodeInfo.getChild(i);
                }
                break;
            case CLOSEBUTTON:
                ......
            default:break;
        }

        return null;
    }

至此,基本自動搶紅包的功能都完成了。為了保證程式的健壯性,還需要對搶紅包的失敗的情況進行處理。
具體程式碼已傳GIT,異常處理可能有不全的地方,待感冒好了再改了 藍瘦。
git:https://github.com/everyhappy/RedRUsh