Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android AccessibilityService實現微信搶紅包插件

Android AccessibilityService實現微信搶紅包插件

編輯:關於Android編程

在你的手機更多設置或者高級設置中,我們會發現有個無障礙的功能,很多人不知道這個功能具體是干嘛的,其實這個功能是為了增強用戶界面以幫助殘障人士,或者可能暫時無法與設備充分交互的人們

它的具體實現是通過AccessibilityService服務運行在後台中,通過AccessibilityEvent接收指定事件的回調。這樣的事件表示用戶在界面中的一些狀態轉換,例如:焦點改變了,一個按鈕被點擊,等等。這樣的服務可以選擇請求活動窗口的內容的能力。簡單的說AccessibilityService就是一個後台監控服務,當你監控的內容發生改變時,就會調用後台服務的回調方法

AccessibilityService使用

1.1 創建服務類

編寫自己的Service類,重寫onServiceConnected()方法、onAccessibilityEvent()方法和onInterrupt()方法

public class QHBAccessibilityService extends AccessibilityService {

 /**
 * 當啟動服務的時候就會被調用
 */
 @Override
 protected void onServiceConnected() {
 super.onServiceConnected();
 }

 /**
 * 監聽窗口變化的回調
 */
 @Override
 public void onAccessibilityEvent(AccessibilityEvent event) {
 int eventType = event.getEventType();
 //根據事件回調類型進行處理
 }

 /**
 * 中斷服務的回調
 */
 @Override
 public void onInterrupt() {

 }
}

下面是對AccessibilityService中常用的方法的介紹

disableSelf():禁用當前服務,也就是在服務可以通過該方法停止運行
findFoucs(int falg):查找擁有特定焦點類型的控件
getRootInActiveWindow():如果配置能夠獲取窗口內容,則會返回當前活動窗口的根結點
getSeviceInfo():獲取當前服務的配置信息
onAccessibilityEvent(AccessibilityEvent event):有關AccessibilityEvent事件的回調函數,系統通過sendAccessibiliyEvent()不斷的發送AccessibilityEvent到此處
performGlobalAction(int action):執行全局操作,比如返回,回到主頁,打開最近等操作
setServiceInfo(AccessibilityServiceInfo info):設置當前服務的配置信息
getSystemService(String name):獲取系統服務
onKeyEvent(KeyEvent event):如果允許服務監聽按鍵操作,該方法是按鍵事件的回調,需要注意,這個過程發生了系統處理按鍵事件之前
onServiceConnected():系統成功綁定該服務時被觸發,也就是當你在設置中開啟相應的服務,系統成功的綁定了該服務時會觸發,通常我們可以在這裡做一些初始化操作
onInterrupt():服務中斷時的回調

1.2 聲明服務

既然是個後台服務,那麼就需要我們在manifests中配置該服務信息

<service
 android:name=".AccessibilityService.QHBAccessibilityService"
 android:enabled="true"
 android:exported="true"
 android:label="@string/label"
 android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
 <intent-filter>
 <action android:name="android.accessibilityservice.AccessibilityService" />
 </intent-filter>
</service>

我們必須注意:任何一個信息配置錯誤,都會使該服務無反應

android:label:在無障礙列表中顯示該服務的名字

android:permission:需要指定BIND_ACCESSIBILITY_SERVICE權限,這是4.0以上的系統要求的
intent-filter:這個name是固定不變的

1.3 配置服務參數

配置服務參數是指:配置用來接受指定類型的事件,監聽指定package,檢索窗口內容,獲取事件類型的時間等等。其配置服務參數有兩種方法:

方法一:安卓4.0之後可以通過meta-data標簽指定xml文件進行配置
方法二:通過代碼動態配置參數

1.3.1 方法一

在原先的manifests中增加meta-data標簽指定xml文件

<service
 android:name=".AccessibilityService.QHBAccessibilityService"
 android:enabled="true"
 android:exported="true"
 android:label="@string/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>

接下來是accessibility_service_config文件的配置

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
 android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged|typeWindowsChanged"
 android:accessibilityFeedbackType="feedbackGeneric"
 android:accessibilityFlags="flagDefault"
 android:canRetrieveWindowContent="true"
 android:description="@string/description"
 android:notificationTimeout="100"
 android:packageNames="com.tencent.mm" />

下面是對xml參數的介紹

accessibilityEventTypes:表示該服務對界面中的哪些變化感興趣,即哪些事件通知,比如窗口打開,滑動,焦點變化,長按等。具體的值可以在AccessibilityEvent類中查到,如typeAllMask表示接受所有的事件通知
accessibilityFeedbackType:表示反饋方式,比如是語音播放,還是震動
canRetrieveWindowContent:表示該服務能否訪問活動窗口中的內容。也就是如果你希望在服務中獲取窗體內容,則需要設置其值為true
description:對該無障礙功能的描述,具體體現在下圖

