Android輔助功能 Accessibility Services基本用法筆記
概述
Accessbility 又叫做輔助功能,是Android官方推出幫助身體不便或者操作不靈活的人來輔助操作手機應用的。當然也可以用來幹一些別的事,比如自動搶紅包啊,靜默安裝app,幫助用於開一系列許可權操作等。出於專案需求,大概研究了下Accessilibity的基本用法。
Accessibility用法
實現自己的輔助功能類
要實現自己的輔助功能,需要繼承系統的AccessibilityService服務類。然後實現其中的抽象方法,這些方法被系統呼叫的順序是:當輔助功能服務開始時onServiceConnected()方法會被呼叫,當輔助功能正在執行時onAccessibilityEvent(), onInterrupt()方法會被呼叫,當輔助功能結束時onUnbind()方法會被呼叫。以上也就是輔助功能的生命週期了。
- onServiceConnected() (可選的方法)系統會在成功連線上你的服務的時候呼叫這個方法,在這個方法裡你可以做一下初始化工作,例如裝置的聲音震動管理,也可以呼叫setServiceInfo()進行配置工作。
- onAccessibilityEvent() - (必須實現的方法) 通過這個函式可以接收系統傳送來的AccessibilityEvent,接收來的AccessibilityEvent是經過過濾的,過濾是在配置工作時設定的。在整個生命週期裡會被呼叫多次
- onInterrupt() - (required) 這個在系統想要中斷AccessibilityService返給的響應時會呼叫。在整個生命週期裡會被呼叫多次。
- onUnbind() - (optional) 在系統將要關閉這個AccessibilityService會被呼叫。在這個方法中進行一些釋放資源的工作。
Manifest 註冊和許可權申明
想要實現自己的輔助功能,你需要在Manifest配置檔案中註冊該服務,且申明相應的許可權。如此,你才能接收到系統的服務功能服務。
Accessibility service 的申明
為了實現輔助功能,你需要在Manifest中新增如下程式碼:
<application>
<service android:name=".MyAccessibilityService"
android:label ="@string/accessibility_service_label"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
</service>
</application>
注意 1. action 是固定的“android.accessibilityservice.AccessibilityService”用於系統啟動我們的輔助功能
注意2. 你必須給你的輔助功能服務申請許可權 android:permission=”android.permission.BIND_ACCESSIBILITY_SERVICE”,否者不能使用輔助功能。
Accessibility service 配置
輔助功能必須指定一個輔助事件配置檔案來處理和過濾指定的事件。輔助功能配置可以由AccessibilityServiceInfo來實現,在輔助功能服務的onServiceConnected()方法中呼叫AccessibilityServiceInfo類的setServiceInfo()方法來配置。
當然從Android 4.0開始,你也可以在manifest中新增 標籤來未輔助功能指定一個xml的配置。程式碼如下:
<service android:name=".MyAccessibilityService">
...
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config" />
</service>
標籤指向一個XML資原始檔,檔案路徑(/res/xml/accessibility_service_config.xml). 示例程式碼如下:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/accessibility_service_description"
android:packageNames="com.example.android.apis"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFlags="flagDefault"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
/>
如上配置可以指定該輔助功能監聽哪個應用的操作,允許處理哪些事件,使用者點選事件反饋的型別,查詢節點方式,超時時間,是否允許輔助功能獲得視窗節點資訊等。配置檔案屬性解釋如下:
- android:description :輔助功能描述,描述該輔助功能用來幹嘛的
- android:packageNames :指定輔助功能監聽的包名,不指定表示監聽所有應用
- android:accessibilityEventTypes:輔助功能處理事件型別,一般配置為typeAllMask表示接收所有事件
- android:accessibilityFlags:輔助功能查詢截點方式,一般配置為flagDefault預設方式。
- android:accessibilityFeedbackType:操作相應按鈕以後輔助功能給使用者的反饋型別,包括聲音,震動等。
- android:notificationTimeout:相應時間設定
- android:canRetrieveWindowContent:是否允許輔助功能獲得視窗的節點資訊,為了能正常實用輔助功能,請務必保持該項配置為true
從一個AccessibilityEvent中調查完全檢視層級的能力隱式地暴露私有使用者資訊給你的無障礙服務。出於這個原因,你的服務必須通過無障礙服務配置XML檔案請求這個級別的訪問權,通過包含canRetrieveWindowContent屬性和設定它為true。如果你不在你的服務配置xml檔案中包含這個設定,那麼對getSource()的呼叫會失敗。
想要了解更多更詳細的配置,請閱讀AccessibilityServiceInfo類。該類就是用於輔助功能服務配置資訊類。
開啟輔助功能開關
如下程式碼判斷輔助功能是否開啟,如果沒有開啟,則跳轉到系統頁面去開啟。
/**
* 該輔助功能開關是否打開了
* @param accessibilityServiceName:指定輔助服務名字
* @param context:上下文
* @return
*/
private boolean isAccessibilitySettingsOn(String accessibilityServiceName, Context context) {
int accessibilityEnable = 0;
String serviceName = context.getPackageName() + "/" +accessibilityServiceName;
try {
accessibilityEnable = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0);
} catch (Exception e) {
Log.e(TAG, "get accessibility enable failed, the err:" + e.getMessage());
}
if (accessibilityEnable == 1) {
TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
String settingValue = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
mStringColonSplitter.setString(settingValue);
while (mStringColonSplitter.hasNext()) {
String accessibilityService = mStringColonSplitter.next();
if (accessibilityService.equalsIgnoreCase(serviceName)) {
Log.v(TAG, "We've found the correct setting - accessibility is switched on!");
return true;
}
}
}
}else {
Log.d(TAG,"Accessibility service disable");
}
return false;
}
/**
* 跳轉到系統設定頁面開啟輔助功能
* @param accessibilityServiceName:指定輔助服務名字
* @param context:上下文
*/
private void openAccessibility(String accessibilityServiceName, Context context){
if (!isAccessibilitySettingsOn(accessibilityServiceName,context)) {
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivity(intent);
}
}
使用輔助功能查詢節點
首先我們可以通過如下方法獲取當前頁面節點資訊AccessibilityNodeInfo:
- AccessibilityEvent#getSource :得到AccessibilityNodeInfo事件來源
- AccessibilityService#getRootInActiveWindow :得到當前視窗根節點所有資訊
以上兩個方法都可以獲取到當前頁面的節點資訊,然後我們可以通過AccessibilityNodeInfo來查詢具體的View,然後對該View操作。查詢View有兩如下兩種方法:
- AccessibilityNodeInfo#findAccessibilityNodeInfosByText():通過Text查詢
- AccessibilityNodeInfo#findAccessibilityNodeInfosByViewId():通過Id查詢
我們一般都通過指定的Text內容來查詢,這樣查詢方便,簡單
使用輔助功能模擬點選
我們通過AccessibilityNodeInfo#findAccessibilityNodeInfosByText()查詢到指定的node節點後,就可以通過相應的方法模擬點選事件了。方法如下:
- AccessibilityNodeInfo#performAction() :執行使用者行為操作
比如點選事件程式碼如下:
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
使用者常用的行為事件型別一般有如下:
- ACTION_CLICK:模擬點選
- ACTION_SELECT:模擬選中
- ACTION_LONG_CLICK:模擬長按
- ACTION_SCROLL_FORWARD:模擬往前滾動
總結:
雖然很多手機支援輔助功能,但是由於各手機廠商定製或者自定義View,沒有嚴格按照Google標準來,使得有些介面使用了自定義的View,導致輔助功能找不到其節點,從而使得即使有輔助功能,也不能模擬點選View操作。