【Android】中微信搶紅包助手的實現(程式碼整理)
實現原理
通過利用AccessibilityService輔助服務,監測螢幕內容,如監聽狀態列的資訊,螢幕跳轉等,以此來實現自動拆紅包的功能。關於AccessibilityService輔助服務,可以自行百度瞭解更多。
程式碼基礎:
1.首先宣告一個RedPacketService繼承自AccessibilityService,該服務類有兩個方法必須重寫,如下:
-
/**
-
* Created by Yemon on 2017/2/3.
-
* email:[email protected]
-
*
-
* 搶紅包服務類
-
*/
-
public class RedPacketService extends AccessibilityService {
-
/**
-
* 必須重寫的方法:此方法用了接受系統發來的event。在你註冊的event發生是被呼叫。在整個生命週期會被呼叫多次。
-
*/
-
@Override
-
public void onAccessibilityEvent(AccessibilityEvent event) {
-
}
-
/**
-
* 必須重寫的方法:系統要中斷此service返回的響應時會呼叫。在整個生命週期會被呼叫多次。
-
*/
-
@Override
-
public void onInterrupt() {
-
Toast.makeText(this, "我快被終結了啊-----", Toast.LENGTH_SHORT).show();
-
}
-
/**
-
* 服務已連線
-
*/
-
@Override
-
protected void onServiceConnected() {
-
Toast.makeText(this, "搶紅包服務開啟", Toast.LENGTH_SHORT).show();
-
super.onServiceConnected();
-
}
-
/**
-
* 服務已斷開
-
*/
-
@Override
-
public boolean onUnbind(Intent intent) {
-
Toast.makeText(this, "搶紅包服務已被關閉", Toast.LENGTH_SHORT).show();
-
return super.onUnbind(intent);
-
}
-
}
2.對我們的RedPacketService進行一些配置,這裡配置方法可以選擇程式碼動態配置(onServiceConnected裡配置),也可以直接在res/xml下新建.xml檔案,沒有xml資料夾就新建。這裡我們將檔案命名為redpacket_service_config.xml,程式碼如下:
-
<?xml version="1.0" encoding="utf-8"?>
-
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
-
android:accessibilityEventTypes="typeAllMask"
-
android:accessibilityFeedbackType="feedbackGeneric"
-
android:accessibilityFlags="flagDefault"
-
android:canRetrieveWindowContent="true"
-
android:description="@string/desc"
-
android:notificationTimeout="100"
-
android:packageNames="com.tencent.mm" />
accessibilityEventTypes:
響應哪一種型別的事件,typeAllMask就是響應所有型別的事件了,另外還有單擊、長按、滑動等。
accessibilityFeedbackType:
用什麼方式反饋給使用者,有語音播出和振動。可以配置一些TTS引擎,讓它實現發音。
packageNames:
指定響應哪個應用的事件。這裡我們是寫搶紅包助手,就寫微信的包名:com.tencent.mm,這樣就可以監聽微信產生的事件了。
notificationTimeout:
響應時間
description:
輔助服務的描述資訊。
3.service是四大元件之一,需要在AndroidManifest進行配置,注意這裡稍微有些不同:
-
<!--搶紅包服務-->
-
<service
-
android:name=".RedPacketService"
-
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/redpacket_service_config"></meta-data>
-
</service>
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" 許可權申請
android:resource="@xml/redpacket_service_config" 引用剛才的配置檔案 核心程式碼: 我們的紅包助手,核心思路分為三步走: 監聽通知欄微信訊息,如果彈出[微信紅包]字樣,模擬手指點選狀態列跳轉到微信聊天介面→在微信聊天介面查詢紅包,如果找到則模擬手指點選開啟,彈出開啟紅包介面→模擬手指點選紅包“開” 1.監聽通知欄訊息,檢視是否有[微信紅包]字樣,程式碼如下:
-
@Override
-
public void onAccessibilityEvent(AccessibilityEvent event) {
-
int eventType = event.getEventType();
-
switch (eventType) {
-
//通知欄來資訊,判斷是否含有微信紅包字樣,是的話跳轉
-
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
-
List<CharSequence> texts = event.getText();
-
for (CharSequence text : texts) {
-
String content = text.toString();
-
if (!TextUtils.isEmpty(content)) {
-
//判斷是否含有[微信紅包]字樣
-
if (content.contains("[微信紅包]")) {
-
//如果有則開啟微信紅包頁面
-
openWeChatPage(event);
-
}
-
}
-
}
-
break;
-
}
-
}
-
/**
-
* 開啟紅包所在的聊天頁面
-
*/
-
private void openWeChatPage(AccessibilityEvent event) {
-
//A instanceof B 用來判斷記憶體中實際物件A是不是B型別,常用於強制轉換前的判斷
-
if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
-
Notification notification = (Notification) event.getParcelableData();
-
//開啟對應的聊天介面
-
PendingIntent pendingIntent = notification.contentIntent;
-
try {
-
pendingIntent.send();
-
} catch (PendingIntent.CanceledException e) {
-
e.printStackTrace();
-
}
-
}
-
}
2.判斷當前是否在微信聊天頁面,是的話遍歷當前頁面各個控制元件,找到含有微信紅包或者領取紅包的textview控制元件,然後逐層找到他的可點選父佈局(圖中綠色部分),模擬點選跳轉到含有“開”的紅包介面,程式碼如下:
-
@Override
-
public void onAccessibilityEvent(AccessibilityEvent event) {
-
int eventType = event.getEventType();
-
switch (eventType) {
-
//視窗發生改變時會呼叫該事件
-
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
-
String className = event.getClassName().toString();
-
//判斷是否是微信聊天介面
-
if ("com.tencent.mm.ui.LauncherUI".equals(className)) {
-
//獲取當前聊天頁面的根佈局
-
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
-
//開始找紅包
-
findRedPacket(rootNode);
-
}
-
}
-
}
-
/**
-
* 遍歷查詢紅包
-
*/
-
private void findRedPacket(AccessibilityNodeInfo rootNode) {
-
if (rootNode != null) {
-
//從最後一行開始找起
-
for (int i = rootNode.getChildCount() - 1; i >= 0; i--) {
-
AccessibilityNodeInfo node = rootNode.getChild(i);
-
//如果node為空則跳過該節點
-
if (node == null) {
-
continue;
-
}
-
CharSequence text = node.getText();
-
if (text != null && text.toString().equals("領取紅包")) {
-
AccessibilityNodeInfo parent = node.getParent();
-
//while迴圈,遍歷"領取紅包"的各個父佈局,直至找到可點選的為止
-
while (parent != null) {
-
if (parent.isClickable()) {
-
//模擬點選
-
parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
-
//isOpenRP用於判斷該紅包是否點選過
-
isOpenRP = true;
-
break;
-
}
-
parent = parent.getParent();
-
}
-
}
-
//判斷是否已經開啟過那個最新的紅包了,是的話就跳出for迴圈,不是的話繼續遍歷
-
if (isOpenRP) {
-
break;
-
} else {
-
findRedPacket(node);
-
}
-
}
-
}
-
}
3.點選紅包後,在模擬手指點選“開”以此開啟紅包,跳轉到紅包詳情介面,方法與步驟二類似:
-
@Override
-
public void onAccessibilityEvent(AccessibilityEvent event) {
-
int eventType = event.getEventType();
-
switch (eventType) {
-
//視窗發生改變時會呼叫該事件
-
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
-
String className = event.getClassName().toString();
-
//判斷是否是顯示‘開’的那個紅包介面
-
if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI".equals(className)) {
-
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
-
//開始搶紅包
-
openRedPacket(rootNode);
-
}
-
break;
-
}
-
}
-
/**
-
* 開始開啟紅包
-
*/
-
private void openRedPacket(AccessibilityNodeInfo rootNode) {
-
for (int i = 0; i < rootNode.getChildCount(); i++) {
-
AccessibilityNodeInfo node = rootNode.getChild(i);
-
if ("android.widget.Button".equals(node.getClassName())) {
-
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
-
}
-
openRedPacket(node);
-
}
-
}
結合以上三步,下面是完整程式碼,註釋已經寫的很清楚,直接看程式碼:
-
package com.cxk.redpacket;
-
import android.accessibilityservice.AccessibilityService;
-
import android.app.KeyguardManager;
-
import android.app.Notification;
-
import android.app.PendingIntent;
-
import android.app.Service;
-
import android.content.Context;
-
import android.content.Intent;
-
import android.os.IBinder;
-
import android.os.PowerManager;
-
import android.text.TextUtils;
-
import android.util.Log;
-
import android.view.accessibility.AccessibilityEvent;
-
import android.view.accessibility.AccessibilityNodeInfo;
-
import android.widget.Toast;
-
import java.util.List;
-
/**
-
* 搶紅包Service,繼承AccessibilityService
-
*/
-
public class RedPacketService extends AccessibilityService {
-
/**
-
* 微信幾個頁面的包名+地址。用於判斷在哪個頁面 LAUCHER-微信聊天介面,LUCKEY_MONEY_RECEIVER-點選紅包彈出的介面
-
*/
-
private String LAUCHER = "com.tencent.mm.ui.LauncherUI";
-
private String LUCKEY_MONEY_DETAIL = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI";
-
private String LUCKEY_MONEY_RECEIVER = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI";
-
/**
-
* 用於判斷是否點選過紅包了
-
*/
-
private boolean isOpenRP;
-
@Override
-
public void onAccessibilityEvent(AccessibilityEvent event) {
-
int eventType = event.getEventType();
-
switch (eventType) {
-
//通知欄來資訊,判斷是否含有微信紅包字樣,是的話跳轉
-
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
-
List<CharSequence> texts = event.getText();
-
for (CharSequence text : texts) {
-
String content = text.toString();
-
if (!TextUtils.isEmpty(content)) {
-
//判斷是否含有[微信紅包]字樣
-
if (content.contains("[微信紅包]")) {
-
//如果有則開啟微信紅包頁面
-
openWeChatPage(event);
-
isOpenRP=false;
-
}
-
}
-
}
-
break;
-
//介面跳轉的監聽
-
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
-
String className = event.getClassName().toString();
-
//判斷是否是微信聊天介面
-
if (LAUCHER.equals(className)) {
-
//獲取當前聊天頁面的根佈局
-
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
-
//開始找紅包
-
findRedPacket(rootNode);
-
}
-
//判斷是否是顯示‘開’的那個紅包介面
-
if (LUCKEY_MONEY_RECEIVER.equals(className)) {
-
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
-
//開始搶紅包
-
openRedPacket(rootNode);
-
}
-
//判斷是否是紅包領取後的詳情介面
-
if(LUCKEY_MONEY_DETAIL.equals(className)){
-
//返回桌面
-
back2Home();
-
}
-
break;
-
}
-
}
-
/**
-
* 開始開啟紅包
-
*/
-
private void openRedPacket(AccessibilityNodeInfo rootNode) {
-
for (int i = 0; i < rootNode.getChildCount(); i++) {
-
AccessibilityNodeInfo node = rootNode.getChild(i);
-
if ("android.widget.Button".equals(node.getClassName())) {
-
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
-
}
-
openRedPacket(node);
-
}
-
}
-
/**
-
* 遍歷查詢紅包
-
*/
-
private void findRedPacket(AccessibilityNodeInfo rootNode) {
-
if (rootNode != null) {
-
//從最後一行開始找起
-
for (int i = rootNode.getChildCount() - 1; i >= 0; i--) {
-
AccessibilityNodeInfo node = rootNode.getChild(i);
-
//如果node為空則跳過該節點
-
if (node == null) {
-
continue;
-
}
-
CharSequence text = node.getText();
-
if (text != null && text.toString().equals("領取紅包")) {
-
AccessibilityNodeInfo parent = node.getParent();
-
//while迴圈,遍歷"領取紅包"的各個父佈局,直至找到可點選的為止
-
while (parent != null) {
-
if (parent.isClickable()) {
-
//模擬點選
-
parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
-
//isOpenRP用於判斷該紅包是否點選過
-
isOpenRP = true;
-
break;
-
}
-
parent = parent.getParent();
-
}
-
}
-
//判斷是否已經開啟過那個最新的紅包了,是的話就跳出for迴圈,不是的話繼續遍歷
-
if (isOpenRP) {
-
break;
-
} else {
-
findRedPacket(node);
-
}
-
}
-
}
-
}
-
/**
-
* 開啟紅包所在的聊天頁面
-
*/
-
private void openWeChatPage(AccessibilityEvent event) {
-
//A instanceof B 用來判斷記憶體中實際物件A是不是B型別,常用於強制轉換前的判斷
-
if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
-
Notification notification = (Notification) event.getParcelableData();
-
//開啟對應的聊天介面
-
PendingIntent pendingIntent = notification.contentIntent;
-
try {
-
pendingIntent.send();
-
} catch (PendingIntent.CanceledException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
/**
-
* 服務連線
-
*/
-
@Override
-
protected void onServiceConnected() {
-
Toast.makeText(this, "搶紅包服務開啟", Toast.LENGTH_SHORT).show();
-
super.onServiceConnected();
-
}
-
/**
-
* 必須重寫的方法:系統要中斷此service返回的響應時會呼叫。在整個生命週期會被呼叫多次。
-
*/
-
@Override
-
public void onInterrupt() {
-
Toast.makeText(this, "我快被終結了啊-----", Toast.LENGTH_SHORT).show();
-
}
-
/**
-
* 服務斷開
-
*/
-
@Override
-
public boolean onUnbind(Intent intent) {
-
Toast.makeText(this, "搶紅包服務已被關閉", Toast.LENGTH_SHORT).show();
-
return super.onUnbind(intent);
-
}
-
/**
-
* 返回桌面
-
*/
-
private void back2Home() {
-
Intent home=new Intent(Intent.ACTION_MAIN);
-
home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
home.addCategory(Intent.CATEGORY_HOME);
-
startActivity(home);
-
}
-
}
使用方法:
設定-輔助功能-無障礙-點選RedPacket開啟即可
已知問題:
1.聊天列表或者聊天介面中無法直接自動搶紅包
2.未做熄屏自動搶紅包處理,想要熄屏能自動搶紅包的同學直接把開屏程式碼寫在第一步即可。