notificationTimeout:接受事件的時間間隔,通常將其設置為100即可
packageNames:表示對該服務是用來監聽哪個包的產生的事件,這裡以微信的包名為例

1.3.2 方法二

通過代碼為我們的AccessibilityService配置AccessibilityServiceInfo信息,這裡我們可以抽取成一個方法進行設置

private void settingAccessibilityInfo() {
 String[] packageNames = {"com.tencent.mm"};
 AccessibilityServiceInfo mAccessibilityServiceInfo = new AccessibilityServiceInfo();
 // 響應事件的類型,這裡是全部的響應事件(長按,單擊,滑動等)
 mAccessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
 // 反饋給用戶的類型,這裡是語音提示
 mAccessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
 // 過濾的包名
 mAccessibilityServiceInfo.packageNames = packageNames;
 setServiceInfo(mAccessibilityServiceInfo);
}

在這裡涉及到了AccessibilityServiceInfo類,AccessibilityServiceInfo類被用於配置AccessibilityService信息,該類中包含了大量用於配置的常量字段及用來xml屬性,常見的有:accessibilityEventTypes,canRequestFilterKeyEvents,packageNames等等

1.4 啟動服務

這裡我們需要在無障礙功能裡面手動打開該項功能,否則無法繼續進行,通過下面代碼可以打開系統的無障礙功能列表

Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivity(intent);

1.5 處理事件信息

由於我們監聽了事件的通知欄和界面等信息,當我們指定packageNames的通知欄或者界面發生變化時,會通過onAccessibilityEvent回調我們的事件,接著進行事件的處理

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
 int eventType = event.getEventType();
 //根據事件回調類型進行處理
 switch (eventType) {
 //當通知欄發生改變時
 case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:

  break;
 //當窗口的狀態發生改變時
 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:

  break;
 }
}

當我們微信收到通知時,狀態欄會有一條推送信息到達,這個時候就會被TYPE_NOTIFICATION_STATE_CHANGED監聽,執行裡面的內容,當我們切換微信界面時,或者使用微信時,這個時候就會被TYPE_WINDOW_STATE_CHANGED監聽,執行裡面的內容

AccessibilityEvent的方法

getEventType():事件類型
getSource():獲取事件源對應的結點信息
getClassName():獲取事件源對應類的類型,比如點擊事件是有某個Button產生的,那麼此時獲取的就是Button的完整類名
getText():獲取事件源的文本信息,比如事件是有TextView發出的,此時獲取的就是TextView的text屬性。如果該事件源是樹結構,那麼此時獲取的是這個樹上所有具有text屬性的值的集合
isEnabled():事件源(對應的界面控件)是否處在可用狀態
getItemCount():如果事件源是樹結構,將返回該樹根節點下子節點的數量

1.6 獲取節點信息

獲取了界面窗口變化後,這個時候就要獲取控件的節點。整個窗口的節點本質是個樹結構,通過以下操作節點信息

1.6.1 獲取窗口節點(根節點)

AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();

1.6.2 獲取指定子節點(控件節點)

//通過文本找到對應的節點集合
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(text);
//通過控件ID找到對應的節點集合,如com.tencent.mm:id/gd
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(clickId);

1.7 模擬節點點擊

當我們獲取了節點信息之後,對控件節點進行模擬點擊、長按等操作,AccessibilityNodeInfo類提供了performAction()方法讓我們執行模擬操作,具體操作可看官方文檔介紹,這裡列舉常用的操作

//模擬點擊
accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
//模擬長按
accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
//模擬獲取焦點
accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
//模擬粘貼
accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);

搶紅包插件實現

2.1 原理分析

1、收到微信紅包的推送信息,在推送信息中判斷是否出現”[微信紅包]”的消息提示,如果出現則點擊進入聊天界面
2、通過遍歷窗口樹節點,發現帶有”領取紅包”字樣的節點,則點擊進入,即紅包,彈出搶紅包界面
3、在搶紅包界面,通過ID獲取”開”按鈕的節點,則打開紅包
4、在紅包詳情頁面,通過ID獲取返回鍵按鈕的節點,點擊並返回微信聊天界面

2.2 注意事項

1、由於微信每個版本的按鈕ID都是不一樣的,在我們的程序中是需要去修改按鈕ID,以達到版本的適配
2、在獲取控件ID的時候,注意其布局是否可點擊,否則獲取不可點擊的控件,會使程序無反應

2.3 獲取控件ID

