From 65ebc8311fa638458c0c1129c5e19ddb56f5c117 Mon Sep 17 00:00:00 2001 From: SuperMonster003 Date: Mon, 23 Jun 2025 16:48:01 +0800 Subject: [PATCH] =?UTF-8?q?6.7.0=20-=20Alpha2=20-=20=E4=BF=AE=E5=A4=8D=20A?= =?UTF-8?q?ndroid=2010=20UiObject#child=20=E5=BC=82=E5=B8=B8=20(issue=20#4?= =?UTF-8?q?16);=20=E7=A7=BB=E9=99=A4=20UiObject#hashCode/equals=20?= =?UTF-8?q?=E8=A6=86=E5=86=99;=20=E6=96=B0=E5=A2=9E=20UiObject#isShifted?= =?UTF-8?q?=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changelog/lang_zh-Hans.json | 10 +- app/build.gradle.kts | 26 ++-- .../autojs/autojs/core/automator/UiObject.kt | 141 ++++++++---------- version.properties | 6 +- 4 files changed, 87 insertions(+), 96 deletions(-) diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index 9773ae08..eeda7924 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -1,15 +1,17 @@ { "$data": { "v6.7.0": { - "released_date": "2025/06/15", + "released_date": "2025/06/23", "feature": [ "zip 模块, 用于文件压缩与解压缩操作 (Ref to [Auto.js Pro](https://g.pro.autojs.org/)) (参阅 项目文档 > [Zip](https://docs.autojs6.com/#/zip))", - "mediainfo 模块, 用于查看媒体文件的详细信息 (参阅 项目文档 > [媒体信息](https://docs.autojs6.com/#/mediainfo))" + "mediainfo 模块, 用于查看媒体文件的详细信息 (参阅 项目文档 > [媒体信息](https://docs.autojs6.com/#/mediainfo))", + "UiObject#isShifted 方法, 用于检测控件位置变化" ], "fix": [ "使用 XML 语法将 JavaScript 表达式作为属性值时, this 对象可能出现指向错误的问题", "调用 images.requestScreenCapture 时用户取消授权可能导致应用崩溃的问题", "images 部分相关方法出现异常时 oneShot 标记功能失效的问题 _[`issue #372`](http://issues.autojs6.com/372)_", + "Android 10 UiObject#child 方法可能出现 ArrayIndexOutOfBoundsException 异常的问题 _[`issue #416`](http://issues.autojs6.com/416)_", "打包应用无法正常使用 Paddle OCR 与 Rapid OCR 功能的问题", "版本历史页面部分系统因字体差别导致统计数据显示不完整的问题" ], @@ -19,6 +21,7 @@ "内置模块相关方法实参类型的异常消息增加类型摘要信息" ], "dependency": [ + "附加 Androidx Core (KTX) 版本 1.16.0", "升级 Gradle 版本 8.14 -> 8.14.2", "升级 Apache Commons 版本 3.16.0 -> 3.17.0", "升级 Retrofit2 Retrofit 版本 2.11.0 -> 2.12.0", @@ -31,7 +34,8 @@ "升级 Jsoup 版本 1.19.1 -> 1.20.1", "升级 Junit Jupiter 版本 5.10.3 -> 5.13.0", "升级 Androidx AppCompat 版本 1.7.0 -> 1.7.1", - "升级 Material Progressbar 版本 1.4.2 -> 1.6.1" + "升级 Material Progressbar 版本 1.4.2 -> 1.6.1", + "升级 Androidx Room 版本 2.7.1 -> 2.7.2" ] }, "v6.6.4": { diff --git a/app/build.gradle.kts b/app/build.gradle.kts index dbf8ea22..c43ac054 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,6 +27,11 @@ val buildTypeRelease = "release" val buildActionAssemble = "assemble" val templateName = "template" +val taskNames = gradle.startParameter.taskNames +val isAppAssembleTaskRequested = taskNames.any { it.contains(Regex("^(:?$flavorNameApp:)?$buildActionAssemble", IGNORE_CASE)) } +val isInrtAssembleTaskRequested = taskNames.any { it.contains(Regex("^(:?$flavorNameApp:)?$buildActionAssemble$flavorNameInrt", IGNORE_CASE)) } +val isInrtTaskRequested = taskNames.any { it.contains(flavorNameInrt, true) } + plugins { id("com.android.application") id("com.google.devtools.ksp") @@ -40,6 +45,9 @@ dependencies /* Unclassified */ { // Kotlin reflect implementation(kotlin("reflect")) + // Androidx Core + implementation("androidx.core:core-ktx:1.16.0") + // LeakCanary debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14") @@ -218,9 +226,9 @@ dependencies /* Unclassified */ { implementation(files("$rootDir/libs/tiny-sign-0.9.jar")) // Room - implementation("androidx.room:room-runtime:2.7.1") - implementation("androidx.room:room-ktx:2.7.1") - ksp("androidx.room:room-compiler:2.7.1") + implementation("androidx.room:room-runtime:2.7.2") + implementation("androidx.room:room-ktx:2.7.2") + ksp("androidx.room:room-compiler:2.7.2") // ApkSig // implementation("com.android.tools.build:apksig:8.7.3") @@ -549,10 +557,10 @@ android { } androidResources { - if (gradle.startParameter.taskNames.any { it.contains(Regex("^(:?$flavorNameApp:)?$buildActionAssemble")) }) { + if (isAppAssembleTaskRequested) { ignoreAssetsPatterns.addAll(listOf(".idea", "declarations", "sample/declarations")) } - if (gradle.startParameter.taskNames.any { it.contains(Regex("^(:?$flavorNameApp:)?$buildActionAssemble$flavorNameInrt", IGNORE_CASE)) }) { + if (isInrtAssembleTaskRequested) { // @Hint by SuperMonster003 on Oct 16, 2023. // ! Runtime assets will be copied from flavor "app" // ! while building an apk on org.autojs.autojs.ui.project.BuildActivity. @@ -635,7 +643,7 @@ android { "EmojiReference.txt", ).let { resources.excludes.addAll(it) } - if (gradle.startParameter.taskNames.any { it.contains(flavorNameInrt, true) }) { + if (isInrtTaskRequested) { listOf( "**/prob_emit.txt", // Jieba Analysis (zh-CN: 结巴分词) "**/dict-chinese-*.db.gzip", // Jieba Analysis (zh-CN: 结巴分词) @@ -742,9 +750,7 @@ android { // Configures multiple APKs based on ABI. abi { // Enables building multiple APKs per ABI. - isEnable = /* isNotAssembleInrt */ gradle.startParameter.taskNames.none { - it.contains(Regex("^(:?$flavorNameApp:)?$buildActionAssemble$flavorNameInrt", IGNORE_CASE)) - } + isEnable = !isInrtAssembleTaskRequested // By default, all ABIs are included, so use reset() and include to specify that we only // want APKs for x86 and x86_64. // Resets the list of ABIs that Gradle should create APKs for to none. @@ -788,7 +794,7 @@ tasks { rename { name -> Utils.digestCRC32(file("${src}/$name")).let { digest -> - name.replace("^(.+?)(\\.$ext)\$".toRegex(), "\$1-$digest\$2") + name.replace(Regex("^(.+?)(\\.$ext)$"), "$1-$digest$2") } } diff --git a/app/src/main/java/org/autojs/autojs/core/automator/UiObject.kt b/app/src/main/java/org/autojs/autojs/core/automator/UiObject.kt index 1f2b6aa4..6ac811cf 100644 --- a/app/src/main/java/org/autojs/autojs/core/automator/UiObject.kt +++ b/app/src/main/java/org/autojs/autojs/core/automator/UiObject.kt @@ -13,9 +13,10 @@ import org.autojs.autojs.core.accessibility.AccessibilityNodeInfoHelper import org.autojs.autojs.core.accessibility.UiSelector import org.autojs.autojs.extension.AnyExtensions.isJsNullish import org.autojs.autojs.extension.AnyExtensions.isJsUndefined -import org.autojs.autojs.extension.ArrayExtensions.toHashCode +import org.autojs.autojs.extension.AnyExtensions.jsBrief import org.autojs.autojs.extension.ArrayExtensions.toNativeArray import org.autojs.autojs.runtime.ScriptRuntime +import org.autojs.autojs.runtime.exception.WrappedIllegalArgumentException import org.autojs.autojs.util.DisplayUtils.toRoundIntX import org.autojs.autojs.util.DisplayUtils.toRoundIntY import org.autojs.autojs.util.RhinoUtils @@ -24,9 +25,9 @@ import org.autojs.autojs.util.StringUtils.str import org.autojs.autojs6.R import org.mozilla.javascript.BaseFunction import org.mozilla.javascript.Context -import org.mozilla.javascript.Scriptable import org.opencv.core.Point import org.opencv.core.Size +import kotlin.math.abs import kotlin.math.roundToInt /** @@ -56,32 +57,45 @@ open class UiObject( indexInParent: Int = -1, ) : this(info, null, depth, indexInParent) - open fun parent(): UiObject? = try { + open fun parent(): UiObject? = runCatching { super.getParent()?.let { node -> UiObject(node.unwrap(), depth - 1, node.parent?.run { // @Dubious by SuperMonster003 on May 26, 2022. // ! May lead to poor performance? // ! zh-CN: 可能导致性能不佳? - (0 until childCount).firstOrNull { getChild(it) == node } ?: -1 + (0 until childCount).firstOrNull { child(it) == node } ?: -1 } ?: 0) } - } catch (e: IllegalStateException) { + }.onFailure { // FIXME by Stardust on May 5, 2017. - null.also { e.printStackTrace() } + it.printStackTrace() + }.getOrNull() + + fun parent(i: Int): UiObject? { + require(i >= 0) { "Index for UiObject#parent cannot be negative ($i)" } + return compass("p$i") } - fun parent(i: Int): UiObject? = if (i < 0) throw Exception("i < 0") else compass("p$i") - - open fun child(i: Int): UiObject? { - if (i < 0) { - return (i + childCount).takeIf { it >= 0 }?.let { child(it) } + open fun child(i: Int): UiObject? = when { + i >= childCount -> { + null } - return try { - super.getChild(i)?.run { UiObject(unwrap(), depth + 1, i) } - } catch (e: IllegalStateException) { + i < 0 -> when (val targetIdx = i + childCount) { + in 0 until childCount -> child(targetIdx) + else -> null + } + else -> runCatching { + // @Caution by SuperMonster003 on Jun 19, 2025. + // ! `super.getChild` may cause ArrayIndexOutOfBoundsException when `i` is out of range of [0..childCound). + // ! zh-CN: 当 `i` 超出 [0..childCount) 范围时可能导致 ArrayIndexOutOfBoundsException 异常. + // @Dubious by SuperMonster003 on Jun 19, 2025. + // ! It seems that the situation above only happens on API Level 29 (Android 10) [Q] ? + // ! zh-CN: 似乎上述情况只发生在 API Level 29 (Android 10) [Q] ? + super.getChild(i)?.let { UiObject(it.unwrap(), depth + 1, i) } + }.onFailure { // FIXME by Stardust on May 5, 2017. - null.also { e.printStackTrace() } - } + it.printStackTrace() + }.getOrNull() } // @Deprecated by SuperMonster003 on Jul 20, 2023. @@ -413,10 +427,13 @@ open class UiObject( false.also { e.printStackTrace() } } - override fun getChild(index: Int): AccessibilityNodeInfoCompat = - allocator?.getChild(this, index) ?: super.getChild(index) + override fun getChild(index: Int): AccessibilityNodeInfoCompat { + return allocator?.getChild(this, index) ?: super.getChild(index) + } - override fun getParent(): AccessibilityNodeInfoCompat? = allocator?.getParent(this) ?: super.getParent() + override fun getParent(): AccessibilityNodeInfoCompat? { + return allocator?.getParent(this) ?: super.getParent() + } override fun findAccessibilityNodeInfosByText(text: String): List { return allocator?.findAccessibilityNodeInfosByText(this, text) @@ -492,49 +509,7 @@ open class UiObject( return "[${UiObject::class.java.simpleName}] $simpledClassName ${summary()}" } - override fun hashCode() = listOf( - "${packageName()}", - "$parent", - "${id()}", - "${fullId()}", - "${idHex()}", - "${desc()}", - text(), - "${bounds()}", - "${className()}", - "${clickable()}", - "${longClickable()}", - "${scrollable()}", - "${indexInParent()}", - "${childCount()}", - "${depth()}", - "${checked()}", - "${enabled()}", - "${editable()}", - "${focusable()}", - "${checkable()}", - "${selected()}", - "$isDismissable", - "${visibleToUser()}", - "$isContextClickable", - "${focused()}", - "$isAccessibilityFocused", - "${rowCount()}", - "${columnCount()}", - "${row()}", - "${column()}", - "${rowSpan()}", - "${columnSpan()}", - "$drawingOrder", - "${actionNames()}", - "${isSingleton()}", - "${firstSibling()}", - "${lastSibling()}", - "${firstChild()}", - "${lastChild()}", - ).toHashCode() - - override fun equals(other: Any?): Boolean { + fun isSimilar(other: Any?): Boolean { if (this === other) return true if (other !is UiObject) return false @@ -543,6 +518,7 @@ open class UiObject( val info = unwrap() ?: return false return info == other.unwrap() && + bounds() == other.bounds() && packageName() == other.packageName() && parent == other.parent && id() == other.id() && @@ -550,7 +526,6 @@ open class UiObject( idHex() == other.idHex() && desc() == other.desc() && text() == other.text() && - bounds() == other.bounds() && className() == other.className() && clickable() == other.clickable() && longClickable() == other.longClickable() && @@ -578,26 +553,32 @@ open class UiObject( drawingOrder == other.drawingOrder && actionNames() == other.actionNames() && isSingleton() == other.isSingleton() && - "${firstSibling()}" == "${other.firstSibling()}" && - "${lastSibling()}" == "${other.lastSibling()}" && - "${firstChild()}" == "${other.firstChild()}" && - "${lastChild()}" == "${other.lastChild()}" + "${firstSibling() ?: ""}" == "${other.firstSibling()}" && + "${lastSibling() ?: ""}" == "${other.lastSibling()}" && + "${firstChild() ?: ""}" == "${other.firstChild()}" && + "${lastChild() ?: ""}" == "${other.lastChild()}" } - fun isSimilar(other: Any?): Boolean { - if (this === other) return true - if (other == null) return false - when (other) { - is Scriptable -> { - val thisHashCode = hashCode() - val otherHashCode = RhinoUtils.hashCodeOfScriptable(other) ?: return false - return thisHashCode == otherHashCode + fun isShifted(other: UiObject) = bounds() == other.bounds() + + fun isShifted(other: UiObject, tolerance: Double): Boolean { + val a = bounds() + val b = other.bounds() + return when { + tolerance < 0 -> throw WrappedIllegalArgumentException( + "Argument \"tolerance\" ${tolerance.jsBrief()} for UiObject#isShifted must be greater than or equal to 0", + ) + tolerance < 1 -> (width() to height()).let { (w, h) -> + return@let abs(a.left - b.left) > tolerance * w + || abs(a.top - b.top) > tolerance * h + || abs(a.right - b.right) > tolerance * w + || abs(a.bottom - b.bottom) > tolerance * h } - else -> { - return when { - other.javaClass != UiObject::class.java -> false - else -> hashCode() == other.hashCode() - } + else -> let { + return@let abs(a.left - b.left) > tolerance + || abs(a.top - b.top) > tolerance + || abs(a.right - b.right) > tolerance + || abs(a.bottom - b.bottom) > tolerance } } } diff --git a/version.properties b/version.properties index 5ac68ec0..231254c2 100644 --- a/version.properties +++ b/version.properties @@ -1,5 +1,5 @@ -#Sun Jun 15 14:16:44 CST 2025 -BUILD_TIME=1749968204434 +#Mon Jun 23 16:34:06 CST 2025 +BUILD_TIME=1750667646268 COMPILE_SDK_VERSION=35 IMAGE_QUANT_CMAKE_VERSION=3.22.1 IMAGE_QUANT_NDK_VERSION=26.1.10909125 @@ -19,6 +19,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13 RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3 TARGET_SDK_VERSION=35 TARGET_SDK_VERSION_INRT=29 -VERSION_BUILD=3293 +VERSION_BUILD=3302 VERSION_NAME=6.7.0 Alpha2 VSCODE_EXT_REQUIRED_VERSION=1.0.8