300行程式碼實現 微信或QQ 搶紅包功能
每次看到別人發紅包,但是沒有搶到,這就很尷尬了。於是想著做一個紅包外掛,嘿嘿,再也不怕我反應遲鈍了。
首先,想到做這個功能時,我聯想到的是這個是不是lua寫出來的指令碼呢?但是條件不成立,因為可以不需要ROOT的。那麼問題就來了,這玩意怎麼實現?網上查了查,這下子明白了,Android平臺為了給一些使用手機不是很方便的人提供了一個解決方案,被叫做AccessibilityService。這是一個輔助類,用於監聽手機的焦點,視窗變化,按鈕點選等等。
接下來,分析了原始碼上的綠色字型,大致意思如下:
無障礙服務的開發需要擴充套件這個類並實現其抽象方法。
生命週期
由系統和遵循已建立的服務生命週期
當用戶在裝置設定中或在裝置設定期間關閉時,無障礙服務將停止
宣告
必須完成2 件事:
1.指定處理”android.accessibilityservice.AccessibilityService
”
2.請求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的連結