轉自 :http://www.jianshu.com/p/ba298b8d5a6e
一、AccessibilityService的使用
首先先寫一個類去繼承AccessibilityService
public class MyAccessibilityService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
//主要操作都在這里
}
@Override
public void onInterrupt() { }
...
}
接下來在res/xml文件夾下新建一個xml文件(不要問我xml文件夾哪來的(:зゝ∠)),叫做aaa.xml(名字隨意拉~~)。
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged|typeNotificationStateChanged"
android:accessibilityFeedbackType="feedbackAllMask"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="true"
android:description="@string/intro"
android:notificationTimeout="10"
android:packageNames="com.tencent.mm"/>
接下來解釋下各個屬性:
- description: 就是給你的這個輔助功能做個簡短說明。
- packageNames: 這個servie希望接收到的事件的包名,如果要接收多個,可以在幾個包名之間用 , 隔開。
- accessibilityEventTypes :這個就是設置了它能監視的動作,比如狀態欄來消息拉,界面發生變化拉等等,可以監視一個或多個(由分隔'|')動作,它有typeViewTextChanged,typeNotificationStateChanged,typeWindowContentChanged,typeAllMask等等,其他的類型可以參考這里。
- accessibilityFeedbackType :提供反饋類型,語音震動等等。也是可以設置必一個或多個(由分隔'|')值,可以設置feedbackSpoken、feedbackAllMask等。
- notificationTimeout:兩個相同類型的可訪問性事件之間的最小周期。就是監視到一個動作和監視到下一個動作的時間間隔。
- canRetrieveWindowContent:是否要能夠檢索活動窗口的內容,此設置不能在運行時改變。
- settingsActivity:允許用戶修改設置該服務的組件名稱。
aaa.xml文件寫好以后,就開始配置AndroidManifest.xml文件了
<service
android:name=".MyAccessibilityService"
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/aaa"/>
</service>
記住一定還要加上權限
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
好了,現在所有的東西都以及准備好了,運行程序,就可以在手機的設置-輔助功能里面找到自己的功能了。
二、搶紅包功能
首先先了解下這個servie響應的事件類型,TYPE_NOTIFICATION_STATE_CHANGED
是通知欄事件,TYPE_WINDOW_STATE_CHANGED
是窗口狀態改變,TYPE_WINDOW_CONTENT_CHANGED
是窗口內容改變,這3個就是本次最主要的事件類型,通過AccessibilityEvent
可以獲得這個事件類型,我們需要做判斷即可。
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
int eventType = event.getEventType();
switch (eventType) {
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
//TODO處理通知欄來的事件類型
break;
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
//TODO
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
//TODO窗口出現變化的時候處理
break;
}
}
接下來先處理通知欄

從上圖可以看到它的event type以及本次搶紅包的關鍵字“[微信紅包]”,所以在處理通知欄事件的時候先判斷該消息的text是否有關鍵字,如果有,則打開微信界面。
private void getNotificationInfo(AccessibilityEvent event) {
Parcelable parcelable = event.getParcelableData();
if (parcelable instanceof Notification) {
Notification notification = (Notification) parcelable;
try {
notification.contentIntent.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}
}
打開界面也就意味這窗口發生改變了,所以onAccessibilityEvent
又接收到新的事件了。接下來就要在另外兩個事件里面做找紅包和開紅包的操作了。


從上面兩個圖片可以知道,點擊搶紅包和點擊開的時候它們的event type都是TYPE_WINDOW_STATE_CHANGED
,並且在重點關注一下它們的class,所以可以在TYPE_WINDOW_STATE_CHANGED
里面做獲取event.getClassName();
然后再根據這個class判斷到底是做搶紅包呢還是開紅包呢。
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
rootNodeInfo = getRootInActiveWindow();//相當於獲得了這個界面,可以在這上面找控件啊什么的
if (rootNodeInfo == null) return;
if (className.equals(com.tencent.mm.ui.LauncherUI)) {
getLuckyMoney();//找紅包,找到紅包就打開
} else if (className.equals(com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI)) {
openLuckyMoney();//開紅包
backLuckyMoney(eventType);//紅包詳情,紅包搶光了,紅包超時了做一個取消的動作
} else if (className.equals(com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI)) {
backLuckyMoney(eventType);
}
break;

找紅包就是在rootNodeInfo
里面findAccessibilityNodeInfosByText("領取紅包")
,如果有,則返回這個紅包的AccessibilityNodeInfo
。對於圖片里面的textview:領取紅包,為了反正在聊天界面不停的找到這個開過的紅包,然后打開紅包,關閉,打開,關閉的死循環,在找紅包這邊需要加一個判斷,如果這個紅包已經開過一次了,在窗口沒有變化的時候將不點擊紅包。判斷的條件就是圖片中的textview:時間和imageview:XXX頭像。
public boolean judgeNode(AccessibilityNodeInfo node) {
try {
if (node == null) return false;
AccessibilityNodeInfo node1 = node.getParent().getParent();
int count = node1.getChildCount();
result.setLength(0);
for (int i = 0; i < count; i++) {
AccessibilityNodeInfo node2 = node1.getChild(i);
if ("android.widget.ImageView".equals(node2.getClassName())) {
CharSequence contentDescription = node2.getContentDescription();
if (contentDescription != null)
result.append(contentDescription.toString());
}
if ("android.widget.TextView".equals(node2.getClassName())) {
CharSequence thisNodeText = node2.getText();
if (thisNodeText != null) result.append(thisNodeText.toString());
}
}
if (result.equals(info)) return false;
info = hongbaoInfo;
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
紅包找到了別忘了在判斷結束后加上node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
來打開搶紅包界面。下面就是開紅包了,需要找到紅包界面中中間的那個“開”字,有兩種辦法獲得這個字,第一種是遞歸尋找界面上面的button,它是該界面的唯一button,第二種就是直接使用這個button的resource-id,不過據說這個id騰X會經常改動它,不怕麻煩可以選擇哦。

找到之后別忘了點擊這個node哦node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
。
在打開紅包后進入紅包詳情,這時候要退出這個界面回到聊天界面,還有就是紅包被搶光了,也需要回到聊天界面,這時候分別判斷rootNodeInfo.findAccessibilityNodeInfosByText(text)
,如果找到,則為true,做performGlobalAction(GLOBAL_ACTION_BACK);
就可以了。
具體的操作全部記錄完畢了,目前的代碼還有些碧油雞,還需要改改,先這樣吧。
參考資料: