diff --git a/app/src/main/java/com/loveerror/bested/button/ActionEvent.kt b/app/src/main/java/com/loveerror/bested/button/ActionEvent.kt new file mode 100644 index 0000000..5e024aa --- /dev/null +++ b/app/src/main/java/com/loveerror/bested/button/ActionEvent.kt @@ -0,0 +1,34 @@ +package com.loveerror.bested.button + +enum class ActionEvent { + + START_ACTION("com.loveerror.bested.START_ACTION", "启动按钮"), + FILL_ACTION("com.loveerror.bested.FILL_ACTION", "填充表单"), + SET_DATA_SOURCE("com.loveerror.bested.SET_DATA_SOURCE", "设置数据源"), + HIDE_FLOATING_BUTTON("com.loveerror.bested.HIDE_FLOATING_BUTTON", "隐藏悬浮按钮"), + PRINT_CURRENT_PAGE("com.loveerror.bested.PRINT_CURRENT_PAGE", "打印当前界面"), + + UNKNOWN("com.loveerror.bested.UNKNOWN", "未知",isDisplay = false); + + constructor(event: String, menuName: String, isDisplay: Boolean = true){ + this.event = event + this.menuName = menuName + this.isDisplay = isDisplay + } + + val menuName: String + val event: String + + val isDisplay: Boolean + + companion object { + fun getByMenuName(menuName: String) :ActionEvent { + for (item in ActionEvent.entries){ + if (item.menuName == menuName){ + return item + } + } + return UNKNOWN + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/loveerror/bested/button/DragFloatingButton.kt b/app/src/main/java/com/loveerror/bested/button/DragFloatingButton.kt index ad5f954..588ea99 100644 --- a/app/src/main/java/com/loveerror/bested/button/DragFloatingButton.kt +++ b/app/src/main/java/com/loveerror/bested/button/DragFloatingButton.kt @@ -118,67 +118,31 @@ class DragFloatingButton(context: Context) : View(context) { private fun showPopupMenu() { val popup = PopupMenu(context, this) - popup.menu.add("启动按钮") - popup.menu.add("填充表单") - popup.menu.add("设置数据源") - popup.menu.add("隐藏悬浮按钮") + // 添加菜单项 use ActionEvent + for (item in ActionEvent.entries){ + if (item.isDisplay) { + popup.menu.add(item.menuName) + } + } popup.setOnMenuItemClickListener { item -> - when (item.title) { - "启动按钮" -> { - performStartAction() - true - } - "填充表单" -> { - performFillFormAction() - true - } - "设置数据源" -> { - performSetDataSource() - true - } - "隐藏悬浮按钮" -> { - hideFloatingButton() - true - } - else -> false + val actionEvent = ActionEvent.getByMenuName(item.title.toString()) + try { + context.sendBroadcast(android.content.Intent(actionEvent.event).apply { + setPackage(context.packageName) + }) + } catch (e: IllegalArgumentException) { + // 处理枚举值不存在的情况 + e.printStackTrace() + println("枚举值不存在: ${item.title},${actionEvent}, ${e.toString()}") } + true } popup.show() } - - private fun performStartAction() { - // 执行启动按钮操作 - context.sendBroadcast(android.content.Intent("com.loveerror.bested.START_ACTION").apply { - setPackage(context.packageName) - }) - } - - private fun performSetDataSource() { - // 执行设置数据源操作 - context.sendBroadcast(android.content.Intent("com.loveerror.bested.SET_DATA_SOURCE").apply { - setPackage(context.packageName) - }) - } - - private fun hideFloatingButton() { - context.sendBroadcast(android.content.Intent("com.loveerror.bested.HIDE_FLOATING_BUTTON").apply { - setPackage(context.packageName) - }) - - // 或者直接通过 WindowManager 移除视图 - try { - windowManager.removeView(this) - } catch (e: Exception) { - // 处理可能的异常 - } - } } -private fun DragFloatingButton.performFillFormAction() { - // 执行启动按钮操作 - context.sendBroadcast(android.content.Intent("com.loveerror.bested.FILL_ACTION").apply { - setPackage(context.packageName) - }) -} + + + diff --git a/app/src/main/java/com/loveerror/bested/button/GlobalFloatingButtonManager.kt b/app/src/main/java/com/loveerror/bested/button/GlobalFloatingButtonManager.kt index a31a691..9186af8 100644 --- a/app/src/main/java/com/loveerror/bested/button/GlobalFloatingButtonManager.kt +++ b/app/src/main/java/com/loveerror/bested/button/GlobalFloatingButtonManager.kt @@ -23,14 +23,14 @@ class GlobalFloatingButtonManager(private val context: Context) { broadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { when (intent.action) { - "com.loveerror.bested.HIDE_FLOATING_BUTTON" -> { + ActionEvent.HIDE_FLOATING_BUTTON.event -> { hideFloatingButton() } } } } - val filter = IntentFilter("com.loveerror.bested.HIDE_FLOATING_BUTTON") + val filter = IntentFilter(ActionEvent.HIDE_FLOATING_BUTTON.event) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { context.registerReceiver(broadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED) } else { diff --git a/app/src/main/java/com/loveerror/bested/handler/NormalGroupCreateForm.kt b/app/src/main/java/com/loveerror/bested/handler/NormalGroupCreateForm.kt index cf368d2..655f48f 100644 --- a/app/src/main/java/com/loveerror/bested/handler/NormalGroupCreateForm.kt +++ b/app/src/main/java/com/loveerror/bested/handler/NormalGroupCreateForm.kt @@ -1,147 +1,308 @@ package com.loveerror.bested.handler import android.view.accessibility.AccessibilityNodeInfo +import com.loveerror.bested.UIInspectorService import com.loveerror.bested.tool.AccessibilityTool import com.loveerror.bested.tool.NodeToolHint import com.loveerror.bested.tool.NodeToolText -class NormalGroupCreateForm { - companion object{ +class NormalGroupCreateForm (private val uiInspectorService: UIInspectorService){ - class FieldName { - val fieldName: String - val hintText: String - val defaultValue: String - val type: FieldType - constructor(fieldName: String, hintText: String, defaultValue: String, type: FieldType){ - this.fieldName = fieldName - this.hintText = hintText - this.defaultValue = defaultValue - this.type = type + class FieldName { + + val fieldName: String + val hintText: String + val defaultValue: String + val type: FieldType + + constructor(fieldName: String, hintText: String, defaultValue: String, type: FieldType = FieldType.TEXT){ + this.fieldName = fieldName + this.hintText = hintText + this.defaultValue = defaultValue + this.type = type + } + } + + enum class FieldType { + TEXT, + SELECT, + CHECKBOX, + RADIO, + DATE, + TIME, + NUMBER, + CURRENCY, + EMAIL, + PHONE, + ADDRESS, + URL, + PASSWORD, + FILE, + IMAGE, + VIDEO, + AUDIO, + OTHER + } + + var fields = arrayListOf() + init { + fields.add(FieldName("集团名称", "请输入集团名称", "集团abc", FieldType.TEXT)) + fields.add(FieldName("证件名称", "证件名称", "jituan", FieldType.TEXT)) + fields.add(FieldName("证件号码", "请输入证件号码", "44010119900101001X", FieldType.TEXT)) + fields.add(FieldName("法人代表", "请输入法人代表", "张三", FieldType.TEXT)) + fields.add(FieldName("证件地址", "请输入证件地址", "北京市海淀区", FieldType.TEXT)) + fields.add(FieldName("联系电话", "请输入联系电话", "13266667777", FieldType.TEXT)) + fields.add(FieldName("员工数", "请输入人数", "1", FieldType.TEXT)) + + // select + fields.add(FieldName("集团状态", "", "潜在集团", FieldType.SELECT)) + fields.add(FieldName("证件有效期", "", "2025", FieldType.SELECT)) + + + } + + + /** + * 自动填充普通集团建档表单 + * @param root AccessibilityNodeInfo根节点 + * @param groupName 集团名称 + */ + fun autoFillForm(root: AccessibilityNodeInfo) { + + val fieldMap = mapOf( + "集团名称" to "集团abc2", + "证件名称" to "jituan2", + "证件号码" to "44010119900101001X", + "法人代表" to "张三2", + "证件地址" to "北京市海淀区2", + "联系电话" to "13288887777", + "员工数" to "2" + ) + performBottomSheetSelection(root, "证件有效期", "2056") + +// 先展开分类信息区域 +// expandClassificationSection(root) + + for(field in fields){ + if (field.type == FieldType.SELECT){ + performBottomSheetSelection(root, field.fieldName, fieldMap[field.fieldName] ?: field.defaultValue) + }else{ + fillEditTextByHintText(root,fieldMap[field.fieldName] ?: field.defaultValue, field.fieldName, field.hintText) } } - enum class FieldType { - TEXT, - SELECT, - CHECKBOX, - RADIO, - DATE, - TIME, - NUMBER, - CURRENCY, - EMAIL, - PHONE, - ADDRESS, - URL, - PASSWORD, - FILE, - IMAGE, - VIDEO, - AUDIO, - OTHER - } + AccessibilityTool.printViewTree(uiInspectorService.rootInActiveWindow,"after 已点击兄弟节点") + var rootNew = uiInspectorService.rootInActiveWindow - var fields = arrayListOf() - init { - fields.add(FieldName("集团名称", "请输入集团名称", "集团abc", FieldType.TEXT)) - fields.add(FieldName("证件名称", "证件名称", "jituan", FieldType.TEXT)) - fields.add(FieldName("证件号码", "请输入证件号码", "44010119900101001X", FieldType.TEXT)) - fields.add(FieldName("法人代表", "请输入法人代表", "张三", FieldType.TEXT)) - fields.add(FieldName("证件地址", "请输入证件地址", "北京市海淀区", FieldType.TEXT)) - fields.add(FieldName("联系电话", "请输入联系电话", "13266667777", FieldType.TEXT)) - fields.add(FieldName("员工数", "请输入人数", "1", FieldType.TEXT)) + var peopleCountNode = NodeToolText.findNodeByText(rootNew,"请输入人数") - } - /** - * 自动填充普通集团建档表单 - * @param root AccessibilityNodeInfo根节点 - * @param groupName 集团名称 - */ - fun autoFillForm(root: AccessibilityNodeInfo) { + if (peopleCountNode == null){ + println("未找到员工数输入框") + // 使用示例 + val targetNode = NodeToolText.findNodeByText(root, "分类信息") + val nextSibling = NodeToolText.findNextSiblingNode(targetNode) - val fieldMap = mapOf( - "集团名称" to "集团abc2", - "证件名称" to "jituan2", - "证件号码" to "44010119900101001X", - "法人代表" to "张三2", - "证件地址" to "北京市海淀区2", - "联系电话" to "13288887777", - "员工数" to "1" - ) - // 查找并填写集团名称输入框 - val groupName = "集团abc" + if (nextSibling != null && nextSibling.isClickable) { +// AccessibilityTool.focus(nextSibling) + AccessibilityTool.ensureNodeVisible(nextSibling) + AccessibilityTool.performClick(nextSibling, false) - for(fieldName in fields){ - fillEditTextByHintText(root,fieldMap[fieldName.fieldName] ?: fieldName.defaultValue, fieldName.fieldName, fieldName.hintText) + Thread.sleep(1000) + println("已点击兄弟节点") +// AccessibilityTool.printViewTree(uiInspectorService.rootInActiveWindow,"after 已点击兄弟节点") + } else { + println("未找到可点击的兄弟节点") } + rootNew = uiInspectorService.rootInActiveWindow + peopleCountNode = NodeToolText.findNodeByText(rootNew,"请输入人数") + } + fillEditText(peopleCountNode, fieldMap["员工数"] ?: "1","员工数") +// fillEditTextByHintText(rootNew,fieldMap["员工数"] ?: "1", "员工数", "请输入人数") +// 示例:点击"集团状态"触发底部选择,选择"在网集团" // fillCompanyNameField(root, groupName) - // 可以继续添加其他字段的填充逻辑 - // 例如:选择集团状态、选择集团等级等 - } + // 可以继续添加其他字段的填充逻辑 + // 例如:选择集团状态、选择集团等级等 + } - /** - * 填写集团名称字段 - * 根据UI树分析,查找hint为"请输入集团名称"的EditText并填入值 - */ - private fun fillGroupNameField(root: AccessibilityNodeInfo, value: String) { - fillEditTextByHintText(root,value, "集团名称","请输入集团名称") - } - private fun fillCompanyNameField(root: AccessibilityNodeInfo, value: String) { - fillEditTextByHintText(root,value, "证件名称","证件名称") - } + // 点击可点击的触发元素来显示底部选择对话框 + fun clickToOpenBottomSheet(root: AccessibilityNodeInfo, triggerText: String) { + val triggerNode = NodeToolText.findNodeByText(root, triggerText) + if (triggerNode != null && triggerNode.isClickable) { + AccessibilityTool.ensureNodeVisible(triggerNode) + AccessibilityTool.performClick(triggerNode) + Thread.sleep(1000) // 等待对话框弹出 + }else{ - private fun fillEditTextByHintText(root: AccessibilityNodeInfo, value: String, fieldName : String, hintText: String) { - var editTextNode = NodeToolHint.findNodeByHint(root, hintText) - fillEditText(editTextNode,value,fieldName) - } - private fun fillEditText(editText: AccessibilityNodeInfo?, value: String, fieldName : String) { - if (editText == null) { - return - } - - if (editText.isEditable) { - AccessibilityTool.inputText(editText, value) - println("已填写$fieldName: $value") - } else { - println("未找到${fieldName}输入框或输入框不可编辑") - } - } - - /** - * 选择集团状态 - * 根据UI树分析,可以通过点击相关视图来展开选项 - */ - fun selectGroupStatus(root: AccessibilityNodeInfo, status: String = "在网集团") { - // 查找包含"在网集团"文本的TextView并点击 - val statusView = NodeToolText.findNodeByText(root, status) - if (statusView != null && statusView.isClickable) { - AccessibilityTool.performClick(statusView) - println("已选择集团状态: $status") - } else { - println("未找到集团状态选项或选项不可点击") - } - } - - /** - * 选择集团等级 - * 根据UI树分析,可以通过点击相关视图来展开选项 - */ - fun selectGroupLevel(root: AccessibilityNodeInfo, level: String = "D") { - // 查找包含"D"文本的TextView并点击 - val levelView = NodeToolText.findNodeByText(root, level) - if (levelView != null && levelView.isClickable) { - AccessibilityTool.performClick(levelView) - println("已选择集团等级: $level") - } else { - println("未找到集团等级选项或选项不可点击") + var parent = triggerNode?.parent + while (parent != null) { + if (parent.isClickable) { + AccessibilityTool.ensureNodeVisible(parent) + AccessibilityTool.performClick(parent) + return + } + parent = parent.parent } } } + + // 完整流程:点击触发 -> 选择选项 + fun performBottomSheetSelection(root: AccessibilityNodeInfo, triggerText: String, optionText: String) { + // 1. 点击触发元素打开底部对话框 + clickToOpenBottomSheet(root, triggerText) + + // 2. 等待对话框完全显示 + Thread.sleep(1000) + + // 3. 选择目标选项 + selectOptionFromBottomSheet(optionText) + + // 4. 明确点击确认按钮 dubbo check + val newRoot = uiInspectorService.rootInActiveWindow + clickConfirmButton(newRoot) + Thread.sleep(500) // 等待确认操作完成 + } + + // 在底部对话框中选择指定选项 + fun selectOptionFromBottomSheet(optionText: String, confirm: Boolean = true) { + // 获取更新后的根节点 + val newRoot = uiInspectorService.rootInActiveWindow + + // 查找并点击选项 + val optionNode = NodeToolText.findNodeByText(newRoot, optionText) + if (optionNode != null) { + AccessibilityTool.ensureNodeVisible(optionNode) + if (optionNode.isClickable) { + AccessibilityTool.performClick(optionNode) + println("已选择底部选项: $optionText") + + // 根据需要点击确认或取消按钮 + if (confirm) { + clickConfirmButton(newRoot) + } else { + clickCancelButton(newRoot) + } + } + } else { + println("未找到底部选项: $optionText") + } + } + + + // 点击确认按钮 + private fun clickConfirmButton(root: AccessibilityNodeInfo): Boolean { + val confirmButton = NodeToolText.findNodeByText(root, "确定") + ?: NodeToolText.findNodeByText(root, "确认") + + println("查找确认按钮: ${confirmButton != null}") + if (confirmButton != null) { + println("确认按钮属性 - clickable: ${confirmButton.isClickable}, visibleToUser: ${confirmButton.isVisibleToUser}") + if (confirmButton.isClickable) { + AccessibilityTool.ensureNodeVisible(confirmButton) + AccessibilityTool.performClick(confirmButton) + println("已点击确认按钮") + return true + } else { + println("确认按钮不可点击") + } + } else { + println("未找到确认按钮") + // 打印当前界面树以便调试 + AccessibilityTool.printViewTree(root, "确认按钮未找到时的界面树") + } + return false + } + + // 点击取消按钮 + private fun clickCancelButton(root: AccessibilityNodeInfo) { + val cancelButton = NodeToolText.findNodeByText(root, "取消") + if (cancelButton != null && cancelButton.isClickable) { + AccessibilityTool.performClick(cancelButton) + println("已点击取消按钮") + } + } + + private fun expandClassificationSection(root: AccessibilityNodeInfo) { + var peopleCountNode = NodeToolText.findNodeByText(root, "请输入人数") + if (peopleCountNode == null) { + println("未找到员工数输入框") + val targetNode = NodeToolText.findNodeByText(root, "分类信息") + val nextSibling = NodeToolText.findNextSiblingNode(targetNode) + + if (nextSibling != null && nextSibling.isClickable) { + AccessibilityTool.ensureNodeVisible(nextSibling) + AccessibilityTool.performClick(nextSibling) + Thread.sleep(2000) + println("已点击兄弟节点") + } else { + println("未找到可点击的兄弟节点") + } + } + } + + /** + * 填写集团名称字段 + * 根据UI树分析,查找hint为"请输入集团名称"的EditText并填入值 + */ + private fun fillGroupNameField(root: AccessibilityNodeInfo, value: String) { + fillEditTextByHintText(root,value, "集团名称","请输入集团名称") + } + + private fun fillCompanyNameField(root: AccessibilityNodeInfo, value: String) { + fillEditTextByHintText(root,value, "证件名称","证件名称") + } + + private fun fillEditTextByHintText(root: AccessibilityNodeInfo, value: String, fieldName : String, hintText: String) { + var editTextNode = NodeToolHint.findNodeByHint(root, hintText) + fillEditText(editTextNode,value,fieldName) + } + private fun fillEditText(editText: AccessibilityNodeInfo?, value: String, fieldName : String) { + if (editText == null) { + return + } + + if (editText.isEditable) { + AccessibilityTool.inputText(editText, value) + println("已填写$fieldName: $value") + } else { + println("未找到${fieldName}输入框或输入框不可编辑") + } + } + + /** + * 选择集团状态 + * 根据UI树分析,可以通过点击相关视图来展开选项 + */ + fun selectGroupStatus(root: AccessibilityNodeInfo, status: String = "在网集团") { + // 查找包含"在网集团"文本的TextView并点击 + val statusView = NodeToolText.findNodeByText(root, status) + if (statusView != null && statusView.isClickable) { + AccessibilityTool.performClick(statusView) + println("已选择集团状态: $status") + } else { + println("未找到集团状态选项或选项不可点击") + } + } + + /** + * 选择集团等级 + * 根据UI树分析,可以通过点击相关视图来展开选项 + */ + fun selectGroupLevel(root: AccessibilityNodeInfo, level: String = "D") { + // 查找包含"D"文本的TextView并点击 + val levelView = NodeToolText.findNodeByText(root, level) + if (levelView != null && levelView.isClickable) { + AccessibilityTool.performClick(levelView) + println("已选择集团等级: $level") + } else { + println("未找到集团等级选项或选项不可点击") + } + } + + } diff --git a/app/src/main/java/com/loveerror/bested/handler/ZJMCCMCRM.kt b/app/src/main/java/com/loveerror/bested/handler/ZJMCCMCRM.kt index a00e4b2..c30602f 100644 --- a/app/src/main/java/com/loveerror/bested/handler/ZJMCCMCRM.kt +++ b/app/src/main/java/com/loveerror/bested/handler/ZJMCCMCRM.kt @@ -10,6 +10,7 @@ import android.view.accessibility.AccessibilityNodeInfo import androidx.core.content.ContextCompat import com.loveerror.bested.SelectAccessibility import com.loveerror.bested.UIInspectorService +import com.loveerror.bested.button.ActionEvent import com.loveerror.bested.tool.AccessibilityTool import com.loveerror.bested.tool.NodeToolText @@ -19,16 +20,33 @@ class ZjMccmCrm(private val uiInspectorService: UIInspectorService) { private var broadcastReceiver: BroadcastReceiver? = null fun initialize(context: Context) { + + val listOfActionEvent = listOf( + ActionEvent.START_ACTION, + ActionEvent.FILL_ACTION, +// ActionEvent.SET_DATA_SOURCE, +// ActionEvent.HIDE_FLOATING_BUTTON, + ActionEvent.PRINT_CURRENT_PAGE + ) // 注册广播接收器 broadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { + when (intent.action) { - "com.loveerror.bested.START_ACTION" -> { -// startJob() + ActionEvent.START_ACTION.event -> { startJobAsync() } - "com.loveerror.bested.FILL_ACTION" -> { - fillAction() + + ActionEvent.FILL_ACTION.event -> { + fillActionAsync() + } + + ActionEvent.PRINT_CURRENT_PAGE.event -> { + AccessibilityTool.printViewTree(uiInspectorService.rootInActiveWindow) + } + + else -> { + println("Received unknown action: ${intent.action}") } } } @@ -36,13 +54,11 @@ class ZjMccmCrm(private val uiInspectorService: UIInspectorService) { } - val filter = IntentFilter("com.loveerror.bested.START_ACTION") - - registerReceiver(filter, context) - - val filter2 = IntentFilter("com.loveerror.bested.FILL_ACTION") - registerReceiver(filter2,context ) + listOfActionEvent.forEach { + val filter = IntentFilter(it.event) + registerReceiver(filter, context) + } selectAccessibility = SelectAccessibility(uiInspectorService) @@ -72,12 +88,16 @@ class ZjMccmCrm(private val uiInspectorService: UIInspectorService) { } } - private fun fillAction() { - val root = rootInActiveWindow() - if (root == null) { - return - } - NormalGroupCreateForm.autoFillForm(root) + private fun fillActionAsync() { + Thread { + val root = rootInActiveWindow() + if (root == null) { + return@Thread + } + var normalGroupCreateForm = NormalGroupCreateForm(uiInspectorService) + normalGroupCreateForm.autoFillForm(root) + }.start() + } fun someOptEvent(event: AccessibilityEvent?){ // 获取根节点 diff --git a/app/src/main/java/com/loveerror/bested/tool/AccessibilityTool.kt b/app/src/main/java/com/loveerror/bested/tool/AccessibilityTool.kt index 8db724b..369c01b 100644 --- a/app/src/main/java/com/loveerror/bested/tool/AccessibilityTool.kt +++ b/app/src/main/java/com/loveerror/bested/tool/AccessibilityTool.kt @@ -76,9 +76,9 @@ class AccessibilityTool { } - fun performClick(btn: AccessibilityNodeInfo) { + fun performClick(btn: AccessibilityNodeInfo, clickParent : Boolean = true) { android.os.Handler(android.os.Looper.getMainLooper()).post { - clickButton(btn) + clickButton(btn,clickParent) } } @@ -95,13 +95,13 @@ class AccessibilityTool { } - fun clickButton(node: AccessibilityNodeInfo) { + fun clickButton(node: AccessibilityNodeInfo, clickParent : Boolean = true) { if (node.isClickable) { node.performAction(AccessibilityNodeInfo.ACTION_CLICK) return - } else { + } else if(clickParent){ // 如果节点本身不可点击,尝试查找可点击的父节点 var parent = node.parent while (parent != null) { diff --git a/app/src/main/java/com/loveerror/bested/tool/NodeToolHint.kt b/app/src/main/java/com/loveerror/bested/tool/NodeToolHint.kt index a783e3d..c673a80 100644 --- a/app/src/main/java/com/loveerror/bested/tool/NodeToolHint.kt +++ b/app/src/main/java/com/loveerror/bested/tool/NodeToolHint.kt @@ -4,9 +4,69 @@ import android.view.accessibility.AccessibilityNodeInfo class NodeToolHint { companion object{ - fun findNodeByHint(node: AccessibilityNodeInfo,vararg targetHints: String): AccessibilityNodeInfo? { - return findNodeByHint(node, 0, targetHints.toList()) + + fun findNodeByHint(root: AccessibilityNodeInfo?, targetHint : String) : AccessibilityNodeInfo?{ + if (root == null) { + println("=== targetHint:${targetHint} findNodeByHint Tree Start [root==null] ===") + println("=== targetHint:${targetHint} findNodeByHint Tree End [root==null] ===") + return null + } + println("=== targetHint:${targetHint} findNodeByHint Tree Start ===") + val node = findNodeByHint(root, 0, targetHint) + println("=== targetHint:${targetHint} findNodeByHint Tree End ===") + return node } + + private fun findNodeByHint(node: AccessibilityNodeInfo, depth: Int, targetHint : String) : AccessibilityNodeInfo?{ + val indent = " ".repeat(depth) + val nodeInfo = "${node.className?.toString() ?: "Unknown"} " + + "[text=${node.text?.toString() ?: ""}] " + + "[desc=${node.contentDescription?.toString() ?: ""}] " + + "[id=${node.viewIdResourceName ?: ""}] " + + "[hint=${node.hintText?.toString() ?: ""}] " + + "[clickable=${node.isClickable}] " + + "[longClickable=${node.isLongClickable}] " + + "[editable=${node.isEditable}] " + + "[focusable=${node.isFocusable}] " + + "[focus=${node.isFocused}] " + + "[selected=${node.isSelected}] " + + "[checkable=${node.isCheckable}] " + + "[checked=${node.isChecked}] " + + "[scrollable=${node.isScrollable}] " + + "[visibleToUser=${node.isVisibleToUser}] " + + "[password=${node.isPassword}] " + + "[multiple=${node.isMultiLine}] " + + "[accessibilityFocus=${node.isAccessibilityFocused}] " + + "[package=${node.packageName?.toString() ?: ""}] " + + "[focused=${node.isFocused}] " + + "[enabled=${node.isEnabled}] " + + "[focusable=${node.isFocusable}] " + + "[accessible=${node.isAccessibilityFocused}] " + + "[visible=${node.isVisibleToUser}] " + + "" + + println("${targetHint} $indent$nodeInfo") + if (node.hintText?.toString()?.contains(targetHint) == true) { + println("=== targetHint:${targetHint} findNodeByHint Tree End [find] ===") + return node + } + + // 递归打印子节点 + for (i in 0 until node.childCount) { + val child = node.getChild(i) + if (child != null) { + val ret =findNodeByHint(child, depth + 1, targetHint) + if (ret != null){ + println("=== targetHint:${targetHint} findNodeByHint Tree End [find] ===") + return ret + } + } + } + return null + } +// fun findNodeByHint(node: AccessibilityNodeInfo,vararg targetHints: String): AccessibilityNodeInfo? { +// return findNodeByHint(node, 0, targetHints.toList()) +// } private fun findNodeByHint(node: AccessibilityNodeInfo, depth: Int, targetHints: List): AccessibilityNodeInfo? { if (targetHints.isEmpty()) return null @@ -19,7 +79,7 @@ class NodeToolHint { if (remainingHints.isEmpty()) { return node } - // 如果还有更多层级,继续在子节点中查找 + // 如果还有更多层级,只在子节点中查找下一级hint else { // 在子节点中查找下一级hint for (i in 0 until node.childCount) { @@ -33,7 +93,7 @@ class NodeToolHint { } } } - // 如果当前节点不匹配且没有更多层级,在子节点中查找第一级hint + // 当前节点不匹配,继续在子节点中查找同一级hint else { for (i in 0 until node.childCount) { val child = node.getChild(i) diff --git a/app/src/main/java/com/loveerror/bested/tool/NodeToolText.kt b/app/src/main/java/com/loveerror/bested/tool/NodeToolText.kt index 181dc77..fea4db4 100644 --- a/app/src/main/java/com/loveerror/bested/tool/NodeToolText.kt +++ b/app/src/main/java/com/loveerror/bested/tool/NodeToolText.kt @@ -5,6 +5,26 @@ import android.view.accessibility.AccessibilityNodeInfo class NodeToolText { companion object{ + // 添加一个新的辅助方法来查找兄弟节点 + fun findNextSiblingNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? { + if (node == null || node.parent == null) return null + + val parent = node.parent + val childCount = parent.childCount + var found = false + + for (i in 0 until childCount) { + val child = parent.getChild(i) + if (found && child != null) { + return child // 返回找到的第一个兄弟节点 + } + if (child == node) { + found = true + } + } + + return null // 没有找到兄弟节点 + } fun findNodeByText(node: AccessibilityNodeInfo,vararg targetTexts: String): AccessibilityNodeInfo? { return findNodeByText(node, 0, targetTexts.toList()) }