hi大家好。
新年又來了,微信群里又是各種紅包橫飛。作為技術(shù)人員的我們卻大可不必?fù)?dān)心一不小心,手速慢了點(diǎn),又錯(cuò)過了幾十萬。我們可以通過安卓的輔助功能來實(shí)現(xiàn)自己的微信自動搶紅包,安全又快捷。
輔助服務(wù)
我們在 設(shè)置->無障礙 中,就可以看到手機(jī)中所有的輔助服務(wù)了。輔助功能通常是針對一些視力聽力等有障礙導(dǎo)致使用手機(jī)有障礙的人群,做一些語言提醒等幫助他們更好地使用手機(jī)。
因?yàn)檩o助功能可以得到系統(tǒng)級別的事件和服務(wù),第三方應(yīng)用的輔助功能都需要手動開啟。我們常用的綠色守護(hù),冰箱等應(yīng)用都是利用輔助服務(wù)實(shí)現(xiàn)的。

我們先來實(shí)現(xiàn)一個(gè)簡單的輔助服務(wù)吧。
1,繼承AccessibilityService類。AccessibilityService類一共有四個(gè)可以重寫的方法。
TestService類
public class TestService extends AccessibilityService {
/**
* 必須重寫的方法:此方法用了接受系統(tǒng)發(fā)來的event。在你注冊的event發(fā)生是被調(diào)用。在整個(gè)生命周期會被調(diào)用多次。
*/
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
/**
* 必須重寫的方法:系統(tǒng)要中斷此service返回的響應(yīng)時(shí)會調(diào)用。在整個(gè)生命周期會被調(diào)用多次。
*/
@Override
public void onInterrupt() {
}
/**
* 當(dāng)系統(tǒng)連接上你的服務(wù)時(shí)被調(diào)用
*/
@Override
protected void onServiceConnected() {
super.onServiceConnected();
}
/**
* 在系統(tǒng)要關(guān)閉此service時(shí)調(diào)用。
*/
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
}
2,給你的輔助服務(wù)寫一個(gè)配置文件。
res/xml/text_server_config.xml
<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/accessibility_description"
android:notificationTimeout="10" />
看屬性的名字應(yīng)該比較容易猜測到不同是屬性是用來干嘛的。
accessibilityEventTypes:響應(yīng)那種類型的事件
accessibilityFeedbackType:用什么方式反饋給用戶
notificationTimeout:響應(yīng)時(shí)間
packageNames:指定響應(yīng)哪個(gè)應(yīng)用的事件。如果不填則是響應(yīng)所有的應(yīng)用事件(如果以后寫搶紅包的輔助功能,可以只寫微信的包名)
description:輔助服務(wù)的描述信息。
3,在manifest中注冊服務(wù)。
<service
android:name=".TestService"
//輔助功能的名稱
android:label="@string/test_service_label"
//此處必須聲明一次權(quán)限
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/text_service_config" />
</service>
4,最后,回到服務(wù)的類,TestService。在onAccessibilityEvent(AccessibilityEvent event)方法中,我們可以接收到系統(tǒng)發(fā)過來的事件。當(dāng)然取決于我們在配置文件中選擇了我們要監(jiān)聽哪些種類,哪些應(yīng)用的事件了。
public void onAccessibilityEvent(AccessibilityEvent event) {
//得到事件的包名。如果注冊了多個(gè)應(yīng)用的事件,可以在此做一個(gè)判斷。
String packageName = event.getPackageName().toString();
//得到對應(yīng)的事件類型,這里有很多很多種的事件類型,具體可以自行翻閱AccessibilityEvent類中的定義。
int eventType = event.getEventType();
//得到根的view節(jié)點(diǎn)??梢援?dāng)做當(dāng)前acitivity的視圖看成是樹狀結(jié)構(gòu)的(實(shí)際上也是~。~),而我們現(xiàn)在就得到了它的根節(jié)點(diǎn)。
AccessibilityNodeInfo root = getRootInActiveWindow();
//我們可以得到此節(jié)點(diǎn)的文字
String rootText = root.getText().toString();
//得到此節(jié)點(diǎn)的class
String rootClass = root.getClass().toString();
//得到子節(jié)點(diǎn)的和子節(jié)點(diǎn)總數(shù)
root.getChild(root.getChildCount()-1);
}
到這里,我們可以通過無數(shù)次遍歷,找到視圖上自己想要的那個(gè)控件了。當(dāng)然,還有更加簡單的方法。
//這兩個(gè)方法如果找不到的話,都會報(bào)錯(cuò)。所以請做好對應(yīng)的處理。
root.findAccessibilityNodeInfosByText("根據(jù)文本內(nèi)容查找節(jié)點(diǎn)");
root.findAccessibilityNodeInfosByViewId("根據(jù)id查找節(jié)點(diǎn),當(dāng)然實(shí)際上很難知道id是多少~、~");
最后我們可以對控件做一些操作,比如
//點(diǎn)擊操作
root.performAction(AccessibilityNodeInfo.ACTION_CLICK);
//滑動操作
root.performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
好,對于輔助服務(wù)的基礎(chǔ)知識我們學(xué)到這里已經(jīng)差不多了,如果跟著我寫了一個(gè)demo的童鞋們,可以運(yùn)行一下,在設(shè)置->無障礙中把自己的輔助功能打開試試。把玩一下。
下面可以進(jìn)入搶紅包的開發(fā)。
搶紅包
我們先回顧一下手動搶紅包的過程。
假設(shè)小新某天在用手機(jī)刷微博。叮叮,聽到提示聲,通知欄上顯示:“小明\n [微信紅包]恭喜發(fā)財(cái),大吉大利”。
小新光速點(diǎn)了一下微信通知,手機(jī)自動跳轉(zhuǎn)到了對應(yīng)的微信聊天頁面,聊天界面里正是一條橙色底色,配上紅包圖片的信息:“恭喜發(fā)財(cái),大吉大利\n領(lǐng)取紅包\n微信紅包”。
腦子還沒反應(yīng)過來,手指已經(jīng)自動點(diǎn)到這條信息上,這是有一個(gè)正在加載的提示框一閃而過,然后就是正中一個(gè)大大的紅包,中間是金黃色的“開”字。
不用想了,開!看著開字轉(zhuǎn)啊轉(zhuǎn),心急如焚。最后屏幕一閃,跳到了紅包詳情頁面,心頭大石落地,詳情頁面寫在“0.01分錢”。小新也心滿意足地回去看微博了。
我們的自動搶紅包就是要自動幫我們完成這么一個(gè)流程:
1,獲取通知欄的信息,判斷是否紅包。
2,如果屏幕關(guān)著的,先打開屏幕。
3,點(diǎn)擊通知,進(jìn)入聊天界面
4,點(diǎn)擊紅包信息
5,點(diǎn)擊紅包中的“開”
6,返回主界面,安安靜靜了無痕跡。
先上github的地址。我是地址;大家可以直接去看代碼。代碼很簡單。無非就是遍歷找控件。
1, 得到通知欄信息
通知欄事件
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED
可以通過event.getText()方法得到通知欄的文字。然后在與“[微信紅包]”做對比,判斷是否微信紅包。
2,
打開屏幕。如果是有屏幕鎖的。那我就沒辦法了哎,,知道的童鞋請賜教。
/**
* 解鎖
*/
private void wakeAndUnlock() {
//獲取電源管理器對象
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
//獲取PowerManager.WakeLock對象,后面的參數(shù)|表示同時(shí)傳入兩個(gè)值,最后的是調(diào)試用的Tag
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "bright");
//點(diǎn)亮屏幕
wl.acquire(1000);
//得到鍵盤鎖管理器對象
KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
kl = km.newKeyguardLock("unLock");
//解鎖
kl.disableKeyguard();
}
記得釋放鍵盤管理器
kl.reenableKeyguard();
3,通過通知欄進(jìn)入微信聊天界面。
//打開微信聊天頁面
private void openWeichaPage(AccessibilityEvent event) {
if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
//得到通知的對象
Notification notification = (Notification) event.getParcelableData();
//得到通知欄的信息
// String content = notification.tickerText.toString();
// String name = content.substring(0, content.indexOf(":"));
// String scontent = content.substring(content.indexOf(":"), content.length());
// Log.d("mylog", "------openWeichaPage name: " + name + " content: " + scontent);
//打開通知欄的intent,即打開對應(yīng)的聊天界面
PendingIntent pendingIntent = notification.contentIntent;
try {
pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}
}
4,我們需要監(jiān)聽頁面的變化加自己定義變量來判斷是否跳轉(zhuǎn)到了微信聊天頁面。
頁面跳轉(zhuǎn)的事件是:
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
微信聊天頁面的classname:
com.tencent.mm.ui.LauncherUI
判斷的方法:
String className = event.getClassName().toString();
//是否微信聊天頁面的類
if (className.equals(LAUCHER)) {
findStuff();
}
然后就是做遍歷,大體思路是調(diào)用getChild(i)找到聊天頁面中的紅包。可以先通過getClassName()比較是否“android.widget.TextView”,然后通過getText()匹配文本內(nèi)容。
具體方法不表。大家可以自己寫寫,不想寫可以看GitHub上我寫的代碼。
5,和上面的方法一直,判斷窗口跳轉(zhuǎn),加遍歷找到“開”字,對,開 也是一個(gè)TextView。
6,至此就大功告成,可以返回桌面了。
/**
* 回到系統(tǒng)桌面
*/
private void back2Home() {
Intent home = new Intent(Intent.ACTION_MAIN);
home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
home.addCategory(Intent.CATEGORY_HOME);
startActivity(home);
}
我們的搶紅包之旅到這里就告一段落了,結(jié)束的是一次艱苦的擼代碼的時(shí)光,開啟的是千千萬萬次自動搶紅包的快感。
最后,祝大家新年 大 吉 吧!