1. 程式人生 > >300行程式碼實現 微信或QQ 搶紅包功能

300行程式碼實現 微信或QQ 搶紅包功能

每次看到別人發紅包,但是沒有搶到,這就很尷尬了。於是想著做一個紅包外掛,嘿嘿,再也不怕我反應遲鈍了。
首先,想到做這個功能時,我聯想到的是這個是不是lua寫出來的指令碼呢?但是條件不成立,因為可以不需要ROOT的。那麼問題就來了,這玩意怎麼實現?網上查了查,這下子明白了,Android平臺為了給一些使用手機不是很方便的人提供了一個解決方案,被叫做AccessibilityService。這是一個輔助類,用於監聽手機的焦點,視窗變化,按鈕點選等等。
接下來,分析了原始碼上的綠色字型,大致意思如下:

無障礙服務的開發需要擴充套件這個類並實現其抽象方法。

生命週期
由系統和遵循已建立的服務生命週期

當用戶在裝置設定中或在裝置設定期間關閉時,無障礙服務將停止

宣告
必須完成2
件事: 1.指定處理”android.accessibilityservice.AccessibilityService2.請求android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE許可權使有且僅有系統可以繫結。 兩種配置方法: 1.在manifest中設定; 2.在程式碼類中動態設定。 輔助功能可以配置為特定型別的輔助功能事件,僅監聽特定的包,在給定的時間內做指定的事。

繼續往下看程式碼,Callbacks裡有onAccessibilityEvent、onInterrupt和onServiceConnected是很重要的。

關於onAccessibilityEvent,原始碼的解釋:

  The new event. This event is owned by the caller and cannot be used after this method returns. Services wishing to use the event after this method returns should  make a copy.

意思大致是我們需要繼承AccessibilityService,然後實現onAccessibilityEvent方法。

關於onInterrupt,原始碼的解釋:

Callback for interrupting the accessibility feedback.

這個是用功能中斷時回撥。

關於onServiceConnected,原始碼的解釋:

This method is a part of the {@link AccessibilityService} lifecycle and is called after the system has successfully bound to the service. If is convenient to use this method for setting the {@link AccessibilityServiceInfo}.

系統成功繫結後回撥,前面提到的程式碼配置也是在這個方法內。

好了,原始碼看到這裡。我們需要的幾個方法都瞭解了,接下來直接來看程式碼。首先建立工程,然後建立一個RBAccessibilityService繼承自AccessibilityService。實現onAccessibilityEvent、onInterrupt和onServiceConnected方法。然後配置manifest

<service
               android:name=".service.RBAccessibilityService"
               android:exported="true"
               android:enabled="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/accessibilityservice_set">
               </meta-data>
          </service>

我們需要注意的是meta-data裡的resource,這裡需要在res建立一個xml資料夾,寫入如下程式碼:

<accessibility-service
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:accessibilityEventTypes="typeAllMask"
     android:accessibilityFeedbackType="feedbackSpoken"
     android:accessibilityFlags="flagDefault"
     android:canRequestTouchExplorationMode="true"
     android:canRetrieveWindowContent="true"
     android:notificationTimeout="50"
       android:description="@string/accessibility_description"
     android:packageNames="com.tencent.mobileqq,com.tencent.mm"/>

packageNames裡的是需要監聽的包名,需逗號隔開。配置完成,繼續實現具體程式碼,完善RBAccessibilityService的onAccessibilityEvent:

@Override
     public void onAccessibilityEvent(AccessibilityEvent event) {
          String className = event.getClassName().toString();
          switch (event.getEventType()) {
               //通知欄
               case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
                    List<CharSequence> texts = event.getText();
                    if (!texts.isEmpty()) {
                         for (CharSequence text : texts) {
                              String content = text.toString();
                              /**
                               * 微信的notification
                               */
                              if (content.contains(WECHAT_RB_TEXT)) {
                                   //賦值給判斷值
                                   QQ_OR_WECHAT = THISWECHAT;
                                   RBnotification(event);
                              }
                              /**
                               * QQ的notification
                               */
                              if (content.contains(QQ_RB_TXT)) {
                                   //賦值給判斷值
                                   QQ_OR_WECHAT = THISQQ;
                                   RBnotification(event);
                              }
                         }
                    }
                    break;
               case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
                    //判斷是QQ或者微信傳送來的紅包
                    if (QQ_OR_WECHAT == THISQQ) {
                         openQQHongBao(event);
                    } else {
                         openWeChatHongBao(event);
                    }
                    break;
               case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
                    break;
               default:
                    break;
          }
     }

看明白了麼?這個方法內TYPE_NOTIFICATION_STATE_CHANGED狀態就是監聽微信或QQ傳送紅包過來時出現的notification,然後通過這個notification進入出現該紅包的聊天頁面。這個時候
視窗狀態也變換了,又執行到了TYPE_WINDOW_STATE_CHANGED這個狀態,這時我們的微信或QQ應該還是在聊天頁面,接下來去實現點選或開啟功能。需要注意的是,微信開啟紅包需要兩次點選,先是領取,然後開啟,但是QQ只需要一次點選,微信紅包較簡單,只有一種型別,QQ紅包型別有兩種。所以我們應該在視窗變化之前監聽是QQ還是微信傳送過來的紅包,然後根據不同的值去做不同的動作。

QQ紅包

