6.7.0 - Alpha13 - 修复 auto.registerEvent 注册的无障碍服务事件会被其他脚本误清理的问题 (issue #466)
This commit is contained in:
@@ -56,6 +56,7 @@
|
||||
"images 部分相关方法可能引发内存泄露的问题 _[`issue #372`](http://issues.autojs6.com/372)_",
|
||||
"ocr 部分重载方法可能无法正常使用的问题",
|
||||
"ocr.detect 方法获得的结果可能与 ocr.mode 不匹配的问题 _[`issue #468`](http://issues.autojs6.com/468)_",
|
||||
"auto.registerEvent 注册的无障碍服务事件会被其他脚本误清理的问题 _[`issue #466`](http://issues.autojs6.com/466)_",
|
||||
"Android 10 UiObject#child 方法可能出现 ArrayIndexOutOfBoundsException 异常的问题 _[`issue #416`](http://issues.autojs6.com/416)_",
|
||||
"运行项目时 project.json 配置参数无法正常解析的问题",
|
||||
"项目配置文件中构建版本号或构建时间出现较大数字时可能导致应用崩溃的问题",
|
||||
|
||||
@@ -9,11 +9,11 @@ import android.view.accessibility.AccessibilityNodeInfo
|
||||
import org.autojs.autojs.core.accessibility.AccessibilityTool.Companion.DEFAULT_A11Y_SERVICE_START_TIMEOUT
|
||||
import org.autojs.autojs.core.accessibility.SimpleActionAutomator.Companion.AccessibilityEventCallback
|
||||
import org.autojs.autojs.core.automator.AccessibilityEventWrapper
|
||||
import org.autojs.autojs.event.EventDispatcher
|
||||
import org.autojs.autojs.core.pref.Language
|
||||
import org.autojs.autojs.event.EventDispatcher
|
||||
import org.autojs.autojs.ui.main.drawer.DrawerFragment.Companion.Event.AccessibilityServiceStateChangedEvent
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.util.TreeMap
|
||||
import java.util.*
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -21,7 +21,7 @@ import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
/**
|
||||
* Created by Stardust on May 2, 2017.
|
||||
* Modified by SuperMonster003 as of Mar 20, 2022.
|
||||
* Modified by SuperMonster003 as of Dec 29, 2025.
|
||||
*/
|
||||
open class AccessibilityService : android.accessibilityservice.AccessibilityService() {
|
||||
|
||||
@@ -30,7 +30,9 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
|
||||
var fastRootInActiveWindow: AccessibilityNodeInfo? = null
|
||||
var bridge: AccessibilityBridge? = null
|
||||
|
||||
private val eventBox = TreeMap<Int, AccessibilityEventCallback?>()
|
||||
// eventType -> (ownerId -> callback)
|
||||
private val eventBox = HashMap<Int, MutableMap<String, AccessibilityEventCallback?>>()
|
||||
private val eventBoxLock = Any()
|
||||
|
||||
private val gestureEventDispatcher = EventDispatcher<GestureListener>()
|
||||
|
||||
@@ -43,23 +45,62 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
|
||||
AccessibilityEvent::class.java.getField(
|
||||
"TYPE_${event.uppercase(Language.getPrefLanguage().locale)}"
|
||||
).get(null) as Int
|
||||
} catch (unused: NoSuchFieldException) {
|
||||
} catch (_: NoSuchFieldException) {
|
||||
throw IllegalArgumentException("Unknown event: $event")
|
||||
}
|
||||
}
|
||||
|
||||
fun addAccessibilityEventCallback(name: String, callback: AccessibilityEventCallback?) {
|
||||
eventBox[eventNameToType(name)] = callback
|
||||
fun addAccessibilityEventCallback(ownerId: String, name: String, callback: AccessibilityEventCallback?) {
|
||||
val type = eventNameToType(name)
|
||||
synchronized(eventBoxLock) {
|
||||
val bucket = eventBox.getOrPut(type) { HashMap() }
|
||||
if (callback == null) {
|
||||
bucket.remove(ownerId)
|
||||
if (bucket.isEmpty()) eventBox.remove(type)
|
||||
} else {
|
||||
bucket[ownerId] = callback
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeAccessibilityEventCallback(name: String) {
|
||||
eventBox.remove(eventNameToType(name))
|
||||
fun removeAccessibilityEventCallback(ownerId: String, name: String) {
|
||||
val type = eventNameToType(name)
|
||||
synchronized(eventBoxLock) {
|
||||
val bucket = eventBox[type] ?: return
|
||||
bucket.remove(ownerId)
|
||||
if (bucket.isEmpty()) eventBox.remove(type)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeAllAccessibilityEventCallbacks(ownerId: String) {
|
||||
synchronized(eventBoxLock) {
|
||||
val it = eventBox.entries.iterator()
|
||||
while (it.hasNext()) {
|
||||
val entry = it.next()
|
||||
entry.value.remove(ownerId)
|
||||
if (entry.value.isEmpty()) it.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAccessibilityEvent(event: AccessibilityEvent) {
|
||||
instance = this
|
||||
val type = event.eventType
|
||||
eventBox[type]?.onAccessibilityEvent(AccessibilityEventWrapper(event))
|
||||
|
||||
// Snapshot callbacks to avoid holding lock while invoking user code.
|
||||
// zh-CN: 为回调建立快照, 以避免在调用用户代码时持锁.
|
||||
val callbacks: List<AccessibilityEventCallback> = synchronized(eventBoxLock) {
|
||||
val bucket = eventBox[type] ?: return@synchronized emptyList()
|
||||
bucket.values.filterNotNull().toList()
|
||||
}
|
||||
|
||||
if (callbacks.isNotEmpty()) {
|
||||
val wrapper = AccessibilityEventWrapper(event)
|
||||
callbacks.forEach { cb ->
|
||||
cb.onAccessibilityEvent(wrapper)
|
||||
}
|
||||
}
|
||||
|
||||
if (containsAllEventTypes || eventTypes.contains(type)) {
|
||||
if (type == TYPE_WINDOW_STATE_CHANGED || type == TYPE_VIEW_FOCUSED) {
|
||||
rootInActiveWindow?.also { fastRootInActiveWindow = it }
|
||||
@@ -67,11 +108,9 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
|
||||
for ((_, delegate) in delegates) {
|
||||
val types = delegate.eventTypes
|
||||
if (types == null || types.contains(type)) {
|
||||
// val start = System.currentTimeMillis()
|
||||
if (delegate.onAccessibilityEvent(this@AccessibilityService, event)) {
|
||||
break
|
||||
}
|
||||
// Log.v(TAG, "millis: " + (System.currentTimeMillis() - start) + " delegate: " + delegate::class.java.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,11 +140,7 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
|
||||
}
|
||||
|
||||
override fun getRootInActiveWindow(): AccessibilityNodeInfo? {
|
||||
return try {
|
||||
super.getRootInActiveWindow()
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
return runCatching { super.getRootInActiveWindow() }.getOrNull()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@@ -177,13 +212,10 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() = try {
|
||||
fun stop() = runCatching {
|
||||
instance?.disableSelf()
|
||||
instance = null
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}.isSuccess
|
||||
|
||||
fun waitForStarted(timeout: Long = DEFAULT_A11Y_SERVICE_START_TIMEOUT): Boolean {
|
||||
if (hasInstance()) {
|
||||
@@ -209,7 +241,11 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ
|
||||
|
||||
@JvmStatic
|
||||
fun clearAccessibilityEventCallback() {
|
||||
instance?.eventBox?.clear()
|
||||
instance?.let { svc ->
|
||||
synchronized(svc.eventBoxLock) {
|
||||
svc.eventBox.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setCallback(listener: AccessibilityServiceCallback?) {
|
||||
|
||||
@@ -34,9 +34,12 @@ import android.accessibilityservice.AccessibilityService as AndroidAccessibility
|
||||
|
||||
/**
|
||||
* Created by Stardust on Apr 2, 2017.
|
||||
* Modified by SuperMonster003 as of Dec 29, 2025.
|
||||
*/
|
||||
class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge, private val scriptRuntime: ScriptRuntime) {
|
||||
|
||||
private val mA11yEventOwnerId: String = scriptRuntime.ownerId
|
||||
|
||||
private val mGlobalActionAutomatorRaw by lazy {
|
||||
GlobalActionAutomator(scriptRuntime.uiHandler.applicationContext, Handler(scriptRuntime.loopers.servantLooper)) {
|
||||
ensureService()
|
||||
@@ -198,12 +201,27 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge
|
||||
// ! zh-CN: 参考 Auto.js Pro.
|
||||
fun registerEvent(eventName: String, callback: AccessibilityEventCallback?) {
|
||||
ensureService()
|
||||
AccessibilityService.instance?.addAccessibilityEventCallback(eventName, callback)
|
||||
val service = AccessibilityService.instance ?: return
|
||||
|
||||
if (callback == null) {
|
||||
service.removeAccessibilityEventCallback(mA11yEventOwnerId, eventName)
|
||||
return
|
||||
}
|
||||
service.addAccessibilityEventCallback(mA11yEventOwnerId, eventName, callback)
|
||||
}
|
||||
|
||||
// @Created by 抠脚本人 on Jul 10, 2023.
|
||||
fun removeEvent(eventName: String) {
|
||||
AccessibilityService.instance?.removeAccessibilityEventCallback(eventName)
|
||||
AccessibilityService.instance?.removeAccessibilityEventCallback(mA11yEventOwnerId, eventName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the script exits, only clears accessibility
|
||||
* event listeners registered by the current script.
|
||||
* zh-CN: 脚本退出时调用, 只清理由当前脚本注册的无障碍事件监听.
|
||||
*/
|
||||
fun removeAllEventsForThisRuntime() {
|
||||
AccessibilityService.instance?.removeAllAccessibilityEventCallbacks(mA11yEventOwnerId)
|
||||
}
|
||||
|
||||
private fun performAction(simpleAction: SimpleAction): Boolean {
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.autojs.autojs.annotation.ScriptInterface
|
||||
import org.autojs.autojs.annotation.ScriptVariable
|
||||
import org.autojs.autojs.concurrent.VolatileDispose
|
||||
import org.autojs.autojs.core.accessibility.AccessibilityBridge
|
||||
import org.autojs.autojs.core.accessibility.AccessibilityService
|
||||
import org.autojs.autojs.core.accessibility.SimpleActionAutomator
|
||||
import org.autojs.autojs.core.accessibility.monitor.CloseableManager
|
||||
import org.autojs.autojs.core.activity.ActivityInfoProvider
|
||||
@@ -177,8 +176,8 @@ import org.autojs.autojs.runtime.api.augment.util.VersionCodes as UtilVersionCod
|
||||
|
||||
/**
|
||||
* Created by Stardust on Jan 27, 2017.
|
||||
* Modified by SuperMonster003 as of Dec 1, 2021.
|
||||
* Created by SuperMonster003 on May 24, 2024.
|
||||
* Modified by SuperMonster003 as of Dec 29, 2025.
|
||||
* Transformed by SuperMonster003 on May 24, 2024.
|
||||
*/
|
||||
@Suppress("unused", "PropertyName", "PrivatePropertyName")
|
||||
class ScriptRuntime private constructor(builder: Builder) {
|
||||
@@ -186,6 +185,8 @@ class ScriptRuntime private constructor(builder: Builder) {
|
||||
private val mJob = SupervisorJob()
|
||||
val coroutineScope = CoroutineScope(Dispatchers.Default + mJob)
|
||||
val coroutineContext = coroutineScope.coroutineContext
|
||||
|
||||
val ownerId = "runtime@${System.identityHashCode(this)}"
|
||||
|
||||
private var mUiHandlerAppContext: Context
|
||||
private var mRootShell: AbstractShell? = null
|
||||
@@ -601,7 +602,12 @@ class ScriptRuntime private constructor(builder: Builder) {
|
||||
// ! 清空无障碍事件.
|
||||
// ! en-US (translated by SuperMonster003 on Jul 29, 2024):
|
||||
// ! To clear accessibility event callbacks.
|
||||
ignoresException({ AccessibilityService.clearAccessibilityEventCallback() })
|
||||
// @Hint by SuperMonster003 on Dec 29, 2025.
|
||||
// ! Only clean up accessibility event callbacks registered
|
||||
// ! by the current script to avoid affecting other still-running scripts.
|
||||
// ! zh-CN: 只清理当前脚本注册的无障碍事件回调,避免影响其他仍在运行的脚本.
|
||||
// # ignoresException({ AccessibilityService.clearAccessibilityEventCallback() })
|
||||
ignoresException({ automator.removeAllEventsForThisRuntime() })
|
||||
|
||||
ignoresException({ RootUtils.resetRuntimeOverriddenRootModeState() })
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#Mon Dec 29 18:48:41 CST 2025
|
||||
BUILD_TIME=1767005321663
|
||||
#Mon Dec 29 19:40:59 CST 2025
|
||||
BUILD_TIME=1767008459948
|
||||
COMPILE_SDK_VERSION=36
|
||||
IMAGE_QUANT_CMAKE_VERSION=3.22.1
|
||||
IMAGE_QUANT_NDK_VERSION=26.1.10909125
|
||||
@@ -27,6 +27,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13
|
||||
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
||||
TARGET_SDK_VERSION=36
|
||||
TARGET_SDK_VERSION_INRT=29
|
||||
VERSION_BUILD=3561
|
||||
VERSION_BUILD=3562
|
||||
VERSION_NAME=6.7.0 Alpha13
|
||||
VSCODE_EXT_REQUIRED_VERSION=1.0.8
|
||||
|
||||
Reference in New Issue
Block a user