6.6.1 - Alpha2 - 增加 UiObject#isSimilar 及 UiObjectCollection#isSimilar 方法

This commit is contained in:
SuperMonster003
2024-12-09 20:38:45 +08:00
parent c48cb3d589
commit ec910ab0f7
4 changed files with 165 additions and 17 deletions

View File

@@ -1,19 +1,22 @@
{
"$data": {
"v6.6.1": {
"released_date": "2024/12/08",
"released_date": "2024/12/09",
"feature": [
"pinyin 模块, 用于汉语拼音转换 (参阅 项目文档 > [汉语拼音](https://docs.autojs6.com/#/pinyin))"
"pinyin 模块, 用于汉语拼音转换 (参阅 项目文档 > [汉语拼音](https://docs.autojs6.com/#/pinyin))",
"UiObject#isSimilar 及 UiObjectCollection#isSimilar 方法, 用于确定控件或控件集合是否相似"
],
"fix": [
"部分环境因回退版本过低而无法正常编译项目的问题",
"调用不存在的方法时可能出现的 \"非原始类型值\" 异常",
"部分设备无法正常添加脚本快捷方式的问题 (试修) _[`issue #221`](http://issues.autojs6.com/221)_",
"automator.click/longClick 方法参数类型限制错误 _[`issue #275`](http://issues.autojs6.com/275)_"
"automator.click/longClick 方法参数类型限制错误 _[`issue #275`](http://issues.autojs6.com/275)_",
"UiObjectCollection 实例缺失自身方法及属性的问题"
],
"improvement": [
"脚本项目识别在 project.json 损坏情况下尽可能还原关键信息",
"打包单文件时自动生成的包名后缀支持将简体中文自动转换为拼音",
"UiSelector#findOnce 及 UiSelector#find 方法支持负数参数",
"UI 元素及 className 相关选择器支持更多的包名前缀省略形式 (如 RecyclerView, Snackbar 等)"
]
},

View File

@@ -13,6 +13,7 @@ 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.ArrayExtensions.toNativeArray
import org.autojs.autojs.runtime.ScriptRuntime
import org.autojs.autojs.util.DisplayUtils.toRoundIntX
@@ -22,6 +23,7 @@ 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.roundToInt
@@ -485,8 +487,109 @@ open class UiObject(
override fun toString() = "[${UiObject::class.java.simpleName}] $className ${summary()}"
override fun hashCode(): Int {
return super.hashCode()
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 {
if (this === other) return true
if (other !is UiObject) return false
return packageName() == other.packageName() &&
parent == other.parent &&
id() == other.id() &&
fullId() == other.fullId() &&
idHex() == other.idHex() &&
desc() == other.desc() &&
text() == other.text() &&
bounds() == other.bounds() &&
className() == other.className() &&
clickable() == other.clickable() &&
longClickable() == other.longClickable() &&
scrollable() == other.scrollable() &&
indexInParent() == other.indexInParent() &&
childCount() == other.childCount() &&
depth() == other.depth() &&
checked() == other.checked() &&
enabled() == other.enabled() &&
editable() == other.editable() &&
focusable() == other.focusable() &&
checkable() == other.checkable() &&
selected() == other.selected() &&
isDismissable == other.isDismissable &&
visibleToUser() == other.visibleToUser() &&
isContextClickable == other.isContextClickable &&
focused() == other.focused() &&
isAccessibilityFocused == other.isAccessibilityFocused &&
rowCount() == other.rowCount() &&
columnCount() == other.columnCount() &&
row() == other.row() &&
column() == other.column() &&
rowSpan() == other.rowSpan() &&
columnSpan() == other.columnSpan() &&
drawingOrder == other.drawingOrder &&
actionNames() == other.actionNames() &&
isSingleton() == other.isSingleton() &&
"${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
}
else -> {
return when {
other.javaClass != UiObject::class.java -> false
else -> hashCode() == other.hashCode()
}
}
}
}
companion object {
@@ -496,14 +599,7 @@ open class UiObject(
internal const val RESULT_TYPE_WIDGET = "widget"
private val RESULT_GROUP_WIDGET by lazy { arrayOf("#", "w", RESULT_TYPE_WIDGET) }
private val RESULT_GROUP_WIDGET_COLLECTION by lazy {
arrayOf("{}", "wc", "collection", "list").plus(
listAliases(
"#",
"w"
)
)
}
private val RESULT_GROUP_WIDGET_COLLECTION by lazy { arrayOf("{}", "wc", "collection", "list").plus(listAliases("#", "w")) }
private val RESULT_GROUP_WIDGETS by lazy { arrayOf("[]", "ws", "widgets").plus(arrayAliases("#", "w")) }
private val RESULT_GROUP_CONTENT by lazy { arrayOf("$", "txt", "content") }
private val RESULT_GROUP_CONTENTS by lazy { arrayOf("contents").plus(arrayAliases("$", "txt", "content")) }
@@ -520,9 +616,8 @@ open class UiObject(
@JvmStatic
@Throws(IllegalArgumentException::class)
fun ensureCompass(s: CharSequence?) {
if (s != null && !isCompass(s)) {
throw IllegalArgumentException(str(R.string.error_invalid_compass, s))
}
s ?: return
require(isCompass(s)) { str(R.string.error_invalid_compass, s) }
}
@JvmStatic

View File

@@ -1,7 +1,10 @@
package org.autojs.autojs.core.automator
import org.autojs.autojs.core.accessibility.UiSelector
import org.autojs.autojs.extension.ArrayExtensions.toHashCode
import org.autojs.autojs.tool.Consumer
import org.autojs.autojs.util.RhinoUtils
import org.mozilla.javascript.Scriptable
/**
* Created by Stardust on Mar 9, 2017.
@@ -62,7 +65,36 @@ class UiObjectCollection private constructor(val nodes: List<UiObject?>) : UiObj
}
override fun toString(): String {
return "${UiObjectCollection::class.java.name}@${hashCode()}"
return "${UiObjectCollection::class.java.name}@${super.hashCode()}"
}
override fun hashCode(): Int = when {
nodes.isEmpty() -> super.hashCode()
else -> nodes.map { it.hashCode() }.toHashCode()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is UiObjectCollection) return false
return nodes == other.nodes
}
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
}
else -> {
return when {
other.javaClass != UiObjectCollection::class.java -> false
else -> hashCode() == other.hashCode()
}
}
}
}
companion object {

View File

@@ -7,9 +7,11 @@ import android.os.Looper
import android.os.Parcelable
import android.util.Log
import org.autojs.autojs.AutoJs
import org.autojs.autojs.core.automator.UiObjectCollection
import org.autojs.autojs.extension.AnyExtensions.isJsNullish
import org.autojs.autojs.extension.AnyExtensions.jsBrief
import org.autojs.autojs.extension.AnyExtensions.jsSpecies
import org.autojs.autojs.extension.AnyExtensions.jsUnwrapped
import org.autojs.autojs.extension.ScriptableExtensions.defineProp
import org.autojs.autojs.extension.ScriptableExtensions.prop
import org.autojs.autojs.rhino.TopLevelScope
@@ -813,6 +815,22 @@ object RhinoUtils {
@JvmStatic
fun newNativeArray(array: Array<Any?>) = NativeArray(array).also { it.exportAsJSClass(NativeArray.MAX_PROTOTYPE_ID, it, false) }
@JvmStatic
fun hashCodeOfScriptable(other: Any?): Int? {
if (other !is Scriptable) return null
val getClassFunc = other["getClass"]
if (getClassFunc is BaseFunction) {
val clazz = callFunction(getClassFunc, emptyArray()).jsUnwrapped()
if (clazz != UiObjectCollection::class.java) return null
}
val hashCodeFunc = other["hashCode"]
if (hashCodeFunc is BaseFunction) {
val hashCode = callFunction(hashCodeFunc, emptyArray()).jsUnwrapped()
if (hashCode is Number) return hashCode.toInt()
}
return null
}
class ObsoletedRhinoFunctionException(funcName: String) : Exception(
"Function \"$funcName\" can no longer be used as it has been obsoleted",
)