//檢測到QQ紅包
     private void openQQHongBao(AccessibilityEvent event) {
          state_qq = STATE_NO_QQ;
//          getRunningActivityName();
          if ("cooperation.qwallet.plugin.QWalletPluginProxyActivity".equals(event.getClassName())) {
               state_qq = STATE_OPENED_QQ;
               if (RBSharedPerences.readRBQQState(getApplicationContext(), STATE_CODE_QQ).equals(state_qq)) {
                    performGlobalAction(GLOBAL_ACTION_HOME);
                    RBSharedPerences.writeRBQQstate(getApplicationContext(), STATE_CODE_QQ, STATE_NO_QQ);
               }
          } else if ("com.tencent.mobileqq.activity.SplashActivity".equals(event.getClassName())) {
               //拆紅包
               state_qq = STATE_CLIECKED_QQ;
               openQQPacket();
          }
     }

     //領取開啟QQ紅包
     private void openQQPacket() {
          AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
          if (nodeInfo != null) {
               AccessibilityNodeInfo targetNode = null;
               targetNode = findNodeInfosByText(nodeInfo, RB_BUTTON_TEXT_NAME);
               //普通紅包
               if (targetNode != null) {
                    performClick(targetNode);
                    RBSharedPerences.writeRBQQstate(getApplicationContext(), STATE_CODE_QQ, STATE_OPENED_QQ);
               }
               //口令紅包
               else {
                    AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
                    if (accessibilityNodeInfo != null) {
                         List<AccessibilityNodeInfo> nodeInfos = accessibilityNodeInfo
                                   .findAccessibilityNodeInfosByText(RB_PASSWORD);
                         for (AccessibilityNodeInfo nodeInfo1 : nodeInfos) {
                              targetNode = nodeInfos.get(nodeInfos.size() - 1);
                              performClick(targetNode);
                              writePassword();
                         }
                    }
               }
          }
     }

     //寫入併發送口令
     private void writePassword() {
          AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
          if (accessibilityNodeInfo != null) {
               findWidgetByText(accessibilityNodeInfo, RB_CLICK_TO_PASTE_PASSWORD);
          }
          if (accessibilityNodeInfo != null) {
               findWidgetByText(accessibilityNodeInfo, SEND_PASSWORD);
               RBSharedPerences.writeRBQQstate(getApplicationContext(), STATE_CODE_QQ, STATE_OPENED_QQ);
          }
     }

這裡面有個巧妙的點,就是開始時我始終找不到紅包RelativeLayout的ID,那可怎麼辦呢,我繼續往外層查詢,紅包外層還是一層RelativeLayout,但是content-desc中的值是 “xxxx,點選檢視詳情”。
查詢普通紅包
明白了嗎?沒錯,我通過文字查詢遇到有文字包含有這個時再去實現點選nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
那麼口令紅包又是如何實現的呢?同理,也是需要通過文字查詢文字
查詢口令紅包
但是這裡直接查詢”口令紅包”就可以了,查詢到後實現點選,QQ會自動給我們出現一個按鈕在輸入框上方
點選輸入口令
繼續查詢文字並點選”點選輸入口令”,這時候,輸入框就有口令了,那麼查詢到傳送的文字再實現一次點選,紅包就領取成功了。

微信紅包

//檢測到微信紅包
     private void openWeChatHongBao(AccessibilityEvent event) {
          state = STATE_NO;
//          getRunningActivityName();
          if ("com.tencent.mm.ui.LauncherUI".equals(event.getClassName())) {
               //點中紅包
               getWeChatPacket();
          } else if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI".equals(event.getClassName())) {
               //拆紅包
               openWeChatPacket();
          } else if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI".equals(event.getClassName())) {
               state = STATE_OPENED;
               //拆完紅包後返回
               if (RBSharedPerences.readRBWeChatState(getApplicationContext(), STATE_CODE).equals(state)) {
                    performGlobalAction(GLOBAL_ACTION_HOME);
                    RBSharedPerences.writeRBWeChatState(getApplicationContext(), STATE_CODE, STATE_NO);
               }
          }
     }

//領取紅包
     private void getWeChatPacket() {
          AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
          if (accessibilityNodeInfo != null) {
               findWidgetByText(accessibilityNodeInfo, GET_RB_TEXT);
          }
     }

     //拆開紅包
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
     private void openWeChatPacket() {
          AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
          if (nodeInfo != null) {
               AccessibilityNodeInfo targetNode = null;
               targetNode = findNodeInfosByClassName(nodeInfo, RB_BUTTON_CLASS_NAME);
               performClick(targetNode);
               //設定值
               RBSharedPerences.writeRBWeChatState(getApplicationContext(), STATE_CODE, STATE_OPENED);
          }
     }

微信的紅包和QQ 不一樣的地方在於需要先點開紅包出現一個紅包頁面,再點選紅包才會開啟紅包。點開紅包查詢文字”領取紅包”並實現點選,這時頁面進入”com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI”
開啟微信紅包
這時候顯然通過文字查詢是不行的了,因為這裡只是一個Button,並沒有文字,所以說需要另闢蹊徑,因為當前頁面僅有這一個Button,所以我們可以通過控制元件具體名字查詢控制元件。

     //通過元件名字查詢
     public static AccessibilityNodeInfo findNodeInfosByClassName(AccessibilityNodeInfo nodeInfo, String className) {
          if (TextUtils.isEmpty(className)) {
               return null;
          }
          for (int i = 0; i < nodeInfo.getChildCount(); i++) {
               AccessibilityNodeInfo node = nodeInfo.getChild(i);
               if (className.equals(node.getClassName())) {
                    return node;
               }
          }
          return null;
     }

再實現一個點選事件。perfect,微信紅包領取成功。

一個搶紅包功能就這麼愉快的搞定了!!快去試試吧!!你的點贊是對我最好的支援!!謝謝!!

最後附上CSDN下載連結和github的連結