當我們手機接入USB線時,在Android Device Monitor中的選擇設備並開啟Dump View Hierarchy for UI Automator工具,通過它可以獲取控件信息

獲取”開”按鈕ID和返回按鈕ID

2.4 代碼實現

注意:這裡使用的是微信最新6.3.30版本的控件ID,如果是其他版本的請自行適配

/**
 * =====作者=====
 * 許英俊
 * =====時間=====
 * 2016/11/19.
 */
public class QHBAccessibilityService extends AccessibilityService {

 private List<AccessibilityNodeInfo> parents;

 /**
  * 當啟動服務的時候就會被調用
  */
 @Override
 protected void onServiceConnected() {
  super.onServiceConnected();
  parents = new ArrayList<>();
 }

 /**
  * 監聽窗口變化的回調
  */
 @Override
 public void onAccessibilityEvent(AccessibilityEvent event) {
  int eventType = event.getEventType();
  switch (eventType) {
   //當通知欄發生改變時
   case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
    List<CharSequence> texts = event.getText();
    if (!texts.isEmpty()) {
     for (CharSequence text : texts) {
      String content = text.toString();
      if (content.contains("[微信紅包]")) {
       //模擬打開通知欄消息,即打開微信
       if (event.getParcelableData() != null &&
         event.getParcelableData() instanceof Notification) {
        Notification notification = (Notification) event.getParcelableData();
        PendingIntent pendingIntent = notification.contentIntent;
        try {
         pendingIntent.send();
         Log.e("demo","進入微信");
        } catch (Exception e) {
         e.printStackTrace();
        }
       }
      }
     }
    }
    break;
   //當窗口的狀態發生改變時
   case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
    String className = event.getClassName().toString();
    if (className.equals("com.tencent.mm.ui.LauncherUI")) {
     //點擊最後一個紅包
     Log.e("demo","點擊紅包");
     getLastPacket();
    } else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI")) {
     //開紅包
     Log.e("demo","開紅包");
     inputClick("com.tencent.mm:id/bg7");
    } else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")) {
     //退出紅包
     Log.e("demo","退出紅包");
     inputClick("com.tencent.mm:id/gd");
    }
    break;
  }
 }

 /**
  * 通過ID獲取控件,並進行模擬點擊
  * @param clickId
  */
 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
 private void inputClick(String clickId) {
  AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
  if (nodeInfo != null) {
   List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(clickId);
   for (AccessibilityNodeInfo item : list) {
    item.performAction(AccessibilityNodeInfo.ACTION_CLICK);
   }
  }
 }

 /**
  * 獲取List中最後一個紅包,並進行模擬點擊
  */
 private void getLastPacket() {
  AccessibilityNodeInfo rootNode = getRootInActiveWindow();
  recycle(rootNode);
  if(parents.size()>0){
   parents.get(parents.size() - 1).performAction(AccessibilityNodeInfo.ACTION_CLICK);
  }
 }

 /**
  * 回歸函數遍歷每一個節點,並將含有"領取紅包"存進List中
  *
  * @param info
  */
 public void recycle(AccessibilityNodeInfo info) {
  if (info.getChildCount() == 0) {
   if (info.getText() != null) {
    if ("領取紅包".equals(info.getText().toString())) {
     if (info.isClickable()) {
      info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
     }
     AccessibilityNodeInfo parent = info.getParent();
     while (parent != null) {
      if (parent.isClickable()) {
       parents.add(parent);
       break;
      }
      parent = parent.getParent();
     }
    }
   }
  } else {
   for (int i = 0; i < info.getChildCount(); i++) {
    if (info.getChild(i) != null) {
     recycle(info.getChild(i));
    }
   }
  }
 }

 /**
  * 中斷服務的回調
  */
 @Override
 public void onInterrupt() {

 }
}

當收到紅包發送的時候,Log的打印信息

11-21 13:53:06.275 2909-2909/com.handsome.boke2 E/demo: 進入微信
11-21 13:53:06.921 2909-2909/com.handsome.boke2 E/demo: 點擊紅包
11-21 13:53:07.883 2909-2909/com.handsome.boke2 E/demo: 開紅包
11-21 13:53:08.732 2909-2909/com.handsome.boke2 E/demo: 退出紅包

你可能會想到做一些竊取信息的軟件,比如獲取QQ密碼、支付寶密碼等等,哈哈,凡是EditText中設置inputType為password類型的,都無法獲取其輸入值

2.5 源碼下載:http://xiazai.jb51.net/201611/yuanma/Androidwxpackage(jb51.net).rar

本文已被整理到了《Android微信開發教程匯總》,歡迎大家學習閱讀。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved