中文 | English
安卓无障碍服务Api, 为了简化无障碍服务使用,并使用 Kotlin 以提供简洁的Api。
由于4.0版本代码进行重构,之前接入的项目需要修改 import package
, 并且 implementation 'com.github.Krosxx.Android-Accessibility-Api:accessibility-api:4.0.0'
。UiAutomator 可参考 Demo App。
最新 library : implementation 'com.github.Krosxx:Android-Auto-Api:Tag'
(图片加载过慢可到 Gitee 查看)
更多操作:
方法 | 说明 |
---|---|
lockScreen() | 锁屏,需要Android P |
screenShot() | 触发系统截屏,需要Android P |
splitScreen() | 触发系统分屏,需要Android P |
requireBaseAccessibility()
//使用 ScreenTextFinder() 来搜索屏幕上的文字
val ts = ScreenTextFinder().find().joinToString("\n\n")
withContext(Dispatchers.Main) {
AlertDialog.Builder(act).apply {
setTitle("提取文字:")
setMessage(ts)
show()
}
}
ViewFinder
, 并封装一个 ViewFinderWithMultiCondition
来指定搜索条件,实现快速搜索;查看所有方法:view_finder_api.ktSmartFinder
,支持多条件(AND, OR)搜索,扩展性极高 ViewFinder
主要方法:
注意:在3.0.0
及之后版本,搜索方法需在协程作用域内使用,若需要java
内调用请使用2.1.1
版本
方法 | 说明 |
---|---|
findFirst(includeInvisible: Boolean = false): ViewNode? | 立即搜索,返回满足条件的第一个结果 includeInvisible: 是否包含不可见元素 |
findAll(includeInvisible: Boolean = false): Array\ |
立即搜索,返回满足条件的所有结果 |
waitFor(waitMillis: Long = 30000): ViewNode? | 等待搜索,在指定时间内循环搜索(视图更新),超时返回null |
require(waitMillis: Long = WAIT_MILLIS): ViewNode | 等待超时抛出异常 |
findByDepths(vararg depths: Int): ViewNode? | 指定深度索引搜索 |
exist(): Boolean | 是否存在 (findFirst() != null) |
attachCoroutine() | 支持协程调用,支持cancel()打断搜索 |
示例1: 等待 Chrome 打开 > 展开菜单
//等待无障碍开启 默认时间30s,超时将抛出异常
waitBaseAccessibility()
toast("start chrome after 1s")
delay(1000)
//打开Chrome
val targetApp = "com.android.chrome"
act.startActivity(act.packageManager.getLaunchIntentForPackage(targetApp))
//等待页面
if (
waitForApp(targetApp, 5000).also {
toast("wait " + if (it) "success" else "failed")
}
) {
//id 搜索,点击打开菜单
withId("menu_button").tryClick()
}
示例2: 文本操作
搜索所有可点击的视图:
requireBaseAccessibility()
//自定义条件搜索
val s = findAllWith { it: AccessibilityNodeInfo ->
it.isClickable
}.joinToString("\n\n")
withContext(Dispatchers.Main) {
AlertDialog.Builder(act).apply {
setTitle("可点击节点:")
setMessage(s)
show()
}
}
扩展性极高,支持多条件(AND, OR)搜索
更多已支持的条件见:SmartFinderConditions.kt
//SF 为 SmartFinder 缩写
SF.text("SmartFinder测试").findFirst()
//等效:
SF.where(_text eq "SmartFinder测试").findFirst()
//搜索 文本为123 或者 id为text1
SF.text("123").or().id("text1").findFirst()
原始AND,OR
//搜索 所有isChecked
SF.where {
it.isChecked
}.find()
SF.where(IdCondition("view_id")).or(RTextEqCondition("[0-9]+")).find()
//等效:
SF.id("view_id").or().matchText("[0-9]+").find()
支持Group
// (text=="111" && desc=="111") || (text=="222" && desc=="222")
SF.where(SF.text("111").desc("111"))
.or(SF.text("222").desc("222"))
.find()
中缀表达式
//使用中缀表达式
(SF where text("1111") or text("2222")
and id("111") or longClickable()).findAll()
添加 attachCoroutine
方法,搜索等待可及时中断
fun run(act: Activity) = runBlocking {
val outterJob = coroutineContext[Job]
val searchJob = GlobalScope.async {
val t = SF.attachCoroutine()//attach当前协程上下文,需要主动调用
.containsText("周三").waitFor(10000)
AlertDialog.Builder(act).apply {
setMessage(t.toString())
withContext(Dispatchers.Main) {
show()
}
}
}
searchJob.invokeOnCompletion {
outterJob?.cancel()
}
delay(3000)
//取消搜索测试
searchJob.cancel()
}
封装自定义搜索条件,使调用起来更简洁 库中搜索条件全部实现位于
SmartFinderConditions.kt
例如 定义使用正则匹配Node文本
class TextMatcherCondition(private val regex: String) : MatchCondition {
//此处注意直接创建Regex,防止在搜索时重复创建;另外可直接检查正则表达式有效性
private val reg = regex.toRegex()
override fun invoke(node: AcsNode) =
node.text?.toString()?.let {
reg.matches(it)
} ?: false
}
此时,已经可以这样使用:
SF.where(TextMatcherCondition("[0-9]+")).findAll()
追究简洁,可进行扩展方法:
fun ConditionGroup.matchText(reg: String) = link(TextMatcherCondition(reg))
之后可简化调用
SF.matchText("[0-9]+").findAll()
根据ViewFinder
搜索得到 ViewNode
,可进行的操作详见接口:ViewOperation.kt
全局手势可以点击/长按任意坐标,执行路径手势。
手势Api全部需要Android N+;代码必须执行于非主线程
由于部分系统版本启动支持手势的无障碍服务会造成系统卡顿(掉帧),所以本库分为两个服务来设计(若无需手势功能,可不实现手势服务)。
allprojects {
repositories {
//...
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.Krosxx:Android-Auto-Api:Tag'
}
注意:在3.0.0
及之后版本,搜索方法需在协程作用域内使用,若需要java
内调用请使用2.1.1
版本
用来支持 布局检索,视图操作
<service
android:name=".service.BaseAccessibilityService"
android:description="@string/base_ser_desc"
android:label="BaseService Demo"
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/base_accessibility_config" />
</service>
用于执行手势,Android N+可用
<service
android:name=".service.GestureAccessibilityService"
android:description="@string/ges_ser_desc"
android:label="Gesture Service Demo"
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/gesture_accessibility_config" />
</service>
在 Application 中初始化:
指定 BASE_SERVICE_CLS
及 GESTURE_SERVICE_CLS
override fun onCreate() {
super.onCreate()
AccessibilityApi.init(this,
BaseAccessibilityService::class.java,
GestureAccessibilityService::class.java)
}
如果你想使用一个服务来完成,可使用如下配置
class AppAccessibilityService : AccessibilityApi() {
//启用 页面更新 回调
override val enableListenAppScope: Boolean = true
//页面更新回调
override fun onPageUpdate(currentScope: AppScope) {
Log.d("TAG", "onPageUpdate: $currentScope")
}
}
<service
android:name=".service.AppAccessibilityService"
android:description="@string/ser_desc"
android:label="Service Demo"
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/accessibility_config" />
</service>
res/xml/accessibility_config.xml
在 Application 中初始化:
override fun onCreate() {
super.onCreate()
AccessibilityApi.init(this, AppAccessibilityService::class.java)
}
更多 Api
可在下列文件查看