一步一步實現微信搶紅包
聖誕過後,又到了搶紅包的季節。各個公司的年會將逐漸展開,各個紅包群就熱鬧了起來。為了應對領導在群裡時不時的一個紅包,寫一個搶紅包的應用迫在眉睫了。之前由於沒有自動搶紅包錯失了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。