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())