实现自定义的无障碍服务
自定义一个服务继承自AccessibilityService
package com.android.jarvis.accessibility import android.accessibilityservice.AccessibilityService import android.accessibilityservice.AccessibilityServiceInfo import android.util.Log import android.view.KeyEvent import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo class JarvisAccessibilityService : AccessibilityService() { public override fun onServiceConnected() { Log.i(TAG, "onServiceConnected: ") val accessibilityServiceInfo = AccessibilityServiceInfo() accessibilityServiceInfo.packageNames = null // 监听所有应用 accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK //监听哪些行为 accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_ALL_MASK //反馈 accessibilityServiceInfo.notificationTimeout = 200 serviceInfo = accessibilityServiceInfo } override fun onAccessibilityEvent(event: AccessibilityEvent) { traverseNode(event.source) processAccessibilityEvent(event) } private fun processAccessibilityEvent(event: AccessibilityEvent) { processBlockingNotification(event) if (rootInActiveWindow == null) { Log.i(TAG, "AccessibilityNodeInfo = null") return } traverseNode(rootInActiveWindow) } private fun processBlockingNotification(event: AccessibilityEvent) { val node = event.source if (node != null) { if (findblockingUI("是否允许 USB 调试?", node)) { findAndPerformCheck("始终允许使用这台计算机进行调试", node) findAndPerformAction("确定", node) } if (findblockingUI("是否允许USB调试?", node)) { findAndPerformCheck("始终允许使用这台计算机进行调试", node) findAndPerformAction("确定", node) } if (findblockingUI("允许USB调试吗?", node)) { findAndPerformCheck("一律允许使用这台计算机进行调试", node) findAndPerformAction("确定", node) } if (findblockingUI("允许 USB 调试吗?", node)) { findAndPerformCheck("一律允许使用这台计算机进行调试", node) findAndPerformAction("允许", node) } if (findblockingUI("允许 USB 调试吗?", node)) { findAndPerformCheck("一律允许使用这台计算机进行调试", node) findAndPerformAction("确定", node) } } } private fun findblockingUI(text: String, source: AccessibilityNodeInfo): Boolean { val nodes = source.findAccessibilityNodeInfosByText(text) if (nodes == null || nodes.isEmpty()) { return false } Log.d(TAG, "findblockingUI $text") return true } public override fun onKeyEvent(event: KeyEvent): Boolean { return true } override fun onInterrupt() { Log.e(TAG, "服务被Interrupt") } private fun traverseNode(node: AccessibilityNodeInfo?) { if (node != null) { val count = node.childCount if (count > 0) { for (i in 0 until count) { traverseNode(node.getChild(i)) } return } val clickable = node.isClickable val text = node.text val pkgName = node.packageName if (!"com.miui.home".contentEquals(pkgName)) { Log.i( TAG, "pkg:" + pkgName as Any + " Node:" + text as Any + " clickable:" + clickable ) } } } private fun findAndPerformAction(text: String, source: AccessibilityNodeInfo?): Int { if (source == null) { return 0 } val nodes = source.findAccessibilityNodeInfosByText(text) var count = 0 if (nodes != null && !nodes.isEmpty()) { for (i in nodes.indices) { if (performActionClick(nodes[i], text)) { count++ } } } return count } private fun performActionClick(node: AccessibilityNodeInfo?, text: String): Boolean { if (node == null) { return false } if (!isButton(node) && !isTextView(node) && !isView(node)) { return false } node.performAction(16) return true } private fun findAndPerformCheck(text: String, source: AccessibilityNodeInfo?) { if (source != null) { val nodes = source.findAccessibilityNodeInfosByText(text) if (nodes != null && !nodes.isEmpty()) { for (i in nodes.indices) { Log.d(TAG, "performCheck $text") performActionCheck(nodes[i]) } } } } private fun performActionCheck(node: AccessibilityNodeInfo?) { if (node != null && isCheckBox(node) && !node.isChecked) { node.performAction(16) } } private fun isButton(node: AccessibilityNodeInfo): Boolean { return node.className == "android.widget.Button" || node.className == "amigo.widget.AmigoButton" } private fun isTextView(node: AccessibilityNodeInfo): Boolean { return node.className == "android.widget.TextView" } private fun isView(node: AccessibilityNodeInfo): Boolean { return node.className == "android.widget.View" } private fun isCheckBox(node: AccessibilityNodeInfo): Boolean { return node.className == "android.widget.CheckBox" } private fun isCheckedTextView(node: AccessibilityNodeInfo): Boolean { return node.className == "android.widget.CheckedTextView" } companion object { private const val TAG = "JarvisAccessibility" } }
配置
在res/xml目录下新建accessibility_service_config.xml文件,如下:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/accessibility_service_description" android:accessibilityEventTypes="typeViewClicked|typeViewLongClicked|typeViewSelected|typeViewFocused|typeViewTextChanged|typeWindowStateChanged|typeNotificationStateChanged|typeViewHoverEnter|typeViewHoverExit|typeTouchExplorationGestureStart|typeTouchExplorationGestureEnd|typeWindowContentChanged|typeViewScrolled|typeViewTextSelectionChanged|typeAllMask" android:accessibilityFlags="flagDefault" android:accessibilityFeedbackType="feedbackGeneric" android:canRetrieveWindowContent="true" android:notificationTimeout="200" />
在AndroidManifest.xml中注册服务
<service android:name=".accessibility.JarvisAccessibilityService" android:label="智能辅助服务" android:enabled="true" android:exported="true" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter android:priority="2147483647"> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service>
自动开启无障碍服务
可以通过执行下面的命令就可以自动开启指定的无障碍服务:
adb shell content call --uri content://settings/secure --method PUT_secure --arg enabled_accessibility_services --extra _user:i:0 --extra value:s:com.android.jarvis/com.android.jarvis.accessibility.JarvisAccessibilityService adb shell content call --uri content://settings/secure --method PUT_secure --arg accessibility_enabled --extra _user:i:0 --extra value:s:1 adb shell settings put secure enabled_accessibility_services com.android.jarvis/com.android.jarvis.accessibility.JarvisAccessibilityService adb shell settings put secure accessibility_enabled 1