本文主要介紹輔助功能的使用node
輔助功能(AccessibilityService)實際上是一個Android系統提供給的一種服務,自己是繼承Service類的。這個服務提供了加強的用戶界面,旨在幫助殘障人士或者可能暫時沒法與設備充分交互的人們。android
從開發者的角度看,其實就是提供兩種功能:查找界面元素,實現模擬點擊。實現一個輔助功能服務要求繼承AccessibilityService類並實現它的抽象方法。自定義一個服務類AccessibilitySampleService(這個命名能夠隨意),繼承系統的AccessibilityService並覆寫onAccessibilityEvent和onInterrupt方法。編寫好服務類以後,在系統配置文件(AndroidManifest.xml)中註冊服務。完成前面兩個步驟就完成了基本發輔助功能服務註冊與配置,具體的功能實現須要在onAccessibilityEvent中完成,根據onAccessibilityEvent回調方法傳遞過來的AccessibilityEvent對象能夠對事件進行過濾,結合AccessibilitySampleService自己提供的查找節點與模擬點擊相關的接口便可實現權限節點的查找與點擊。
git
import android.accessibilityservice.AccessibilityService; import android.view.accessibility.AccessibilityEvent; import com.accessibility.utils.AccessibilityLog; public class AccessibilitySampleService extends AccessibilityService { @Override protected void onServiceConnected() { super.onServiceConnected(); } @Override public void onAccessibilityEvent(AccessibilityEvent event) { // 此方法是在主線程中回調過來的,因此消息是阻塞執行的 // 獲取包名 String pkgName = event.getPackageName().toString(); int eventType = event.getEventType(); // AccessibilityOperator封裝了輔助功能的界面查找與模擬點擊事件等操做 AccessibilityOperator.getInstance().updateEvent(this, event); AccessibilityLog.printLog("eventType: " + eventType + " pkgName: " + pkgName); switch (eventType) { case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: break; } } @Override public void onInterrupt() { } }
// 註冊輔助功能服務 <service android:name=".AccessibilitySampleService" android:label="@string/accessibility_tip" android:exported="true" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" android:process=":BackgroundService"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> // 經過xml文件完成輔助功能相關配置,也能夠在onServiceConnected中動態配置 <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_config"/> </service>
上面android:label="@string/accessibility_tip"是配置此輔助功能服務在系統輔助功能頁面裏面顯示的名字。github
accessibility_config文件內容以下:api
<?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeAllMask" android:accessibilityFeedbackType="feedbackGeneric" android:canRetrieveWindowContent="true" android:description="@string/accessibility_desc" android:notificationTimeout="100" />
完成上面配置以後,輔助功能服務就註冊成功了,在系統輔助功能頁面就能找到這個服務,可是默認是關閉的,也就是說,這個服務要開始爲咱們服務,還須要去系統界面開啓那個開關。下面是跳轉到輔助功能頁面的代碼,跳轉過去以後,手動點擊開關按鈕。開關打開以後,這個輔助功能服務就開始工做了,系統開始回調onAccessibilityEvent方法。咱們能夠在onAccessibilityEvent方法中處理查找節點與點擊操做。框架
public class OpenAccessibilitySettingHelper { private static final String ACTION = "action"; private static final String ACTION_START_ACCESSIBILITY_SETTING = "action_start_accessibility_setting"; public static void jumpToSettingPage(Context context) { try { Intent intent = new Intent(context, AccessibilityOpenHelperActivity.class); intent.putExtra(ACTION, ACTION_START_ACCESSIBILITY_SETTING); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } catch (Exception ignore) {} } }
下圖是小米手機開啓輔助功能的界面ide
AccessibilityOperator封裝了輔助功能的界面查找與模擬點擊事件等操做,下面介紹幾個關鍵的技術點。動畫
界面節點查找操做ui
AccessibilityNodeInfo提供兩種查找View節點的方法this
1. 根據View的ID精確查找,可是要求SDK_INT >= 18才能用
/** * 根據View的ID搜索符合條件的節點,精確搜索方式; * 這個只適用於本身寫的界面,由於ID可能重複 * api要求18及以上 * @param viewId */ public List<AccessibilityNodeInfo> findNodesById(String viewId) { AccessibilityNodeInfo nodeInfo = getRootNodeInfo(); if (nodeInfo != null) { if (Build.VERSION.SDK_INT >= 18) { return nodeInfo.findAccessibilityNodeInfosByViewId(viewId); } } return null; }
2. 根據View的Text文本進行模糊查找
/** * 根據Text搜索全部符合條件的節點, 模糊搜索方式 */ public List<AccessibilityNodeInfo> findNodesByText(String text) { AccessibilityNodeInfo nodeInfo = getRootNodeInfo(); if (nodeInfo != null) { return nodeInfo.findAccessibilityNodeInfosByText(text); } return null; }
模擬界面操做
1. 普通的View事件模擬(ACTION_CLICK)
private boolean performClick(List<AccessibilityNodeInfo> nodeInfos) { if (nodeInfos != null && !nodeInfos.isEmpty()) { AccessibilityNodeInfo node; for (int i = 0; i < nodeInfos.size(); i++) { node = nodeInfos.get(i); // 得到點擊View的類型 AccessibilityLog.printLog("View類型:" + node.getClassName()); // 進行模擬點擊 if (node.isEnabled()) { return node.performAction(AccessibilityNodeInfo.ACTION_CLICK); } } } return false; }
2. 全局事件模擬(返回鍵:AccessibilityService.GLOBAL_ACTION_BACK)
public boolean clickBackKey() { return performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK); } private boolean performGlobalAction(int action) { return mAccessibilityService.performGlobalAction(action); }
https://github.com/PopFisher/AccessibilitySample