From 8b2d6d2b15e37f52dc41d4edf5a5f4835ecf8960 Mon Sep 17 00:00:00 2001
From: Libra <1132412166@qq.com>
Date: Mon, 19 Dec 2022 23:04:09 +0800
Subject: [PATCH] update finder api with coroutine
---
.idea/codeStyles/codeStyleConfig.xml | 2 +-
.idea/compiler.xml | 2 +-
.idea/gradle.xml | 1 -
.../AccessibilityApi.kt | 2 +-
.../andro_accessibility_api/api/nav_api.kt | 19 ++--
.../api/view_finder_api.kt | 16 ++--
.../andro_accessibility_api/utils/utils.kt | 24 +++--
.../viewfinder/FinderBuilderWithOperation.kt | 3 +-
.../viewfinder/ViewFinder.kt | 90 ++++++++-----------
.../demo/MainActivity.kt | 1 +
.../demo/actions/actoins.kt | 9 +-
11 files changed, 79 insertions(+), 90 deletions(-)
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
index 79ee123..a55e7a1 100644
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index fb7f4a8..b589d56 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index c74e703..d87cedf 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -15,7 +15,6 @@
-
diff --git a/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/AccessibilityApi.kt b/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/AccessibilityApi.kt
index 0efbd90..8c8c333 100644
--- a/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/AccessibilityApi.kt
+++ b/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/AccessibilityApi.kt
@@ -179,7 +179,7 @@ abstract class AccessibilityApi : AccessibilityService(), BaseServiceApi {
@JvmOverloads
@JvmStatic
@Throws(NeedAccessibilityException::class)
- fun waitAccessibility(waitMillis: Long = 30000, cls: Class<*>): Boolean {
+ suspend fun waitAccessibility(waitMillis: Long = 30000, cls: Class<*>): Boolean {
val se = if (cls == BASE_SERVICE_CLS) isBaseServiceEnable
else isGestureServiceEnable
diff --git a/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/api/nav_api.kt b/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/api/nav_api.kt
index 018e510..2b36609 100644
--- a/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/api/nav_api.kt
+++ b/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/api/nav_api.kt
@@ -5,6 +5,7 @@ import androidx.annotation.RequiresApi
import cn.vove7.andro_accessibility_api.AccessibilityApi
import cn.vove7.andro_accessibility_api.AppScope
import cn.vove7.andro_accessibility_api.utils.whileWaitTime
+import kotlinx.coroutines.delay
import java.lang.Thread.sleep
import kotlin.math.min
@@ -37,18 +38,14 @@ fun screenShot(): Boolean = AccessibilityApi.requireBase.screenShot()
@RequiresApi(Build.VERSION_CODES.N)
fun splitScreen(): Boolean = AccessibilityApi.requireBase.splitScreen()
-fun waitForApp(pkg: String, waitTime: Long = 30000): Boolean {
- if (!AccessibilityApi.isBaseServiceEnable) {
- return false
- }
+suspend fun waitForApp(pkg: String, waitTime: Long = 30000): Boolean {
return waitForPage(AppScope(pkg, ""), waitTime)
}
-fun waitForPage(scope: AppScope, waitTime: Long = 30000): Boolean {
- return whileWaitTime(min(waitTime, 30000)) {
- if (AccessibilityApi.currentScope == scope) true else {
- sleep(100)
- null
- }
+suspend fun waitForPage(scope: AppScope, waitTime: Long = 30000): Boolean {
+ requireBaseAccessibility()
+ return whileWaitTime(min(waitTime, 30000), 100) {
+ if (AccessibilityApi.currentScope == scope) true
+ else null
} ?: false
-}
\ No newline at end of file
+}
diff --git a/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/api/view_finder_api.kt b/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/api/view_finder_api.kt
index 35ba00f..a8840b8 100644
--- a/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/api/view_finder_api.kt
+++ b/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/api/view_finder_api.kt
@@ -24,7 +24,7 @@ fun requireBaseAccessibility(autoJump: Boolean = false) {
AccessibilityApi.requireBaseAccessibility(autoJump)
}
-fun waitBaseAccessibility(waitMillis: Long = 30000) {
+suspend fun waitBaseAccessibility(waitMillis: Long = 30000) {
AccessibilityApi.waitAccessibility(waitMillis, AccessibilityApi.BASE_SERVICE_CLS)
}
@@ -32,11 +32,11 @@ fun requireGestureAccessibility(autoJump: Boolean = false) {
AccessibilityApi.requireGestureAccessibility(autoJump)
}
-fun waitGestureAccessibility(waitMillis: Long = 30000) {
+suspend fun waitGestureAccessibility(waitMillis: Long = 30000) {
AccessibilityApi.waitAccessibility(waitMillis, AccessibilityApi.GESTURE_SERVICE_CLS)
}
-fun waitAccessibility(waitMillis: Long = 30000, cls: Class<*>): Boolean {
+suspend fun waitAccessibility(waitMillis: Long = 30000, cls: Class<*>): Boolean {
return AccessibilityApi.waitAccessibility(waitMillis, cls)
}
@@ -90,7 +90,7 @@ fun editor(): ConditionGroup {
* @param depths Array
* @return ViewFindBuilder
*/
-fun withDepths(vararg depths: Int): ViewNode? {
+suspend fun withDepths(vararg depths: Int): ViewNode? {
return ViewFinder.findByDepths(*depths)
}
@@ -139,28 +139,28 @@ private fun ViewNode.printWithChild(
}
}
-fun findWith(
+suspend fun findWith(
includeInvisible: Boolean = false,
predicate: (AccessibilityNodeInfo) -> Boolean
): ViewNode? {
return SF.where(predicate).findFirst(includeInvisible)
}
-fun findAllWith(
+suspend fun findAllWith(
includeInvisible: Boolean = false,
predicate: (AccessibilityNodeInfo) -> Boolean
): Array {
return SF.where(predicate).findAll(includeInvisible)
}
-fun ViewNode.findWith(
+suspend fun ViewNode.findWith(
includeInvisible: Boolean = false,
predicate: (AccessibilityNodeInfo) -> Boolean
): ViewNode? {
return SmartFinder(this).where(predicate).findFirst(includeInvisible)
}
-fun ViewNode.findAllWith(
+suspend fun ViewNode.findAllWith(
includeInvisible: Boolean = false,
predicate: (AccessibilityNodeInfo) -> Boolean
): Array {
diff --git a/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/utils/utils.kt b/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/utils/utils.kt
index 0d38390..12ec2fb 100644
--- a/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/utils/utils.kt
+++ b/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/utils/utils.kt
@@ -3,13 +3,14 @@ package cn.vove7.andro_accessibility_api.utils
import android.content.ComponentName
import android.content.Intent
import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
import android.os.SystemClock
import android.provider.Settings
-import android.widget.Toast
import cn.vove7.andro_accessibility_api.InitCp
-import java.util.*
+import cn.vove7.andro_accessibility_api.viewfinder.ViewFinder
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.ensureActive
+import java.util.Locale
+import kotlin.coroutines.coroutineContext
import kotlin.math.max
/**
@@ -27,19 +28,28 @@ import kotlin.math.max
* @param run () -> T 返回空时,重新执行,直到超时
* @return T
*/
-fun whileWaitTime(waitMillis: Long, run: () -> T?): T? {
+suspend fun whileWaitTime(
+ waitMillis: Long,
+ interval: Long = 0L, run: suspend () -> T?
+): T? {
val begin = SystemClock.elapsedRealtime()
- val ct = Thread.currentThread()
do {
run.invoke()?.also {
//if 耗时操作
return it
}
- } while (SystemClock.elapsedRealtime() - begin < waitMillis && !ct.isInterrupted)
+ if (interval > 0) delay(interval)
+ else ensureActive()
+ } while (SystemClock.elapsedRealtime() - begin < waitMillis)
return null
}
+internal suspend inline fun ensureActive() {
+ coroutineContext.ensureActive()
+}
+
+
fun jumpAccessibilityServiceSettings(cls: Class<*>) {
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
diff --git a/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/viewfinder/FinderBuilderWithOperation.kt b/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/viewfinder/FinderBuilderWithOperation.kt
index 56a852a..2afdc5a 100644
--- a/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/viewfinder/FinderBuilderWithOperation.kt
+++ b/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/viewfinder/FinderBuilderWithOperation.kt
@@ -3,6 +3,7 @@ package cn.vove7.andro_accessibility_api.viewfinder
import android.os.Build
import androidx.annotation.RequiresApi
import cn.vove7.andro_accessibility_api.viewnode.ViewOperation
+import kotlinx.coroutines.runBlocking
/**
* # FindBuilderWithOperation
@@ -19,7 +20,7 @@ interface FinderBuilderWithOperation : ViewOperation {
val finder: ViewFinder<*>
- private val node get() = finder.require()
+ private val node get() = runBlocking { finder.require() }
override val id get() = node.id
override val className get() = node.className
diff --git a/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/viewfinder/ViewFinder.kt b/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/viewfinder/ViewFinder.kt
index c3d5f6c..0611b44 100644
--- a/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/viewfinder/ViewFinder.kt
+++ b/accessibility-api/src/main/java/cn/vove7/andro_accessibility_api/viewfinder/ViewFinder.kt
@@ -1,14 +1,14 @@
package cn.vove7.andro_accessibility_api.viewfinder
import android.view.accessibility.AccessibilityNodeInfo
-import cn.vove7.andro_accessibility_api.AccessibilityApi
-import cn.vove7.andro_accessibility_api.utils.NeedBaseAccessibilityException
+import cn.vove7.andro_accessibility_api.api.requireBaseAccessibility
import cn.vove7.andro_accessibility_api.utils.ViewNodeNotFoundException
+import cn.vove7.andro_accessibility_api.utils.ensureActive
import cn.vove7.andro_accessibility_api.utils.whileWaitTime
import cn.vove7.andro_accessibility_api.viewfinder.FinderBuilderWithOperation.Companion.WAIT_MILLIS
import cn.vove7.andro_accessibility_api.viewnode.ViewNode
+import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
-import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.coroutineContext
/**
@@ -19,20 +19,6 @@ import kotlin.coroutines.coroutineContext
abstract class ViewFinder>(
val node: ViewNode? = null
) {
- private var coroutineCtx: CoroutineContext? = null
-
- /**
- * 协程支持
- */
- suspend fun attachCoroutine(): T {
- coroutineCtx = coroutineContext
- @Suppress("UNCHECKED_CAST")
- return this as T
- }
-
- private fun ensureActive() {
- coroutineCtx?.ensureActive()
- }
companion object {
//Maximum layout recursion depth; in rare cases, there will be a layout wireless loop to prevent function stack overflow
@@ -46,9 +32,10 @@ abstract class ViewFinder>(
* @param depths Array
* @return ViewNode?
*/
- fun findByDepths(depths: IntArray, node: ViewNode): ViewNode? {
+ suspend fun findByDepths(depths: IntArray, node: ViewNode): ViewNode? {
var p: ViewNode? = node
depths.forEach {
+ ensureActive()
try {
p = p?.childAt(it)
} catch (e: ArrayIndexOutOfBoundsException) {
@@ -61,7 +48,7 @@ abstract class ViewFinder>(
return p
}
- fun findByDepths(vararg depths: Int) = findByDepths(depths, ROOT_NODE)
+ suspend fun findByDepths(vararg depths: Int) = findByDepths(depths, ROOT_NODE)
}
val startNode: ViewNode
@@ -71,30 +58,27 @@ abstract class ViewFinder>(
* 等待搜索,在指定时间内循环搜索(视图更新),超时返回null
* 等待View出现 同步 耗时操作
* 主动搜索
- * @param m Long 时限
+ * @param waitTime Long 时限
*/
- fun waitFor(m: Long = 30000, includeInvisible: Boolean = false): ViewNode? {
- if (!AccessibilityApi.isBaseServiceEnable) throw NeedBaseAccessibilityException()
- val t = when {
- m in 0..30000 -> m
- m < 0 -> 0
+ suspend fun waitFor(
+ waitTime: Long = 30000,
+ interval: Long = 0L,
+ includeInvisible: Boolean = false
+ ): ViewNode? {
+ requireBaseAccessibility()
+ val wt = when {
+ waitTime in 0..30000 -> waitTime
+ waitTime < 0 -> 0
else -> 30000
}
val beginTime = System.currentTimeMillis()
- var sc = 0
- val ct = Thread.currentThread()
- val endTime = beginTime + t
- while (System.currentTimeMillis() < endTime &&
- !ct.isInterrupted
- ) {
- ensureActive()
+ val endTime = beginTime + wt
+ do {
val node = findFirst(includeInvisible)
- if (node != null) {
- return node
- } else {
- sc++
- }
- }
+ if (node != null) return node
+ if (interval > 0) delay(interval)
+ else ensureActive()
+ } while (System.currentTimeMillis() < endTime)
return null
}
@@ -102,7 +86,7 @@ abstract class ViewFinder>(
* @param includeInvisible Boolean 是否包含不可见元素
* @return ViewNode?
*/
- fun findFirst(includeInvisible: Boolean = false): ViewNode? {
+ suspend fun findFirst(includeInvisible: Boolean = false): ViewNode? {
//不可见
return traverseAllNode(startNode, includeInvisible = includeInvisible)
}
@@ -112,31 +96,32 @@ abstract class ViewFinder>(
* @param depths Array
* @return ViewNode?
*/
- fun findByDepths(vararg depths: Int): ViewNode? {
+ suspend fun findByDepths(vararg depths: Int): ViewNode? {
return Companion.findByDepths(depths, startNode)
}
//[findAll]
- fun find(includeInvisible: Boolean = false) = findAll(includeInvisible)
+ suspend fun find(includeInvisible: Boolean = false) = findAll(includeInvisible)
@Throws(ViewNodeNotFoundException::class)
- fun require(waitMillis: Long = WAIT_MILLIS): ViewNode {
+ suspend fun require(waitMillis: Long = WAIT_MILLIS): ViewNode {
return waitFor(waitMillis) ?: throw ViewNodeNotFoundException(this)
}
- fun exist(): Boolean = findFirst() != null
+ suspend fun exist(): Boolean = findFirst() != null
/**
*
* @param includeInvisible Boolean 是否包含不可见元素
* @return Array 无结果则返回空
*/
- fun findAll(includeInvisible: Boolean = false): Array {
+ suspend fun findAll(includeInvisible: Boolean = false): Array {
val l = mutableListOf()
traverseAllNode(startNode, l, includeInvisible)
return l.toTypedArray()
}
+
/**
* 深搜遍历
*
@@ -145,7 +130,7 @@ abstract class ViewFinder>(
* @param includeInvisible Boolean 是否包含不可见元素
* @return ViewNode?
*/
- private fun traverseAllNode(
+ private suspend fun traverseAllNode(
node: ViewNode?, list: MutableList? = null,
includeInvisible: Boolean = false, depth: Int = 0
): ViewNode? {
@@ -155,6 +140,7 @@ abstract class ViewFinder>(
return null
}
node.children.forEach { childNode ->
+ ensureActive()
if (!includeInvisible && !childNode.isVisibleToUser) {
return@forEach
}
@@ -175,7 +161,7 @@ abstract class ViewFinder>(
* 默认10s等待时间
* @return Boolean
*/
- fun waitHide(): Boolean {
+ suspend fun waitHide(): Boolean {
return waitHide(10000)
}
@@ -184,8 +170,8 @@ abstract class ViewFinder>(
* @param waitMs max 60s
* @return Boolean false 超时 true 消失
*/
- fun waitHide(waitMs: Int): Boolean {
- return whileWaitTime(waitMs.toLong()) {
+ suspend fun waitHide(waitMs: Int, interval: Long = 0L): Boolean {
+ return whileWaitTime(waitMs.toLong(), interval) {
ensureActive()
if (findFirst() != null) {
null
@@ -196,13 +182,9 @@ abstract class ViewFinder>(
} ?: false
}
- fun await(): ViewNode? {
- return waitFor()
- }
+ suspend fun await() = waitFor()
- fun await(l: Long): ViewNode? {
- return waitFor(l)
- }
+ suspend fun await(l: Long): ViewNode? = waitFor(l)
/**
* 查找条件
diff --git a/app/src/main/java/cn/vove7/andro_accessibility_api/demo/MainActivity.kt b/app/src/main/java/cn/vove7/andro_accessibility_api/demo/MainActivity.kt
index 772243d..ec9f4cf 100644
--- a/app/src/main/java/cn/vove7/andro_accessibility_api/demo/MainActivity.kt
+++ b/app/src/main/java/cn/vove7/andro_accessibility_api/demo/MainActivity.kt
@@ -7,6 +7,7 @@ import android.widget.ArrayAdapter
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity
import cn.vove7.andro_accessibility_api.demo.actions.*
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
class MainActivity : AppCompatActivity() {
diff --git a/app/src/main/java/cn/vove7/andro_accessibility_api/demo/actions/actoins.kt b/app/src/main/java/cn/vove7/andro_accessibility_api/demo/actions/actoins.kt
index a080637..6399991 100644
--- a/app/src/main/java/cn/vove7/andro_accessibility_api/demo/actions/actoins.kt
+++ b/app/src/main/java/cn/vove7/andro_accessibility_api/demo/actions/actoins.kt
@@ -253,7 +253,7 @@ class ClickTextAction : Action() {
val t = node.findFirst()
toast("haveFound: $t")
delay(1000)
- t?.tryClick()
+ toast("点击:${node.tryClick()}")
}
}
@@ -313,8 +313,8 @@ class SmartFinderAction : Action() {
it.isChecked
}.find()
-// SF.where(IdCondition("view_id")).or(RTextEqCondition("[0-9]+")).find()
-// SF.id("view_id").or().matchText("[0-9]+").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"))
@@ -337,8 +337,7 @@ class CoroutineStopAction : Action() {
override suspend fun run(act: Activity) {
requireBaseAccessibility(true)
val job = GlobalScope.async {
- val t = SF.attachCoroutine()
- .containsText("周三").waitFor(10000)
+ val t = SF.containsText("周三").waitFor(10000)
AlertDialog.Builder(act).apply {
setTitle("Output")
setMessage(t.toString())