get view string
This commit is contained in:
@@ -11,7 +11,10 @@ enum class ActionEvent {
|
||||
QUERY_COMPANY_REGISTER("com.loveerror.bested.QUERY_COMPANY_REGISTER", "去全网查询",isDisplay = true),
|
||||
QUERY_COMPANY_SEARCH_ACTION("com.loveerror.bested.QUERY_COMPANY_SEARCH_ACTION", "查询公司是否注册",isDisplay = true, menuWithText = listOf("companyNames")),
|
||||
|
||||
COORDINATE_CLICK("coordinate_click", "坐标点击", true),
|
||||
COORDINATE_CLICK("coordinate_click", "获取坐标位置", true),
|
||||
|
||||
COORDINATE_CLICK_TEST("coordinate_click_test", "坐标点击测试", true, menuWithText = listOf("x", "y")),
|
||||
|
||||
UNKNOWN("com.loveerror.bested.UNKNOWN", "未知",isDisplay = false, enable = false);
|
||||
|
||||
|
||||
@@ -40,5 +43,10 @@ enum class ActionEvent {
|
||||
}
|
||||
return UNKNOWN
|
||||
}
|
||||
|
||||
// 新增辅助方法判断是否为打印当前页面事件
|
||||
fun isPrintCurrentPage(event: ActionEvent): Boolean {
|
||||
return event == PRINT_CURRENT_PAGE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,12 +13,21 @@ import android.widget.EditText
|
||||
import android.widget.PopupMenu
|
||||
import android.app.Activity
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Build
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.loveerror.bested.tool.CoordinateClickTool
|
||||
import com.loveerror.bested.tool.CoordinateListener
|
||||
import com.loveerror.bested.tool.CoordinateListenerImpl
|
||||
import com.loveerror.bested.tool.SystemTool
|
||||
|
||||
class DragFloatingButton(context: Context) : View(context) {
|
||||
|
||||
// 添加坐标存储变量
|
||||
private var lastClickX: Float = 0f
|
||||
private var lastClickY: Float = 0f
|
||||
|
||||
private val paint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
style = Paint.Style.FILL
|
||||
@@ -32,6 +41,9 @@ class DragFloatingButton(context: Context) : View(context) {
|
||||
private var originalY = 0
|
||||
private var isDragging = false
|
||||
|
||||
// 在 DragFloatingButton.kt 中添加成员变量
|
||||
private var dialogBroadcastReceiver: android.content.BroadcastReceiver? = null
|
||||
|
||||
init {
|
||||
// 初始化时创建正确的 WindowManager.LayoutParams
|
||||
this.layoutParams = WindowManager.LayoutParams(
|
||||
@@ -40,8 +52,93 @@ class DragFloatingButton(context: Context) : View(context) {
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
|
||||
PixelFormat.TRANSLUCENT
|
||||
)
|
||||
|
||||
// 注册用于接收显示弹窗请求的广播
|
||||
registerDialogReceiver()
|
||||
}
|
||||
|
||||
// 添加注册广播接收器的方法
|
||||
private fun registerDialogReceiver() {
|
||||
dialogBroadcastReceiver = object : android.content.BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (intent.action) {
|
||||
"com.loveerror.bested.SHOW_VIEW_TREE_DIALOG" -> {
|
||||
val viewTreeData = intent.getStringExtra("view_tree_data") ?: ""
|
||||
showViewTreeDialog(viewTreeData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val filter = IntentFilter("com.loveerror.bested.SHOW_VIEW_TREE_DIALOG")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
context.registerReceiver(dialogBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED)
|
||||
} else {
|
||||
ContextCompat.registerReceiver(
|
||||
context,
|
||||
dialogBroadcastReceiver,
|
||||
filter,
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加清理方法
|
||||
fun cleanup() {
|
||||
dialogBroadcastReceiver?.let {
|
||||
try {
|
||||
context.unregisterReceiver(it)
|
||||
} catch (e: Exception) {
|
||||
// 忽略异常
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加显示弹窗的方法
|
||||
private fun showViewTreeDialog(data: String) {
|
||||
// (context as? Activity)?.runOnUiThread {
|
||||
//
|
||||
// }
|
||||
try {
|
||||
val dialogBuilder = AlertDialog.Builder(context)
|
||||
val editText = EditText(context).apply {
|
||||
setText(data)
|
||||
setTextIsSelectable(true)
|
||||
movementMethod = ScrollingMovementMethod.getInstance()
|
||||
setLines(15)
|
||||
gravity = Gravity.TOP or Gravity.START
|
||||
setBackgroundColor(Color.parseColor("#F0F0F0"))
|
||||
}
|
||||
|
||||
val container = LinearLayout(context).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding(20, 20, 20, 20)
|
||||
addView(editText)
|
||||
}
|
||||
|
||||
val dialog = dialogBuilder
|
||||
.setTitle("界面数据")
|
||||
.setView(container)
|
||||
.setPositiveButton("复制") { _, _ ->
|
||||
SystemTool.copyToClipboard(context, data)
|
||||
}
|
||||
.setNegativeButton("关闭", null)
|
||||
.create()
|
||||
//
|
||||
//.show()
|
||||
|
||||
// 设置对话框类型为系统级对话框
|
||||
val window = dialog.window
|
||||
window?.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY)
|
||||
|
||||
dialog.show()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
@@ -72,9 +169,16 @@ class DragFloatingButton(context: Context) : View(context) {
|
||||
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
|
||||
|
||||
|
||||
startX = event.rawX
|
||||
startY = event.rawY
|
||||
|
||||
// 记录点击坐标
|
||||
lastClickX = startX
|
||||
lastClickY = startY
|
||||
|
||||
// 确保 layoutParams 是 WindowManager.LayoutParams 类型
|
||||
if (this.layoutParams is WindowManager.LayoutParams) {
|
||||
layoutParams = this.layoutParams as WindowManager.LayoutParams
|
||||
@@ -124,12 +228,17 @@ class DragFloatingButton(context: Context) : View(context) {
|
||||
// 只在特定条件下移除覆盖层
|
||||
if (!isDragging) {
|
||||
|
||||
|
||||
val intent = android.content.Intent(ActionEvent.COORDINATE_CLICK.event).apply {
|
||||
setPackage(context.packageName)
|
||||
}
|
||||
|
||||
|
||||
val coordinateListenerImpl =
|
||||
CoordinateListenerImpl(event.x, event.y, intent, context = context)
|
||||
CoordinateListenerImpl(event.x, event.y, intent, context = context, callback = fun(x: Float,y: Float){
|
||||
// 记录点击坐标
|
||||
lastClickX = x
|
||||
lastClickY = y
|
||||
})
|
||||
|
||||
coordinateClickTool.disableCoordinateClickMode(coordinateListener = coordinateListenerImpl)
|
||||
|
||||
@@ -146,16 +255,26 @@ class DragFloatingButton(context: Context) : View(context) {
|
||||
private val coordinateClickTool = CoordinateClickTool(context, windowManager)
|
||||
|
||||
|
||||
class FieldNameValue {
|
||||
var name: String = ""
|
||||
var value: String = ""
|
||||
}
|
||||
|
||||
private fun showPopupMenu() {
|
||||
val popup = PopupMenu(context, this)
|
||||
// 添加菜单项 use ActionEvent
|
||||
for (item in ActionEvent.entries){
|
||||
if (item.isDisplay) {
|
||||
val fieldNameValues = ArrayList<FieldNameValue>()
|
||||
if(item == ActionEvent.COORDINATE_CLICK_TEST){
|
||||
// default x,y value use lastClickX,lastClickY
|
||||
fieldNameValues.add(FieldNameValue().apply {name="x";value=lastClickX.toString()})
|
||||
fieldNameValues.add(FieldNameValue().apply {name="y";value=lastClickY.toString()})
|
||||
}
|
||||
// 检查是否需要文本输入
|
||||
if (item.menuWithText.isNotEmpty()) {
|
||||
popup.menu.add(0, item.ordinal, 0, item.menuName).setOnMenuItemClickListener {
|
||||
showTextInputDialog(item)
|
||||
showTextInputDialog(item, fieldNameValues)
|
||||
true
|
||||
}
|
||||
} else {
|
||||
@@ -177,7 +296,13 @@ class DragFloatingButton(context: Context) : View(context) {
|
||||
|
||||
}
|
||||
|
||||
private fun showTextInputDialog(actionEvent: ActionEvent) {
|
||||
private fun showTextInputDialog(
|
||||
actionEvent: ActionEvent,
|
||||
fieldNameValues: ArrayList<FieldNameValue>
|
||||
) {
|
||||
// fieldNameValues 转成key value形式的map
|
||||
|
||||
val fieldNameValuesMap = fieldNameValues.associate { it.name to it.value }
|
||||
println("Attempting to show text input dialog for: ${actionEvent.menuName}")
|
||||
// 根据 actionEvent.menuWithText 创建对应数量的editText 放到一个 linearLayout 中
|
||||
val editTexts = arrayListOf<EditText>()
|
||||
@@ -200,6 +325,8 @@ class DragFloatingButton(context: Context) : View(context) {
|
||||
hint = menuWithText
|
||||
tag = menuWithText
|
||||
|
||||
val fieldNameValue = fieldNameValuesMap[menuWithText]
|
||||
setText(fieldNameValue?:"")
|
||||
// 添加双击全选支持
|
||||
var lastClickTime = 0L
|
||||
val doubleClickThreshold = 300L // 双击时间阈值(毫秒)
|
||||
@@ -287,6 +414,29 @@ class DragFloatingButton(context: Context) : View(context) {
|
||||
ActionEvent.COORDINATE_CLICK -> {
|
||||
coordinateClickTool.enableCoordinateClickMode()
|
||||
}
|
||||
ActionEvent.COORDINATE_CLICK_TEST -> {
|
||||
// 解析坐标参数
|
||||
var xCoord = 0f
|
||||
var yCoord = 0f
|
||||
|
||||
for (textWithTag in textInputs) {
|
||||
when (textWithTag.tag) {
|
||||
"x" -> xCoord = textWithTag.text.toFloatOrNull() ?: 0f
|
||||
"y" -> yCoord = textWithTag.text.toFloatOrNull() ?: 0f
|
||||
}
|
||||
}
|
||||
|
||||
// 创建坐标监听器并执行点击
|
||||
val intent = android.content.Intent(actionEvent.event).apply {
|
||||
setPackage(context.packageName)
|
||||
}
|
||||
|
||||
val coordinateListener = CoordinateListenerImpl(xCoord, yCoord, intent, context = context)
|
||||
coordinateClickTool.disableCoordinateClickMode(coordinateListener = coordinateListener)
|
||||
|
||||
context.sendBroadcast(intent)
|
||||
|
||||
}
|
||||
else -> {
|
||||
val intent = android.content.Intent(actionEvent.event).apply {
|
||||
setPackage(context.packageName)
|
||||
|
||||
@@ -40,7 +40,16 @@ class ZjMccmCrm(private val rootNodeCallback: RootNodeCallback) {
|
||||
}
|
||||
|
||||
ActionEvent.PRINT_CURRENT_PAGE.event -> {
|
||||
AccessibilityTool.printViewTree(rootNodeCallback.getRootNodeReceived())
|
||||
// AccessibilityTool.printViewTree(rootNodeCallback.getRootNodeReceived())
|
||||
// 修改此处:获取界面数据并发送回 UI 界面显示
|
||||
val viewTreeData = AccessibilityTool.getViewTreeAsString(rootNodeCallback.getRootNodeReceived())
|
||||
|
||||
// 发送广播给 DragFloatingButton 显示弹窗
|
||||
val resultIntent = Intent("com.loveerror.bested.SHOW_VIEW_TREE_DIALOG").apply {
|
||||
putExtra("view_tree_data", viewTreeData)
|
||||
setPackage(context.packageName)
|
||||
}
|
||||
context.sendBroadcast(resultIntent)
|
||||
}
|
||||
|
||||
ActionEvent.QUERY_COMPANY_REGISTER.event -> {
|
||||
|
||||
@@ -434,6 +434,51 @@ class AccessibilityTool {
|
||||
}
|
||||
}
|
||||
|
||||
// 新增返回字符串版本的 printViewTree 方法
|
||||
fun getViewTreeAsString(root: AccessibilityNodeInfo?): String {
|
||||
if (root == null) return "Root node is null"
|
||||
|
||||
val sb = StringBuilder()
|
||||
printNodeToString(root, 0, sb)
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
// 修改 printNodeToString 方法以包含更多布局信息
|
||||
private fun printNodeToString(node: AccessibilityNodeInfo?, depth: Int, sb: StringBuilder) {
|
||||
if (node == null) return
|
||||
|
||||
val indent = " ".repeat(depth)
|
||||
sb.append("$indent${node.className}: \"${node.text}\"")
|
||||
|
||||
if (node.contentDescription != null) {
|
||||
sb.append(" desc:\"${node.contentDescription}\"")
|
||||
}
|
||||
|
||||
if (node.viewIdResourceName != null) {
|
||||
sb.append(" id:${node.viewIdResourceName}")
|
||||
}
|
||||
|
||||
// 添加节点的布局信息
|
||||
val bounds = android.graphics.Rect()
|
||||
node.getBoundsInScreen(bounds)
|
||||
sb.append(" bounds:[${bounds.left},${bounds.top}][${bounds.right},${bounds.bottom}]")
|
||||
|
||||
// 添加节点的其他重要属性
|
||||
sb.append(" clickable:${node.isClickable}")
|
||||
sb.append(" visible:${node.isVisibleToUser}")
|
||||
sb.append(" enabled:${node.isEnabled}")
|
||||
sb.append(" focusable:${node.isFocusable}")
|
||||
sb.append(" focused:${node.isFocused}")
|
||||
sb.append(" selected:${node.isSelected}")
|
||||
sb.append(" scrollable:${node.isScrollable}")
|
||||
|
||||
sb.append("\n")
|
||||
|
||||
for (i in 0 until node.childCount) {
|
||||
printNodeToString(node.getChild(i), depth + 1, sb)
|
||||
}
|
||||
}
|
||||
|
||||
fun printViewTree(root: AccessibilityNodeInfo?, pre : String = "") {
|
||||
if (root == null) {
|
||||
println("=== ${pre} View Tree Start [root==null] ===")
|
||||
@@ -471,6 +516,7 @@ class AccessibilityTool {
|
||||
"[focusable=${node.isFocusable}] " +
|
||||
"[accessible=${node.isAccessibilityFocused}] " +
|
||||
"[visible=${node.isVisibleToUser}] " +
|
||||
"[nodeToString=$node] " +
|
||||
""
|
||||
|
||||
println("${pre} $indent$nodeInfo")
|
||||
|
||||
@@ -7,13 +7,19 @@ interface CoordinateListener {
|
||||
fun onDisable()
|
||||
}
|
||||
|
||||
class CoordinateListenerImpl(private val x: Float,private val y: Float,private val intent: android.content.Intent,private val context: Context): CoordinateListener {
|
||||
fun OtherAction() : Unit {
|
||||
|
||||
}
|
||||
class CoordinateListenerImpl(private val x: Float,private val y: Float,private val intent: android.content.Intent,private val context: Context, private val callback: ((x: Float,y: Float) -> Unit)? = null): CoordinateListener {
|
||||
|
||||
override fun onDisable() {
|
||||
|
||||
intent.putExtra("x",x)
|
||||
intent.putExtra("y",y)
|
||||
context.sendBroadcast(intent)
|
||||
|
||||
// 执行回调函数
|
||||
callback?.invoke(x, y)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -34,16 +40,20 @@ class CoordinateClickTool(private val context: Context, private val windowManage
|
||||
}
|
||||
|
||||
fun disableCoordinateClickMode(coordinateListener: CoordinateListener? = null) {
|
||||
val canSend = isCoordinateClickMode
|
||||
// val canSend = isCoordinateClickMode
|
||||
|
||||
isCoordinateClickMode = false
|
||||
overlayTool.removeOverlayView()
|
||||
|
||||
if(canSend){
|
||||
coordinateListener?.onDisable()
|
||||
}
|
||||
// if(canSend){
|
||||
// coordinateListener?.onDisable()
|
||||
// }
|
||||
}
|
||||
|
||||
// fun sendCoordinate(x: Float, y: Float, intent: android.content.Intent, callback: ((x: Float,y: Float) -> Unit)? = null) {
|
||||
// callback?.invoke(x, y)
|
||||
// }
|
||||
|
||||
fun toggleCoordinateClickMode() {
|
||||
if (isCoordinateClickMode) {
|
||||
disableCoordinateClickMode()
|
||||
|
||||
26
app/src/main/java/com/loveerror/bested/tool/SystemTool.kt
Normal file
26
app/src/main/java/com/loveerror/bested/tool/SystemTool.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.loveerror.bested.tool
|
||||
|
||||
import android.content.Context
|
||||
|
||||
class SystemTool {
|
||||
companion object{
|
||||
fun getCurrentTime(): String {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
return currentTime.toString()
|
||||
}
|
||||
// 添加复制到剪贴板的方法
|
||||
fun copyToClipboard(context: Context,text: String) {
|
||||
try {
|
||||
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
|
||||
val clip = android.content.ClipData.newPlainText("界面数据", text)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
|
||||
// 显示复制成功提示
|
||||
android.widget.Toast.makeText(context, "已复制到剪贴板", android.widget.Toast.LENGTH_SHORT).show()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
android.widget.Toast.makeText(context, "复制失败", android.widget.Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user