對(duì)于那些由于視力、聽(tīng)力或其它身體原因?qū)е虏荒芊奖闶褂?Android 智能手機(jī)的用戶粘勒,Android 提供了 Accessibility 功能和服務(wù)幫助這些用戶更加簡(jiǎn)單地操作設(shè)備屎即,包括文字轉(zhuǎn)語(yǔ)音、觸覺(jué)反饋技俐、手勢(shì)操作、軌跡球和手柄操作啡邑。開(kāi)發(fā)者可以搭建自己的 Accessibility 服務(wù)井赌,這可以加強(qiáng)應(yīng)用的可用性贵扰,例如聲音提示流部,物理反饋,和其他可選的操作模式枝冀。- 隨著Android系統(tǒng)版本的迭代,Accessibility功能也越來(lái)越強(qiáng)大球切,它能實(shí)時(shí)地獲取當(dāng)前操作應(yīng)用的窗口元素信息绒障,并能夠雙向交互,既能獲取用戶的輸入端盆,也能對(duì)窗口元素進(jìn)行操作费封,比如點(diǎn)擊按鈕。更多的介紹見(jiàn)Android開(kāi)發(fā)者官網(wǎng)的Accessibility頁(yè)面焚鹊。
- 話不多說(shuō)直接開(kāi)始韧献,首先使用Android Accessibility 需要三個(gè)步驟:
1、申請(qǐng)權(quán)限
2锤窑、注冊(cè)服務(wù)
3、配置 AccessibilityService Info首先需要申請(qǐng)權(quán)限
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
注冊(cè)服務(wù)
<service
android:name=".Your Accessibility Name"
android:enabled="true"
android:exported="true"
android:label="Your Service Title"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:process=":accessibility">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_config" />
</service>
配置 AccessibilityService Info
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeViewScrolled|typeWindowContentChanged|typeWindowStateChanged"
android:accessibilityFeedbackType="feedbackAllMask"
android:accessibilityFlags="flagDefault|flagReportViewIds"
android:canRetrieveWindowContent="true"
android:description="@string/auto_service_des"
android:packageNames="com.android.packageinstaller,com.google.android.packageinstaller,com.samsung.android.packageinstaller,com.lenovo.safecenter,com.lenovo.security" />
在這里需要注意的是packageNames是AccessibilityService所監(jiān)聽(tīng)的應(yīng)用的包名』嬷ぃ可以監(jiān)聽(tīng)多個(gè),在自動(dòng)安裝的時(shí)候箭筒不同的包名用于做適配胞枕。源碼中會(huì)有所要適配的包名魏宽,包含了大部分安裝程序的包名决乎。該程序?qū)崿F(xiàn)自動(dòng)裝的原理分析:首先我們可以用 getRootInActiveWindow()派桩,和event.getSource()均可以得到AccessibilityNodeInfo的實(shí)例,即為觸發(fā)這次事件的UI節(jié)點(diǎn)唤反。
重寫AccessibilityService服務(wù)鸭津,實(shí)現(xiàn)onAccessibilityEvent方法,該方法是監(jiān)聽(tīng)服務(wù)監(jiān)聽(tīng)到界面變化會(huì)調(diào)用因此逆趋,我們從該方法去做實(shí)現(xiàn)我們的自動(dòng)安裝功能。
那么我們?nèi)绾握业経I元素呢名斟?
1魄眉、findAccessibilityNodeInfosByText(String text) 該方法可以根據(jù)控件顯示的文本得到控件。所注意的是該方法的邏輯是包含(contains)而不是等于(equal)岩梳。
例如:參數(shù)我們傳遞 "安裝" 晃择,那么像,"是否安裝宫屠?","安裝"抵栈,都會(huì)得到乌逐,所以需要我們?nèi)ヌ幚怼W詈笪覀兪褂媚M用戶點(diǎn)擊實(shí)現(xiàn)自動(dòng)點(diǎn)擊效果
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
實(shí)現(xiàn)的基本流程就是這樣浙踢,但是這只是剛剛開(kāi)始,我們需要更嚴(yán)格的邏輯去處理胰舆,現(xiàn)在可以看下onAccessibilityEvent方法我是怎么實(shí)現(xiàn)的,
private void doAccessibilityEvent(AccessibilityEvent event) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
String className = event.getClassName().toString();
if (uninstallPkgSet.contains(className)) {
isInstallOrUninstall = false;
}
if(installViewSet.contains(event.getClassName().toString())) {
isInstallOrUninstall = true;
}
if (installViewSet.contains(event.getPackageName().toString())) {
isInstallOrUninstall = true;
}
AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
if (rootNodeInfo != null && isInstallOrUninstall) {
String pkgName = (String) rootNodeInfo.getPackageName();
if (installPkgSet.contains(pkgName)) {
for (int i = 0; i < nodeContents.size(); i++) {
List<AccessibilityNodeInfo> textNodeInfo = new ArrayList<>();
for (int k = 0; k < completeTexts.size(); k++) {
textNodeInfo.addAll(rootNodeInfo.findAccessibilityNodeInfosByText(completeTexts.get(k)));
}
if (textNodeInfo.size() > 0) {
for (int j = 0; j < textNodeInfo.size(); j++) {
String text = textNodeInfo.get(j).getText().toString();
if (completeTexts.contains(text)) {
clickInstall(textNodeInfo.get(j));
}
}
}
}
}
}
AccessibilityNodeInfo nodeInfo = event.getSource();
if (nodeInfo != null && isInstallOrUninstall) {
for (int i = 0; i < nodeContents.size(); i++) {
List<AccessibilityNodeInfo> textNodeInfo = nodeInfo.findAccessibilityNodeInfosByText(nodeContents.get(i));
List<AccessibilityNodeInfo> installNodeInfo = new ArrayList<>();
for (int k = 0; k < completeTexts.size(); k++) {
installNodeInfo.addAll(nodeInfo.findAccessibilityNodeInfosByText(installTexts.get(k)));
}
boolean isInstall = installNodeInfo.size() != 0;
if (textNodeInfo != null && textNodeInfo.size() > 0) {
for (int j = 0; j < textNodeInfo.size(); j++) {
String text = textNodeInfo.get(j).getText().toString();
if (nodeContents.contains(text) && isInstall) {
clickInstall(textNodeInfo.get(j));
}
}
}
}
}
}}
在這里我是根據(jù)豌豆莢所兼容的android手機(jī)都進(jìn)行了兼容處理,并對(duì)多語(yǔ)言進(jìn)行處理误续。大家看到我對(duì)單個(gè)文字也進(jìn)行了list話不理解的話扫茅,想想多語(yǔ)言,估計(jì)就理解了葫隙。目前測(cè)試國(guó)內(nèi)及國(guó)外手機(jī)幾乎都能實(shí)現(xiàn)自動(dòng)裝。目前有一個(gè)AppInstall的管理類腺办,實(shí)現(xiàn)了Root安裝糟描,及accessibility安裝的管理。讓使用起來(lái)更加方便蚓挤。
項(xiàng)目地址