@@ -50,6 +50,8 @@ import com.blankj.utilcode.util.SizeUtils
5050import com.ven.assists.window.AssistsWindowManager.nonTouchableByWrapper
5151import androidx.core.graphics.createBitmap
5252import com.blankj.utilcode.util.FileUtils
53+ import com.google.gson.Gson
54+ import com.google.gson.GsonBuilder
5355import com.ven.assists.utils.BitmapUtils
5456
5557/* *
@@ -1316,4 +1318,163 @@ object AssistsCore {
13161318 Log .d(tag, toString())
13171319 }
13181320 }
1321+
1322+ /* *
1323+ * 节点边界信息数据类
1324+ * 包含节点在屏幕中的位置和尺寸信息
1325+ */
1326+ data class NodeBounds (
1327+ val left : Int ,
1328+ val top : Int ,
1329+ val right : Int ,
1330+ val bottom : Int ,
1331+ val width : Int ,
1332+ val height : Int ,
1333+ val centerX : Int ,
1334+ val centerY : Int ,
1335+ val exactCenterX : Float ,
1336+ val exactCenterY : Float ,
1337+ val isEmpty : Boolean ,
1338+ )
1339+
1340+ /* *
1341+ * 节点树数据类
1342+ * 包含节点的所有属性和子节点列表,用于生成树形结构的JSON
1343+ */
1344+ data class NodeTree (
1345+ val packageName : String ,
1346+ val text : String ,
1347+ val des : String ,
1348+ val viewId : String ,
1349+ val className : String ,
1350+ val isScrollable : Boolean ,
1351+ val isClickable : Boolean ,
1352+ val isEnabled : Boolean ,
1353+ val boundsInScreen : NodeBounds ,
1354+ val hintText : String ,
1355+ val isCheckable : Boolean ,
1356+ val isChecked : Boolean ,
1357+ val isFocusable : Boolean ,
1358+ val isFocused : Boolean ,
1359+ val isLongClickable : Boolean ,
1360+ val isPassword : Boolean ,
1361+ val isSelected : Boolean ,
1362+ val isVisibleToUser : Boolean ,
1363+ val drawingOrder : Int ,
1364+ val children : List <NodeTree >
1365+ )
1366+
1367+ /* *
1368+ * 将AccessibilityNodeInfo转换为NodeBounds
1369+ * @return 节点边界信息
1370+ */
1371+ private fun AccessibilityNodeInfo.toNodeBounds (): NodeBounds {
1372+ val rect = getBoundsInScreen()
1373+ return NodeBounds (
1374+ left = rect.left,
1375+ top = rect.top,
1376+ right = rect.right,
1377+ bottom = rect.bottom,
1378+ width = rect.width(),
1379+ height = rect.height(),
1380+ centerX = rect.centerX(),
1381+ centerY = rect.centerY(),
1382+ exactCenterX = rect.exactCenterX(),
1383+ exactCenterY = rect.exactCenterY(),
1384+ isEmpty = rect.isEmpty
1385+ )
1386+ }
1387+
1388+ /* *
1389+ * 将AccessibilityNodeInfo转换为NodeTree(递归包含子节点)
1390+ * @return 节点树对象
1391+ */
1392+ fun AccessibilityNodeInfo.toNodeTree (): NodeTree {
1393+ val children = mutableListOf<NodeTree >()
1394+ for (i in 0 until childCount) {
1395+ getChild(i)?.let { child ->
1396+ children.add(child.toNodeTree())
1397+ }
1398+ }
1399+
1400+ return NodeTree (
1401+ packageName = packageName?.toString() ? : " " ,
1402+ text = text?.toString() ? : " " ,
1403+ des = contentDescription?.toString() ? : " " ,
1404+ viewId = viewIdResourceName ? : " " ,
1405+ className = className?.toString() ? : " " ,
1406+ isScrollable = isScrollable,
1407+ isClickable = isClickable,
1408+ isEnabled = isEnabled,
1409+ boundsInScreen = toNodeBounds(),
1410+ hintText = if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) hintText?.toString() ? : " " else " " ,
1411+ isCheckable = isCheckable,
1412+ isChecked = isChecked,
1413+ isFocusable = isFocusable,
1414+ isFocused = isFocused,
1415+ isLongClickable = isLongClickable,
1416+ isPassword = isPassword,
1417+ isSelected = isSelected,
1418+ isVisibleToUser = isVisibleToUser,
1419+ drawingOrder = drawingOrder,
1420+ children = children
1421+ )
1422+ }
1423+
1424+ /* *
1425+ * 获取当前窗口所有节点的树形结构
1426+ * @return 根节点的NodeTree对象,如果获取失败则返回null
1427+ */
1428+ fun getRootNodeTree (): NodeTree ? {
1429+ return AssistsService .instance?.rootInActiveWindow?.toNodeTree()
1430+ }
1431+
1432+ /* *
1433+ * 获取当前窗口所有节点的JSON字符串(树形结构)
1434+ * @param prettyPrint 是否格式化输出JSON,默认为false
1435+ * @return JSON字符串,如果获取失败则返回空字符串
1436+ */
1437+ fun getRootNodeTreeJson (prettyPrint : Boolean = false): String {
1438+ val nodeTree = getRootNodeTree() ? : return " "
1439+ val gson = if (prettyPrint) {
1440+ GsonBuilder ().setPrettyPrinting().create()
1441+ } else {
1442+ Gson ()
1443+ }
1444+ return gson.toJson(nodeTree)
1445+ }
1446+
1447+ /* *
1448+ * 将指定节点及其所有子节点转换为JSON字符串(树形结构)
1449+ * @param prettyPrint 是否格式化输出JSON,默认为false
1450+ * @return JSON字符串
1451+ */
1452+ fun AccessibilityNodeInfo.toNodeTreeJson (prettyPrint : Boolean = false): String {
1453+ val nodeTree = toNodeTree()
1454+ val gson = if (prettyPrint) {
1455+ GsonBuilder ().setPrettyPrinting().create()
1456+ } else {
1457+ Gson ()
1458+ }
1459+ return gson.toJson(nodeTree)
1460+ }
1461+
1462+ /* *
1463+ * 获取当前窗口所有节点的JSON字符串并保存到文件
1464+ * @param file 保存JSON的文件,默认为应用内部文件路径下的时间戳命名文件
1465+ * @param prettyPrint 是否格式化输出JSON,默认为true
1466+ * @return 保存成功时返回文件对象,失败时返回null
1467+ */
1468+ fun saveRootNodeTreeJson (
1469+ file : File = File (PathUtils .getInternalAppFilesPath() + "/node_tree_${System .currentTimeMillis()}.json"),
1470+ prettyPrint : Boolean = true
1471+ ): File ? {
1472+ val json = getRootNodeTreeJson(prettyPrint)
1473+ if (json.isEmpty()) return null
1474+ return runCatching {
1475+ file.parentFile?.mkdirs()
1476+ file.writeText(json)
1477+ file
1478+ }.getOrNull()
1479+ }
13191480}
0 commit comments