probe-dump.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. /**
  2. * probe-dump.js — 把当前屏幕的控件树完整 dump 到一个文本文件
  3. *
  4. * 为什么不用 `uiautomator dump`:TikTok 的页面一直在动(视频、动画),
  5. * uiautomator 等不到 idle state 就直接失败(实测 "could not get idle state")。
  6. * 而 AutoX 的无障碍读取不需要 idle,且**与引擎最终用的是同一套读取路径** ——
  7. * 探针看到的和引擎将来看到的完全一致。
  8. *
  9. * 用法:adb 启动本脚本,几秒后读 /sdcard/tiktok_app/probe-dump.txt
  10. */
  11. var OUT = "/sdcard/tiktok_app/probe-dump.txt";
  12. function clean(v) {
  13. if (v === null || v === undefined) { return ""; }
  14. return String(v).replace(/[\u200b-\u200f\u202a-\u202e\ufeff]/g, "").replace(/\s+/g, " ").replace(/^\s+|\s+$/g, "");
  15. }
  16. function collect() {
  17. var lines = [];
  18. var start = auto.root;
  19. if (!start) { return ["root=null"]; }
  20. var stack = [{ node: start, depth: 0 }];
  21. var count = 0;
  22. while (stack.length > 0 && count < 2000) {
  23. var item = stack.pop();
  24. var node = item.node;
  25. if (!node) { continue; }
  26. count += 1;
  27. var box = null;
  28. var text = "", desc = "", cls = "", rid = "", clickable = false, scrollable = false, editable = false, selected = false;
  29. try { box = node.bounds(); } catch (e) { box = null; }
  30. try { text = clean(node.text()); } catch (e) { }
  31. try { desc = clean(node.desc()); } catch (e) { }
  32. try { cls = String(node.className()); } catch (e) { }
  33. try { rid = String(node.id() || ""); } catch (e) { }
  34. try { clickable = node.clickable() === true; } catch (e) { }
  35. try { scrollable = node.scrollable() === true; } catch (e) { }
  36. try { editable = node.className() === "android.widget.EditText"; } catch (e) { }
  37. try { selected = node.selected() === true; } catch (e) { }
  38. var visible = false;
  39. try { visible = node.isVisibleToUser() === true; } catch (e) { visible = true; }
  40. if (visible && (text !== "" || desc !== "" || clickable || scrollable)) {
  41. var short = cls.split(".").pop();
  42. var shortId = rid.indexOf("/") >= 0 ? rid.split("/").pop() : rid;
  43. lines.push([
  44. item.depth,
  45. short,
  46. shortId,
  47. box ? ("[" + box.left + "," + box.top + "][" + box.right + "," + box.bottom + "]") : "[]",
  48. clickable ? "CLK" : "-",
  49. scrollable ? "SCR" : "-",
  50. editable ? "EDT" : "-",
  51. selected ? "SEL" : "-",
  52. JSON.stringify(text),
  53. JSON.stringify(desc)
  54. ].join(" "));
  55. }
  56. var childCount = 0;
  57. try { childCount = node.childCount(); } catch (e) { childCount = 0; }
  58. for (var i = childCount - 1; i >= 0; i -= 1) {
  59. try { stack.push({ node: node.child(i), depth: item.depth + 1 }); } catch (e) { }
  60. }
  61. }
  62. lines.unshift("total=" + count + " screen=" + device.width + "x" + device.height +
  63. " pkg=" + (auto.root ? auto.root.packageName() : "?"));
  64. return lines;
  65. }
  66. var lines = collect();
  67. files.ensureDir("/sdcard/tiktok_app/");
  68. files.write(OUT, lines.join("\n") + "\n");
  69. console.log("probe written: " + OUT + " lines=" + lines.length);