B2B网络软件

标题: AIWROK adb.exec() 实用工具库 —— 常用功能直接调用版 [打印本页]

作者: YYPOST群发软件    时间: 2 小时前
标题: AIWROK adb.exec() 实用工具库 —— 常用功能直接调用版

AIWROK adb.exec() 实用工具库 —— 常用功能直接调用版
AIWROK adb.exec() 实用工具库 —— 常用功能直接调用版 B2B网络软件


  1. // =============================================================================
  2. // AIWROK adb.exec() 实用工具库 —— 常用功能直接调用版
  3. // =============================================================================
  4. // 适用平台:AIWROK 安卓自动化软件
  5. // 引擎:Rhino (ES5)
  6. // 说明:从 adb_exec高级用法示例.js 中抽取最常用的功能,
  7. //       封装成可直接调用的工具函数,拿来即用。
  8. //
  9. // adb.exec(command, isRoot) 参数说明:
  10. //   - command: shell 命令字符串
  11. //   - isRoot:  true=以 root 权限执行(su -c), false=普通权限
  12. //   - 返回值:  JSON 格式 {"errorMsg":"","success":"实际内容"}
  13. //
  14. // 使用方式:
  15. //   方式1:直接运行本脚本 → 查看各功能演示输出
  16. //   方式2:将本文件内容复制到你的项目中 → 调用 AdbUtil.方法名()
  17. // =============================================================================

  18. print.log("===== adb.exec() 实用工具库 =====\n");

  19. // =============================================================================
  20. // 核心工具层:返回值清洗 + 安全执行
  21. // =============================================================================

  22. /**
  23. * 清洗 adb.exec 返回值
  24. * 处理两种格式:JSON {"success":"..."} 和旧格式 successMsg:...,errorMsg:...
  25. */
  26. function 清洗输出(原始结果) {
  27.     if (!原始结果) return "";
  28.     var 结果 = String(原始结果);

  29.     // 格式1: JSON {"errorMsg":"","success":"实际内容"}
  30.     if (结果.charAt(0) === "{") {
  31.         try {
  32.             var json = JSON.parse(结果);
  33.             if (json && json.success !== undefined) {
  34.                 // 过滤 Broken pipe 警告(| head/grep 提前关闭管道导致,不影响实际结果)
  35.                 if (json.errorMsg && json.errorMsg !== "" &&
  36.                     json.errorMsg.indexOf("Broken pipe") === -1 &&
  37.                     json.errorMsg.indexOf("Failed to write") === -1) {
  38.                     print.log("  [警告] 命令错误: " + json.errorMsg);
  39.                 }
  40.                 return String(json.success).trim();
  41.             }
  42.         } catch (e) {
  43.             // JSON.parse 失败,正则提取 success 字段
  44.             var m = 结果.match(/"success"\s*:\s*"([\s\S]*?)"\s*[,}]/);
  45.             if (m) {
  46.                 return m[1].replace(/\\n/g, "\n").replace(/\\t/g, "\t")
  47.                            .replace(/\\"/g, '"').replace(/\\\\/g, "\\").trim();
  48.             }
  49.         }
  50.         return 结果.trim();
  51.     }

  52.     // 格式2: 旧格式 successMsg:内容,errorMsg:内容
  53.     if (结果.indexOf("successMsg:") === 0) {
  54.         结果 = 结果.substring("successMsg:".length);
  55.     }
  56.     var 错误索引 = 结果.indexOf(",errorMsg:");
  57.     if (错误索引 !== -1) {
  58.         结果 = 结果.substring(0, 错误索引);
  59.     }
  60.     return 结果.trim();
  61. }

  62. /**
  63. * 安全执行命令并返回清洗后的结果
  64. * @param {string} 命令 - shell 命令
  65. * @param {boolean} 是否root - 是否以 root 权限执行
  66. * @returns {string} 清洗后的输出
  67. */
  68. function 执行命令(命令, 是否root) {
  69.     try {
  70.         var 原始 = adb.exec(命令, 是否root || false);
  71.         return 清洗输出(String(原始));
  72.     } catch (e) {
  73.         print.log("  [异常] " + 命令 + " → " + e.message);
  74.         return "";
  75.     }
  76. }

  77. /**
  78. * 安全执行命令(不关心返回值,只关心是否成功)
  79. * @returns {boolean} 是否执行无异常
  80. */
  81. function 执行命令静默(命令, 是否root) {
  82.     try {
  83.         adb.exec(命令, 是否root || false);
  84.         return true;
  85.     } catch (e) {
  86.         return false;
  87.     }
  88. }


  89. // =============================================================================
  90. // 模块一:设备信息采集
  91. // =============================================================================

  92. var AdbUtil = {};

  93. /**
  94. * 获取设备基础信息(型号/品牌/安卓版本/SDK/序列号)
  95. * @returns {Object} 设备信息对象
  96. */
  97. AdbUtil.获取设备信息 = function() {
  98.     return {
  99.         型号:   执行命令("getprop ro.product.model", false),
  100.         品牌:   执行命令("getprop ro.product.brand", false),
  101.         安卓版本: 执行命令("getprop ro.build.version.release", false),
  102.         SDK版本:  执行命令("getprop ro.build.version.sdk", false),
  103.         制造商:  执行命令("getprop ro.product.manufacturer", false),
  104.         序列号:  执行命令("getprop ro.serialno", false)
  105.     };
  106. };

  107. /**
  108. * 获取屏幕分辨率和密度
  109. * @returns {Object} {分辨率:"Physical size: 1080x2400", 密度:"Physical density: 440"}
  110. */
  111. AdbUtil.获取屏幕信息 = function() {
  112.     return {
  113.         分辨率: 执行命令("wm size", false),
  114.         密度:   执行命令("wm density", false)
  115.     };
  116. };

  117. /**
  118. * 获取电池信息(多策略:dumpsys battery → sysfs 文件解析)
  119. * @returns {Object} {电量:"85", 温度:"32.5°C", 状态:"充电中", 技术:"Li-ion"}
  120. */
  121. AdbUtil.获取电池信息 = function() {
  122.     var 电池 = { 电量: "", 温度: "", 状态: "", 技术: "" };
  123.     var 状态映射 = {
  124.         "Charging": "充电中", "Discharging": "放电中",
  125.         "Full": "已充满", "Not charging": "未充电",
  126.         "2": "充电中", "3": "放电中", "4": "已充满", "5": "未充电"
  127.     };

  128.     // 策略1:dumpsys battery(需要 root 权限,普通权限缺少 DUMP 权限)
  129.     var 电池原始 = 执行命令("dumpsys battery", true);
  130.     if (电池原始 && 电池原始.length > 0 && 电池原始.indexOf("Permission Denial") === -1) {
  131.         var 行1 = 电池原始.split("\n");
  132.         for (var i = 0; i < 行1.length; i++) {
  133.             var r = 行1[i].trim();
  134.             if (r.indexOf("level:") !== -1)
  135.                 电池.电量 = r.replace("level:", "").trim();
  136.             if (r.indexOf("temperature:") !== -1) {
  137.                 var t = parseInt(r.replace("temperature:", "").trim(), 10);
  138.                 电池.温度 = isNaN(t) ? "" : (t / 10) + "°C";
  139.             }
  140.             if (r.indexOf("status:") !== -1) {
  141.                 var s = r.replace("status:", "").trim();
  142.                 电池.状态 = 状态映射[s] || s || "";
  143.             }
  144.             if (r.indexOf("technology:") !== -1)
  145.                 电池.技术 = r.replace("technology:", "").trim();
  146.         }
  147.     }

  148.     // 策略2:sysfs 文件系统(root 权限,路径可能因设备不同而异)
  149.     if (!电池.电量) {
  150.         var 路径列表 = [
  151.             "/sys/class/power_supply/battery",
  152.             "/sys/class/power_supply/bms",
  153.             "/sys/class/power_supply/max170xx_battery"
  154.         ];
  155.         for (var pi = 0; pi < 路径列表.length && !电池.电量; pi++) {
  156.             var 基础路径 = 路径列表[pi];
  157.             var cap = 执行命令("cat " + 基础路径 + "/capacity", true);
  158.             if (cap && /^\d+$/.test(cap.trim())) 电池.电量 = cap.trim();
  159.             var 温度原始 = 执行命令("cat " + 基础路径 + "/temp", true);
  160.             if (温度原始 && /^\d+$/.test(温度原始.trim())) {
  161.                 var t2 = parseInt(温度原始.trim(), 10);
  162.                 电池.温度 = isNaN(t2) ? "" : (t2 / 10) + "°C";
  163.             }
  164.             var 状态原始 = 执行命令("cat " + 基础路径 + "/status", true);
  165.             if (状态原始 && 状态原始.indexOf("No such") === -1 && 状态原始.indexOf("Permission") === -1)
  166.                 电池.状态 = 状态映射[状态原始.trim()] || 状态原始.trim() || "";
  167.             var 技术原始 = 执行命令("cat " + 基础路径 + "/technology", true);
  168.             if (技术原始 && 技术原始.indexOf("No such") === -1 && 技术原始.indexOf("Permission") === -1)
  169.                 电池.技术 = 技术原始.trim();
  170.         }
  171.     }

  172.     return 电池;
  173. };

  174. /**
  175. * 获取内存信息
  176. * @returns {string} MemTotal / MemFree / MemAvailable 行
  177. */
  178. AdbUtil.获取内存信息 = function() {
  179.     return 执行命令("cat /proc/meminfo | head -3", false);
  180. };

  181. /**
  182. * 获取存储信息
  183. * @returns {string} df 输出
  184. */
  185. AdbUtil.获取存储信息 = function() {
  186.     return 执行命令("df /sdcard", false);
  187. };


  188. // =============================================================================
  189. // 模块二:应用管理
  190. // =============================================================================

  191. /**
  192. * 列出所有第三方应用包名
  193. * @returns {Array} 包名数组,如 ["com.tencent.mm", "com.taobao.taobao", ...]
  194. */
  195. AdbUtil.列出第三方应用 = function() {
  196.     var 输出 = 执行命令("pm list packages -3", false);
  197.     if (!输出) return [];
  198.     var 行 = 输出.split("\n");
  199.     var 结果 = [];
  200.     for (var i = 0; i < 行.length; i++) {
  201.         var 包名 = 行[i].replace("package:", "").trim();
  202.         if (包名) 结果.push(包名);
  203.     }
  204.     return 结果;
  205. };

  206. /**
  207. * 检查应用是否已安装
  208. * @param {string} 包名 - 应用包名
  209. * @returns {boolean} 是否已安装
  210. */
  211. AdbUtil.应用是否安装 = function(包名) {
  212.     var 结果 = 执行命令("pm list packages " + 包名, false);
  213.     return !!(结果 && 结果.indexOf(包名) !== -1);
  214. };

  215. /**
  216. * 启动应用
  217. * @param {string} 包名 - 应用包名
  218. * @returns {boolean} 是否执行成功
  219. */
  220. AdbUtil.启动应用 = function(包名) {
  221.     // 方式1:monkey 启动(最通用)
  222.     var 结果 = 执行命令静默("monkey -p " + 包名 + " 1 2>/dev/null", false);
  223.     if (结果) return true;
  224.     // 方式2:am start(需要知道主 Activity,这里用包名尝试)
  225.     return 执行命令静默("am start -n " + 包名 + "/.MainActivity 2>/dev/null", false);
  226. };

  227. /**
  228. * 通过 Action 启动系统页面
  229. * @param {string} action - Android action 常量
  230. * @returns {boolean}
  231. */
  232. AdbUtil.启动Action = function(action) {
  233.     return 执行命令静默("am start -a " + action, false);
  234. };

  235. /**
  236. * 强制停止应用
  237. * @param {string} 包名
  238. * @returns {boolean}
  239. */
  240. AdbUtil.停止应用 = function(包名) {
  241.     return 执行命令静默("am force-stop " + 包名, false);
  242. };

  243. /**
  244. * 安装 APK
  245. * @param {string} apk路径 - APK 文件路径,如 /sdcard/app.apk
  246. * @param {boolean} 覆盖安装 - true 使用 -r 覆盖安装
  247. * @returns {boolean}
  248. */
  249. AdbUtil.安装应用 = function(apk路径, 覆盖安装) {
  250.     var 参数 = 覆盖安装 ? "-r " : "";
  251.     var 结果 = 执行命令("pm install " + 参数 + apk路径, false);
  252.     return !!(结果 && 结果.indexOf("Success") !== -1);
  253. };

  254. /**
  255. * 卸载应用
  256. * @param {string} 包名
  257. * @returns {boolean}
  258. */
  259. AdbUtil.卸载应用 = function(包名) {
  260.     var 结果 = 执行命令("pm uninstall " + 包名, false);
  261.     return !!(结果 && 结果.indexOf("Success") !== -1);
  262. };

  263. /**
  264. * 获取应用版本信息
  265. * @param {string} 包名
  266. * @returns {Object} {版本名:"1.0.0", 版本号:"10", 安装路径:"/data/app/..."}
  267. */
  268. AdbUtil.获取应用信息 = function(包名) {
  269.     var 信息 = { 版本名: "", 版本号: "", 安装路径: "" };

  270.     // 策略1:pm path 获取安装路径(轻量命令,不卡死)
  271.     var 路径 = 执行命令("pm path " + 包名, false);
  272.     if (路径) 信息.安装路径 = 路径.replace("package:", "").trim();

  273.     // 策略2:dumpsys package | grep version(grep 读取完整输出但只输出匹配行,JSON返回值很小)
  274.     // 注意:不能用 head 截断(版本信息在输出末尾的 Packages 段,head 会截掉)
  275.     var 版本输出 = 执行命令("dumpsys package " + 包名 + " | grep version", true);
  276.     if (版本输出 && 版本输出.length > 0 && 版本输出.indexOf("Permission Denial") === -1) {
  277.         var 行数组 = 版本输出.split("\n");
  278.         for (var i = 0; i < 行数组.length; i++) {
  279.             var r = 行数组[i].trim();
  280.             if (r.indexOf("versionName=") !== -1 && !信息.版本名) {
  281.                 信息.版本名 = r.replace("versionName=", "").split(" ")[0].trim();
  282.             } else if (r.indexOf("versionName ") !== -1 && !信息.版本名) {
  283.                 信息.版本名 = r.replace("versionName ", "").split(" ")[0].trim();
  284.             }
  285.             if (r.indexOf("versionCode=") !== -1 && !信息.版本号) {
  286.                 信息.版本号 = r.replace("versionCode=", "").split(" ")[0].trim();
  287.             } else if (r.indexOf("versionCode ") !== -1 && !信息.版本号) {
  288.                 信息.版本号 = r.replace("versionCode ", "").split(" ")[0].trim();
  289.             }
  290.             // 也处理 "versionName=xxx" 格式(不带空格的整行)
  291.             if (r.indexOf("versionName=") !== -1 && !信息.版本名) {
  292.                 var m = r.match(/versionName=(\S+)/);
  293.                 if (m) 信息.版本名 = m[1];
  294.             }
  295.             if (r.indexOf("versionCode=") !== -1 && !信息.版本号) {
  296.                 var m2 = r.match(/versionCode=(\S+)/);
  297.                 if (m2) 信息.版本号 = m2[1];
  298.             }
  299.         }
  300.     }

  301.     // 策略3:pm dump | grep version(回退方案)
  302.     if (!信息.版本名) {
  303.         var pm输出 = 执行命令("pm dump " + 包名 + " | grep version", true);
  304.         if (pm输出 && pm输出.length > 0 && pm输出.indexOf("Permission Denial") === -1) {
  305.             var pm行 = pm输出.split("\n");
  306.             for (var j = 0; j < pm行.length; j++) {
  307.                 var pr = pm行[j].trim();
  308.                 if (pr.indexOf("versionName=") !== -1 && !信息.版本名) {
  309.                     var pmM = pr.match(/versionName=(\S+)/);
  310.                     if (pmM) 信息.版本名 = pmM[1];
  311.                 }
  312.                 if (pr.indexOf("versionCode=") !== -1 && !信息.版本号) {
  313.                     var pmM2 = pr.match(/versionCode=(\S+)/);
  314.                     if (pmM2) 信息.版本号 = pmM2[1];
  315.                 }
  316.             }
  317.         }
  318.     }

  319.     return 信息;
  320. };


  321. // =============================================================================
  322. // 模块三:文件操作
  323. // =============================================================================

  324. /**
  325. * 列出目录内容
  326. * @param {string} 目录路径
  327. * @returns {string} ls -la 输出
  328. */
  329. AdbUtil.列目录 = function(目录路径) {
  330.     return 执行命令("ls -la " + 目录路径, false);
  331. };

  332. /**
  333. * 读取文件内容
  334. * @param {string} 文件路径
  335. * @returns {string} 文件内容
  336. */
  337. AdbUtil.读文件 = function(文件路径) {
  338.     return 执行命令("cat " + 文件路径 + " 2>/dev/null", false);
  339. };

  340. /**
  341. * 写入文件(覆盖写入)
  342. * @param {string} 文件路径
  343. * @param {string} 内容 - 仅支持不含单引号的内容,含单引号需自行转义
  344. * @returns {boolean}
  345. */
  346. AdbUtil.写文件 = function(文件路径, 内容) {
  347.     return 执行命令静默("echo '" + 内容 + "' > " + 文件路径, false);
  348. };

  349. /**
  350. * 追加写入文件
  351. * @param {string} 文件路径
  352. * @param {string} 内容
  353. * @returns {boolean}
  354. */
  355. AdbUtil.追加写文件 = function(文件路径, 内容) {
  356.     return 执行命令静默("echo '" + 内容 + "' >> " + 文件路径, false);
  357. };

  358. /**
  359. * 复制文件
  360. * @param {string} 源路径
  361. * @param {string} 目标路径
  362. * @returns {boolean}
  363. */
  364. AdbUtil.复制文件 = function(源路径, 目标路径) {
  365.     return 执行命令静默("cp " + 源路径 + " " + 目标路径 + " 2>/dev/null", false);
  366. };

  367. /**
  368. * 创建目录(递归创建)
  369. * @param {string} 目录路径
  370. * @returns {boolean}
  371. */
  372. AdbUtil.创建目录 = function(目录路径) {
  373.     return 执行命令静默("mkdir -p " + 目录路径, false);
  374. };

  375. /**
  376. * 删除文件或目录
  377. * @param {string} 路径
  378. * @returns {boolean}
  379. */
  380. AdbUtil.删除 = function(路径) {
  381.     return 执行命令静默("rm -rf " + 路径 + " 2>/dev/null", false);
  382. };

  383. /**
  384. * 获取文件大小(字节)
  385. * @param {string} 文件路径
  386. * @returns {number} 文件大小(字节),失败返回 -1
  387. */
  388. AdbUtil.文件大小 = function(文件路径) {
  389.     var 结果 = 执行命令("stat -c %s " + 文件路径 + " 2>/dev/null", false);
  390.     var 大小 = parseInt(结果.trim(), 10);
  391.     return isNaN(大小) ? -1 : 大小;
  392. };


  393. // =============================================================================
  394. // 模块四:输入模拟
  395. // =============================================================================

  396. /**
  397. * 模拟点击坐标
  398. * @param {number} x - X 坐标
  399. * @param {number} y - Y 坐标
  400. * @returns {boolean}
  401. */
  402. AdbUtil.点击 = function(x, y) {
  403.     return 执行命令静默("input tap " + x + " " + y, false);
  404. };

  405. /**
  406. * 模拟滑动
  407. * @param {number} x1 - 起点 X
  408. * @param {number} y1 - 起点 Y
  409. * @param {number} x2 - 终点 X
  410. * @param {number} y2 - 终点 Y
  411. * @param {number} 时长 - 滑动时长(毫秒),默认 300
  412. * @returns {boolean}
  413. */
  414. AdbUtil.滑动 = function(x1, y1, x2, y2, 时长) {
  415.     时长 = 时长 || 300;
  416.     return 执行命令静默("input swipe " + x1 + " " + y1 + " " + x2 + " " + y2 + " " + 时长, false);
  417. };

  418. /**
  419. * 模拟长按(起点终点相同,时长 > 500ms)
  420. * @param {number} x
  421. * @param {number} y
  422. * @param {number} 时长 - 长按时长(毫秒),默认 1000
  423. * @returns {boolean}
  424. */
  425. AdbUtil.长按 = function(x, y, 时长) {
  426.     时长 = 时长 || 1000;
  427.     return 执行命令静默("input swipe " + x + " " + y + " " + x + " " + y + " " + 时长, false);
  428. };

  429. /**
  430. * 模拟输入文字(仅支持 ASCII)
  431. * @param {string} 文字
  432. * @returns {boolean}
  433. */
  434. AdbUtil.输入文字 = function(文字) {
  435.     // input text 不支持空格,需用 %s 替代
  436.     var 替换 = 文字.replace(/ /g, "%s");
  437.     return 执行命令静默("input text '" + 替换 + "'", false);
  438. };

  439. /**
  440. * 模拟按键事件
  441. * @param {number|string} 键码 - KeyEvent 键码,如 4=返回, 3=Home, 26=电源
  442. * @returns {boolean}
  443. *
  444. * 常用键码速查:
  445. *   3=HOME  4=BACK  5=呼叫  6=挂断  24=音量+  25=音量-
  446. *   26=电源  27=相机  66=回车  82=菜单  84=搜索  279=粘贴
  447. */
  448. AdbUtil.按键 = function(键码) {
  449.     return 执行命令静默("input keyevent " + 键码, false);
  450. };

  451. /**
  452. * 模拟返回键
  453. */
  454. AdbUtil.返回键 = function() {
  455.     return AdbUtil.按键(4);
  456. };

  457. /**
  458. * 模拟 Home 键
  459. */
  460. AdbUtil.Home键 = function() {
  461.     return AdbUtil.按键(3);
  462. };


  463. // =============================================================================
  464. // 模块五:前台应用检测 + 进程管理
  465. // =============================================================================

  466. /**
  467. * 获取当前前台包名(多策略:dumpsys window → dumpsys activity → 原生方法)
  468. * @returns {string} 前台包名,如 "com.tencent.mm"
  469. */
  470. AdbUtil.获取前台包名 = function() {
  471.     // 策略1:dumpsys activity activities | head -50(限制输出防止卡死,mResumedActivity在前30行内)
  472.     var activity输出 = 执行命令("dumpsys activity activities | head -50", true);
  473.     if (activity输出 && activity输出.length > 0 && activity输出.indexOf("Permission Denial") === -1) {
  474.         var 行数组 = activity输出.split("\n");
  475.         for (var fi = 0; fi < 行数组.length; fi++) {
  476.             var 行 = 行数组[fi].trim();
  477.             if (行.indexOf("mResumedActivity") !== -1 ||
  478.                 行.indexOf("topResumedActivity") !== -1 ||
  479.                 行.indexOf("ResumedActivity") !== -1) {
  480.                 var 斜杠 = 行.indexOf("/");
  481.                 if (斜杠 !== -1) {
  482.                     var 前段 = 行.substring(0, 斜杠);
  483.                     var 空格 = 前段.lastIndexOf(" ");
  484.                     if (空格 !== -1) {
  485.                         var 包名 = 前段.substring(空格 + 1).trim();
  486.                         if (包名 && 包名.indexOf(".") !== -1) return 包名;
  487.                     }
  488.                 }
  489.             }
  490.         }
  491.     }

  492.     // 策略2:dumpsys window | head -30(限制输出防止卡死,mCurrentFocus在前10行内)
  493.     var window输出 = 执行命令("dumpsys window | head -30", true);
  494.     if (window输出 && window输出.length > 0 && window输出.indexOf("Permission Denial") === -1) {
  495.         var win行 = window输出.split("\n");
  496.         for (var wi = 0; wi < win行.length; wi++) {
  497.             var w行 = win行[wi].trim();
  498.             if (w行.indexOf("mCurrentFocus") !== -1 ||
  499.                 w行.indexOf("mFocusedApp") !== -1 ||
  500.                 w行.indexOf("mFocusedWindow") !== -1) {
  501.                 var w斜杠 = w行.indexOf("/");
  502.                 if (w斜杠 !== -1) {
  503.                     var w前段 = w行.substring(0, w斜杠);
  504.                     var w空格 = w前段.lastIndexOf(" ");
  505.                     if (w空格 !== -1) {
  506.                         var w包名 = w前段.substring(w空格 + 1).trim();
  507.                         if (w包名 && w包名.indexOf(".") !== -1) return w包名;
  508.                     }
  509.                 }
  510.             }
  511.         }
  512.     }

  513.     // 策略3:AIWROK 原生方法
  514.     try {
  515.         if (typeof auto !== "undefined") {
  516.             if (auto.getTopPackName) {
  517.                 var p = String(auto.getTopPackName());
  518.                 if (p && p.length > 0) return p;
  519.             }
  520.         }
  521.     } catch (e) {}

  522.     return "";
  523. };

  524. /**
  525. * 获取当前前台 Activity 全名
  526. * @returns {string} 如 "com.tencent.mm/.ui.LauncherUI"
  527. */
  528. AdbUtil.获取前台Activity = function() {
  529.     // 策略1:dumpsys activity activities | head -50(限制输出防止卡死)
  530.     var activity输出 = 执行命令("dumpsys activity activities | head -50", true);
  531.     if (activity输出 && activity输出.length > 0 && activity输出.indexOf("Permission Denial") === -1) {
  532.         var 行数组 = activity输出.split("\n");
  533.         for (var fi = 0; fi < 行数组.length; fi++) {
  534.             var 行 = 行数组[fi].trim();
  535.             if (行.indexOf("mResumedActivity") !== -1 ||
  536.                 行.indexOf("topResumedActivity") !== -1) {
  537.                 var m = 行.match(/(\w+\.\w+\.\w+\/\S+)/);
  538.                 if (m) return m[1].replace(/[}]/g, "").trim();
  539.             }
  540.         }
  541.     }
  542.     // 策略2:dumpsys window | head -30(限制输出防止卡死)
  543.     var window输出 = 执行命令("dumpsys window | head -30", true);
  544.     if (window输出 && window输出.length > 0 && window输出.indexOf("Permission Denial") === -1) {
  545.         var win行 = window输出.split("\n");
  546.         for (var wi = 0; wi < win行.length; wi++) {
  547.             var w行 = win行[wi].trim();
  548.             if (w行.indexOf("mCurrentFocus") !== -1 ||
  549.                 w行.indexOf("mFocusedApp") !== -1) {
  550.                 var m2 = w行.match(/(\w+\.\w+\.\w+\/\S+)/);
  551.                 if (m2) return m2[1].replace(/[}]/g, "").trim();
  552.             }
  553.         }
  554.     }
  555.     return "";
  556. };

  557. /**
  558. * 检查指定应用是否在前台
  559. * @param {string} 包名
  560. * @returns {boolean}
  561. */
  562. AdbUtil.是否在前台 = function(包名) {
  563.     var 前台 = AdbUtil.获取前台包名();
  564.     return 前台 === 包名;
  565. };

  566. /**
  567. * 检查指定进程是否在运行
  568. * @param {string} 包名或进程名
  569. * @returns {boolean}
  570. */
  571. AdbUtil.进程是否存在 = function(包名) {
  572.     // 策略1:ps -A | grep(需要 root 权限,Android 14 普通权限只能看到当前会话进程)
  573.     var 结果 = 执行命令("ps -A | grep " + 包名, true);
  574.     if (结果 && 结果.length > 0 && 结果.indexOf(包名) !== -1) return true;
  575.     // 策略2:pgrep -f(需要 root 权限)
  576.     var pgrep结果 = 执行命令("pgrep -f " + 包名, true);
  577.     if (pgrep结果 && pgrep结果.length > 0 && /^\d+/.test(pgrep结果.trim())) return true;
  578.     // 策略3:pidof(需要 root 权限)
  579.     var pidof结果 = 执行命令("pidof " + 包名, true);
  580.     if (pidof结果 && pidof结果.length > 0 && pidof结果.indexOf("pidof:") === -1 && /^\d+/.test(pidof结果.trim())) return true;
  581.     // 策略4:ps -A 全量输出在 JS 中搜索(root 权限)
  582.     var ps输出 = 执行命令("ps -A", true);
  583.     if (ps输出 && ps输出.length > 0) {
  584.         var ps行 = ps输出.split("\n");
  585.         for (var pi = 0; pi < ps行.length; pi++) {
  586.             if (ps行[pi].indexOf(包名) !== -1) return true;
  587.         }
  588.     }
  589.     return false;
  590. };

  591. /**
  592. * 获取 CPU 占用最高的进程
  593. * @returns {string} top 命令前 15 行
  594. */
  595. AdbUtil.获取CPU占用 = function() {
  596.     // 需要 root 权限,普通权限 top 只显示当前会话的 3 个进程
  597.     return 执行命令("top -n 1 -b | head -15", true);
  598. };

  599. /**
  600. * 杀死指定进程(需要 root 或同 UID)
  601. * @param {number|string} pid - 进程 ID 或包名
  602. * @param {boolean} 是否root
  603. * @returns {boolean}
  604. */
  605. AdbUtil.杀死进程 = function(pid, 是否root) {
  606.     // 如果传入的是包名,先获取 PID(需要 root 权限)
  607.     if (typeof pid === "string" && pid.indexOf(".") !== -1) {
  608.         var ps结果 = 执行命令("ps -A | grep " + pid, true);
  609.         if (ps结果 && ps结果.indexOf(pid) !== -1) {
  610.             var 字段 = ps结果.trim().split(/\s+/);
  611.             if (字段.length >= 2) pid = 字段[1]; // ps 输出第二列是 PID
  612.         }
  613.     }
  614.     return 执行命令静默("kill " + pid, 是否root || true);
  615. };


  616. // =============================================================================
  617. // 模块六:截图 + 网络诊断
  618. // =============================================================================

  619. /**
  620. * 截图保存到文件
  621. * @param {string} 保存路径 - 如 /sdcard/screenshot.png
  622. * @returns {boolean} 是否成功
  623. */
  624. AdbUtil.截图 = function(保存路径) {
  625.     保存路径 = 保存路径 || ("/sdcard/screenshot_" + Date.now() + ".png");

  626.     // 策略1:screencap 普通权限(增加延时确保文件写入完成)
  627.     执行命令静默("screencap -p " + 保存路径, false);
  628.     sleep.millisecond(1000);
  629.     var 大小1 = AdbUtil.文件大小(保存路径);
  630.     if (大小1 > 0) {
  631.         print.log("  截图已保存: " + 保存路径);
  632.         return true;
  633.     }

  634.     // 策略2:screencap root 权限
  635.     执行命令静默("screencap -p " + 保存路径, true);
  636.     sleep.millisecond(1000);
  637.     var 大小2 = AdbUtil.文件大小(保存路径);
  638.     if (大小2 > 0) {
  639.         print.log("  截图已保存(root): " + 保存路径);
  640.         return true;
  641.     }

  642.     // 策略3:先截图到临时文件再复制(部分设备 screencap 不支持直接写 sdcard)
  643.     var 临时路径 = "/data/local/tmp/screenshot_tmp.png";
  644.     执行命令静默("screencap -p " + 临时路径, false);
  645.     sleep.millisecond(1000);
  646.     var 大小3 = AdbUtil.文件大小(临时路径);
  647.     if (大小3 > 0) {
  648.         执行命令静默("cp " + 临时路径 + " " + 保存路径, false);
  649.         sleep.millisecond(300);
  650.         执行命令静默("rm -f " + 临时路径, false);
  651.         print.log("  截图已保存(临时文件中转): " + 保存路径);
  652.         return true;
  653.     }

  654.     // 策略4:使用 input keyevent 120 (KEYCODE_SYSRQ) 截屏系统快捷键
  655.     执行命令静默("input keyevent 120", false);
  656.     sleep.millisecond(1500);
  657.     // 系统截图默认保存到 /sdcard/Pictures/Screenshots/
  658.     var 系统截图目录 = 执行命令("ls -t /sdcard/Pictures/Screenshots/ | head -1", false);
  659.     if (系统截图目录 && 系统截图目录.length > 0) {
  660.         var 系统截图路径 = "/sdcard/Pictures/Screenshots/" + 系统截图目录.trim();
  661.         var 系统大小 = AdbUtil.文件大小(系统截图路径);
  662.         if (系统大小 > 0) {
  663.             执行命令静默("cp " + 系统截图路径 + " " + 保存路径, false);
  664.             sleep.millisecond(300);
  665.             print.log("  截图已保存(系统快捷键): " + 保存路径);
  666.             return true;
  667.         }
  668.     }

  669.     print.log("  截图失败(screencap 不可用或权限不足)");
  670.     return false;
  671. };

  672. /**
  673. * Ping 测试
  674. * @param {string} 地址 - 域名或 IP,默认 www.baidu.com
  675. * @param {number} 次数 - ping 次数,默认 3
  676. * @returns {string} ping 输出
  677. */
  678. AdbUtil.Ping = function(地址, 次数) {
  679.     地址 = 地址 || "www.baidu.com";
  680.     次数 = 次数 || 3;
  681.     return 执行命令("ping -c " + 次数 + " " + 地址, false);
  682. };

  683. /**
  684. * 获取 WiFi IP 地址
  685. * @returns {string} 如 "192.168.1.100"
  686. */
  687. AdbUtil.获取WiFiIP = function() {
  688.     var 输出 = 执行命令("ip addr show wlan0 2>/dev/null", false);
  689.     if (输出) {
  690.         var m = 输出.match(/inet\s+(\d+\.\d+\.\d+\.\d+)/);
  691.         if (m) return m[1];
  692.     }
  693.     return "";
  694. };

  695. /**
  696. * 获取网络接口信息
  697. * @returns {string} ip addr 输出
  698. */
  699. AdbUtil.获取网络接口 = function() {
  700.     return 执行命令("ip addr", false);
  701. };

  702. /**
  703. * 获取路由表
  704. * @returns {string}
  705. */
  706. AdbUtil.获取路由表 = function() {
  707.     return 执行命令("ip route", false);
  708. };


  709. // =============================================================================
  710. // 模块七:系统设置(部分需要 root)
  711. // =============================================================================

  712. /**
  713. * 获取系统设置值
  714. * @param {string} 命名空间 - "system" / "secure" / "global"
  715. * @param {string} 键名 - 设置键名
  716. * @param {boolean} 是否root
  717. * @returns {string} 设置值
  718. */
  719. AdbUtil.获取设置 = function(命名空间, 键名, 是否root) {
  720.     // 策略1:settings get
  721.     var 结果 = 执行命令("settings get " + 命名空间 + " " + 键名, 是否root || false);
  722.     if (结果 && 结果.indexOf("Exception") === -1 && 结果.indexOf("null") !== 0) {
  723.         return 结果.trim();
  724.     }
  725.     // 策略2:content query(非 root 回退)
  726.     var 查询 = 执行命令(
  727.         "content query --uri content://settings/" + 命名空间 +
  728.         " --where \"name='" + 键名 + "'\"", false
  729.     );
  730.     if (查询 && 查询.indexOf("value=") !== -1) {
  731.         var vIdx = 查询.indexOf("value=");
  732.         return 查询.substring(vIdx + 6).split(",")[0].trim();
  733.     }
  734.     return "";
  735. };

  736. /**
  737. * 修改系统设置值(通常需要 root)
  738. * @param {string} 命名空间
  739. * @param {string} 键名
  740. * @param {string} 值
  741. * @param {boolean} 是否root
  742. * @returns {boolean}
  743. */
  744. AdbUtil.设置值 = function(命名空间, 键名, 值, 是否root) {
  745.     return 执行命令静默(
  746.         "settings put " + 命名空间 + " " + 键名 + " " + 值,
  747.         是否root || false
  748.     );
  749. };

  750. /**
  751. * 获取屏幕超时时间(毫秒)
  752. * @returns {string}
  753. */
  754. AdbUtil.获取屏幕超时 = function() {
  755.     return AdbUtil.获取设置("system", "screen_off_timeout", true);
  756. };

  757. /**
  758. * 设置屏幕超时时间
  759. * @param {number} 毫秒 - 超时时间,如 60000=60秒
  760. * @returns {boolean}
  761. */
  762. AdbUtil.设置屏幕超时 = function(毫秒) {
  763.     return AdbUtil.设置值("system", "screen_off_timeout", 毫秒, true);
  764. };


  765. // =============================================================================
  766. // 综合实战演示 —— 全部功能运行一遍,每个功能前 3 秒倒计时
  767. // =============================================================================

  768. /**
  769. * 3秒倒计时函数 —— 每个功能前执行,增加节奏感
  770. * @param {string} 功能名称 - 即将执行的功能名称
  771. */
  772. function 倒计时(功能名称) {
  773.     print.log("\n────────────────────────────────────────");
  774.     print.log("  即将执行: " + 功能名称);
  775.     for (var i = 3; i > 0; i--) {
  776.         print.log("  ⏳ 倒计时 " + i + " 秒...");
  777.         sleep.second(1);
  778.     }
  779.     print.log("  ▶ 开始执行: " + 功能名称);
  780.     print.log("────────────────────────────────────────\n");
  781. }

  782. print.log("╔════════════════════════════════════════╗");
  783. print.log("║  adb.exec() 全功能实机运行演示        ║");
  784. print.log("║  共 12 个功能模块,每个间隔 3 秒倒计时  ║");
  785. print.log("╚════════════════════════════════════════╝\n");


  786. // ===== 演示1:设备信息一键采集 =====
  787. 倒计时("演示1 / 设备信息一键采集");
  788. print.log("【演示1】设备信息一键采集");
  789. var 设备 = AdbUtil.获取设备信息();
  790. print.log("  型号: " + 设备.型号);
  791. print.log("  品牌: " + 设备.品牌);
  792. print.log("  安卓版本: " + 设备.安卓版本 + " (SDK " + 设备.SDK版本 + ")");
  793. print.log("  制造商: " + 设备.制造商);
  794. print.log("  序列号: " + 设备.序列号);

  795. var 屏幕 = AdbUtil.获取屏幕信息();
  796. print.log("  分辨率: " + 屏幕.分辨率);
  797. print.log("  密度: " + 屏幕.密度);

  798. var 电池 = AdbUtil.获取电池信息();
  799. print.log("  电量: " + (电池.电量 || "?") + "% | 温度: " + (电池.温度 || "?") + " | 状态: " + (电池.状态 || "?"));
  800. print.log("  电池技术: " + (电池.技术 || "?"));
  801. // 诊断:如果电池信息为空,打印 dumpsys battery 前5行帮助排查
  802. if (!电池.电量) {
  803.     print.log("  [诊断] dumpsys battery 前5行:");
  804.     var 诊断电池 = 执行命令("dumpsys battery", true);
  805.     if (诊断电池 && 诊断电池.length > 0) {
  806.         var 诊断行 = 诊断电池.split("\n");
  807.         for (var di = 0; di < Math.min(5, 诊断行.length); di++) {
  808.             print.log("    " + 诊断行[di].trim());
  809.         }
  810.     } else {
  811.         print.log("    dumpsys battery 返回空!");
  812.     }
  813. }

  814. var 内存 = AdbUtil.获取内存信息();
  815. print.log("  内存信息:");
  816. var 内存行 = 内存.split("\n");
  817. for (var mi = 0; mi < 内存行.length; mi++) {
  818.     if (内存行[mi].trim()) print.log("    " + 内存行[mi].trim());
  819. }
  820. print.log("✅ 演示1 完成");


  821. // ===== 演示2:前台应用检测 =====
  822. 倒计时("演示2 / 前台应用检测");
  823. print.log("【演示2】前台应用检测");
  824. var 前台包名 = AdbUtil.获取前台包名();
  825. print.log("  当前前台包名: " + (前台包名 || "获取失败"));
  826. var 前台Activity = AdbUtil.获取前台Activity();
  827. print.log("  当前前台Activity: " + (前台Activity || "获取失败"));
  828. // 诊断:如果前台包名为空,打印 dumpsys activity activities 中包含 Resumed 的行
  829. if (!前台包名) {
  830.     print.log("  [诊断] dumpsys activity activities 中 Resumed 相关行:");
  831.     var 诊断activity = 执行命令("dumpsys activity activities | head -100", true);
  832.     if (诊断activity && 诊断activity.length > 0) {
  833.         var 诊断行 = 诊断activity.split("\n");
  834.         var 找到 = 0;
  835.         for (var di2 = 0; di2 < 诊断行.length && 找到 < 3; di2++) {
  836.             if (诊断行[di2].indexOf("Resumed") !== -1 ||
  837.                 诊断行[di2].indexOf("resumed") !== -1 ||
  838.                 诊断行[di2].indexOf("topResumed") !== -1) {
  839.                 print.log("    " + 诊断行[di2].trim());
  840.                 找到++;
  841.             }
  842.         }
  843.         if (找到 === 0) print.log("    未找到 Resumed 相关行,输出前3行:");
  844.         for (var di3 = 0; di3 < Math.min(3, 诊断行.length); di3++) {
  845.             print.log("    " + 诊断行[di3].trim());
  846.         }
  847.     } else {
  848.         print.log("    dumpsys activity activities 返回空!");
  849.     }
  850. }
  851. if (前台包名) {
  852.     print.log("  是否在前台判断: " + (AdbUtil.是否在前台(前台包名) ? "是 ✓" : "否"));
  853. }
  854. print.log("✅ 演示2 完成");


  855. // ===== 演示3:第三方应用列表 =====
  856. 倒计时("演示3 / 第三方应用列表");
  857. print.log("【演示3】第三方应用列表(前5个)");
  858. var 应用列表 = AdbUtil.列出第三方应用();
  859. print.log("  共 " + 应用列表.length + " 个第三方应用");
  860. for (var i = 0; i < Math.min(5, 应用列表.length); i++) {
  861.     var 信息 = AdbUtil.获取应用信息(应用列表[i]);
  862.     print.log("  " + (i + 1) + ". " + 应用列表[i] + " v" + (信息.版本名 || "?"));
  863.     // 诊断:第一个应用版本为空时,打印 grep version 的原始输出
  864.     if (!信息.版本名 && i === 0) {
  865.         print.log("    [诊断] dumpsys package " + 应用列表[i] + " | grep version 输出:");
  866.         var 诊断pkg = 执行命令("dumpsys package " + 应用列表[i] + " | grep version", true);
  867.         if (诊断pkg && 诊断pkg.length > 0) {
  868.             var pkg行 = 诊断pkg.split("\n");
  869.             for (var dpk = 0; dpk < Math.min(5, pkg行.length); dpk++) {
  870.                 print.log("      " + pkg行[dpk].trim());
  871.             }
  872.         } else {
  873.             print.log("      dumpsys package | grep version 返回空!");
  874.         }
  875.     }
  876. }
  877. print.log("✅ 演示3 完成");


  878. // ===== 演示4:进程检查 =====
  879. 倒计时("演示4 / 进程检查");
  880. print.log("【演示4】进程检查");
  881. print.log("  微信是否运行: " + (AdbUtil.进程是否存在("com.tencent.mm") ? "是 ✓" : "否"));
  882. print.log("  淘宝是否运行: " + (AdbUtil.进程是否存在("com.taobao.taobao") ? "是 ✓" : "否"));
  883. print.log("  QQ是否运行: " + (AdbUtil.进程是否存在("com.tencent.mobileqq") ? "是 ✓" : "否"));
  884. print.log("  设置是否运行: " + (AdbUtil.进程是否存在("com.android.settings") ? "是 ✓" : "否"));
  885. print.log("✅ 演示4 完成");


  886. // ===== 演示5:网络诊断 =====
  887. 倒计时("演示5 / 网络诊断");
  888. print.log("【演示5】网络诊断");
  889. var ip = AdbUtil.获取WiFiIP();
  890. print.log("  WiFi IP: " + (ip || "未连接 WiFi"));
  891. print.log("  Ping 百度: ");
  892. var ping结果 = AdbUtil.Ping("www.baidu.com", 3);
  893. var ping行 = ping结果.split("\n");
  894. var 统计行 = [];
  895. for (var j = ping行.length - 1; j >= 0 && 统计行.length < 3; j--) {
  896.     if (ping行[j].trim()) 统计行.unshift(ping行[j].trim());
  897. }
  898. for (var k = 0; k < 统计行.length; k++) {
  899.     print.log("    " + 统计行[k]);
  900. }
  901. print.log("✅ 演示5 完成");


  902. // ===== 演示6:文件操作 =====
  903. 倒计时("演示6 / 文件操作");
  904. print.log("【演示6】文件操作");
  905. AdbUtil.创建目录("/sdcard/aiwrok_test");
  906. print.log("  ✓ 创建目录 /sdcard/aiwrok_test");
  907. var 写入时间 = new Date().toLocaleString();
  908. AdbUtil.写文件("/sdcard/aiwrok_test/test.txt", "AIWROK adb.exec 测试 " + 写入时间);
  909. print.log("  ✓ 写入文件: AIWROK adb.exec 测试 " + 写入时间);
  910. var 内容 = AdbUtil.读文件("/sdcard/aiwrok_test/test.txt");
  911. print.log("  ✓ 读取文件: " + 内容);
  912. var 大小 = AdbUtil.文件大小("/sdcard/aiwrok_test/test.txt");
  913. print.log("  ✓ 文件大小: " + 大小 + " 字节");
  914. // 测试追加写入
  915. AdbUtil.追加写文件("/sdcard/aiwrok_test/test.txt", "——追加的第二行");
  916. var 内容2 = AdbUtil.读文件("/sdcard/aiwrok_test/test.txt");
  917. print.log("  ✓ 追加后内容: " + 内容2.replace(/\n/g, " | "));
  918. // 测试列目录
  919. var 目录内容 = AdbUtil.列目录("/sdcard/aiwrok_test/");
  920. print.log("  ✓ 目录内容:");
  921. var 目录行 = 目录内容.split("\n");
  922. for (var di = 0; di < Math.min(5, 目录行.length); di++) {
  923.     if (目录行[di].trim()) print.log("    " + 目录行[di].trim());
  924. }
  925. // 测试复制文件
  926. AdbUtil.复制文件("/sdcard/aiwrok_test/test.txt", "/sdcard/aiwrok_test/test_backup.txt");
  927. print.log("  ✓ 复制文件 → test_backup.txt");
  928. print.log("✅ 演示6 完成");


  929. // ===== 演示7:截图 =====
  930. 倒计时("演示7 / 截图");
  931. print.log("【演示7】截图");
  932. var 截图路径 = "/sdcard/aiwrok_test/screenshot_" + Date.now() + ".png";
  933. AdbUtil.截图(截图路径);
  934. var 截图大小 = AdbUtil.文件大小(截图路径);
  935. print.log("  截图文件大小: " + 截图大小 + " 字节");
  936. print.log("✅ 演示7 完成");


  937. // ===== 演示8:屏幕超时 =====
  938. 倒计时("演示8 / 屏幕超时设置");
  939. print.log("【演示8】屏幕超时");
  940. var 超时 = AdbUtil.获取屏幕超时();
  941. print.log("  当前屏幕超时: " + (超时 || "获取失败") + " 毫秒");
  942. if (超时) {
  943.     var 秒数 = Math.floor(parseInt(超时, 10) / 1000);
  944.     print.log("  即: " + 秒数 + " 秒");
  945. }
  946. print.log("✅ 演示8 完成");


  947. // ===== 演示9:输入模拟 =====
  948. 倒计时("演示9 / 输入模拟(Home/点击/滑动/按键)");
  949. print.log("【演示9】输入模拟(真实执行)");

  950. print.log("  [9.1] 按 Home 键回到桌面...");
  951. AdbUtil.Home键();
  952. sleep.second(1);
  953. print.log("    ✓ 已按 Home 键");
  954. var 回桌面后 = AdbUtil.获取前台包名();
  955. print.log("    当前前台: " + (回桌面后 || "未知"));

  956. var 屏幕信息9 = AdbUtil.获取屏幕信息();
  957. var 分辨率文本 = 屏幕信息9.分辨率 || "";
  958. var 尺寸匹配 = 分辨率文本.match(/(\d+)x(\d+)/);
  959. if (尺寸匹配) {
  960.     var 宽度 = parseInt(尺寸匹配[1], 10);
  961.     var 高度 = parseInt(尺寸匹配[2], 10);
  962.     var 中心X = Math.floor(宽度 / 2);
  963.     var 中心Y = Math.floor(高度 / 2);
  964.     print.log("  [9.2] 屏幕分辨率 " + 宽度 + "x" + 高度 + ",中心点 (" + 中心X + ", " + 中心Y + ")");

  965.     print.log("  [9.3] 点击屏幕中心...");
  966.     AdbUtil.点击(中心X, 中心Y);
  967.     sleep.millisecond(500);
  968.     print.log("    ✓ 已点击 (" + 中心X + ", " + 中心Y + ")");

  969.     print.log("  [9.4] 向上滑动...");
  970.     AdbUtil.滑动(中心X, Math.floor(高度 * 0.8), 中心X, Math.floor(高度 * 0.2), 300);
  971.     sleep.millisecond(500);
  972.     print.log("    ✓ 已向上滑动");

  973.     print.log("  [9.5] 下拉通知栏...");
  974.     AdbUtil.滑动(中心X, Math.floor(高度 * 0.1), 中心X, Math.floor(高度 * 0.5), 300);
  975.     sleep.second(1);
  976.     print.log("    ✓ 已下拉通知栏");
  977.     print.log("  [9.6] 收起通知栏...");
  978.     AdbUtil.滑动(中心X, Math.floor(高度 * 0.5), 中心X, Math.floor(高度 * 0.1), 300);
  979.     sleep.millisecond(500);
  980.     print.log("    ✓ 已收起通知栏");
  981. } else {
  982.     print.log("  [9.2] 无法解析屏幕分辨率: " + 分辨率文本);
  983. }

  984. print.log("  [9.7] 按返回键...");
  985. AdbUtil.返回键();
  986. sleep.millisecond(500);
  987. print.log("    ✓ 已按返回键");

  988. print.log("  [9.8] 按音量减键...");
  989. AdbUtil.按键(25);
  990. sleep.millisecond(500);
  991. print.log("    ✓ 已按音量减键");

  992. print.log("  [9.9] 按音量加键恢复...");
  993. AdbUtil.按键(24);
  994. sleep.millisecond(500);
  995. print.log("    ✓ 已按音量加键");

  996. print.log("✅ 演示9 完成");


  997. // ===== 演示10:应用管理 =====
  998. 倒计时("演示10 / 应用管理(安装检测/启动设置/截图)");
  999. print.log("【演示10】应用管理(真实执行)");

  1000. print.log("  [10.1] 检查常见应用安装状态");
  1001. var 常见应用 = [
  1002.     "com.tencent.mm",       // 微信
  1003.     "com.taobao.taobao",    // 淘宝
  1004.     "com.android.settings", // 系统设置
  1005.     "com.tencent.mobileqq", // QQ
  1006.     "com.eg.android.AlipayGphone" // 支付宝
  1007. ];
  1008. for (var ai = 0; ai < 常见应用.length; ai++) {
  1009.     var 已安装 = AdbUtil.应用是否安装(常见应用[ai]);
  1010.     print.log("    " + 常见应用[ai] + ": " + (已安装 ? "已安装 ✓" : "未安装"));
  1011. }

  1012. print.log("\n  [10.2] 获取已安装应用详细信息");
  1013. var 已安装应用 = null;
  1014. for (var bi = 0; bi < 常见应用.length; bi++) {
  1015.     if (AdbUtil.应用是否安装(常见应用[bi])) {
  1016.         已安装应用 = 常见应用[bi];
  1017.         break;
  1018.     }
  1019. }
  1020. if (已安装应用) {
  1021.     var 详情 = AdbUtil.获取应用信息(已安装应用);
  1022.     print.log("    包名: " + 已安装应用);
  1023.     print.log("    版本名: " + (详情.版本名 || "未知"));
  1024.     print.log("    版本号: " + (详情.版本号 || "未知"));
  1025.     print.log("    安装路径: " + (详情.安装路径 || "未知"));
  1026. } else {
  1027.     print.log("    常见应用均未安装");
  1028. }

  1029. print.log("\n  [10.3] 启动系统设置");
  1030. var 启动结果 = AdbUtil.启动Action("android.settings.SETTINGS");
  1031. sleep.second(2);
  1032. print.log("    启动命令已执行: " + (启动结果 ? "✓" : "✗"));

  1033. var 设置前台 = AdbUtil.获取前台包名();
  1034. print.log("    当前前台包名: " + (设置前台 || "未知"));
  1035. if (设置前台 && 设置前台.indexOf("settings") !== -1) {
  1036.     print.log("    ✓ 系统设置已打开");
  1037. } else {
  1038.     print.log("    (设置页面可能正在加载或设备不支持)");
  1039. }

  1040. var 设置Activity = AdbUtil.获取前台Activity();
  1041. print.log("    当前前台Activity: " + (设置Activity || "未知"));

  1042. print.log("\n  [10.4] 截图保存设置页面");
  1043. AdbUtil.截图("/sdcard/aiwrok_test/settings_page.png");

  1044. print.log("  [10.5] 检查设置进程状态");
  1045. var 设置运行 = AdbUtil.进程是否存在("com.android.settings");
  1046. if (!设置运行) 设置运行 = AdbUtil.进程是否存在("settings");
  1047. print.log("    设置进程运行中: " + (设置运行 ? "是 ✓" : "否"));

  1048. print.log("  [10.6] 按 Home 键回到桌面");
  1049. AdbUtil.Home键();
  1050. sleep.second(1);
  1051. var 回桌面后2 = AdbUtil.获取前台包名();
  1052. print.log("    当前前台: " + (回桌面后2 || "未知"));
  1053. print.log("✅ 演示10 完成");


  1054. // ===== 演示11:存储与CPU信息 =====
  1055. 倒计时("演示11 / 存储与CPU信息");
  1056. print.log("【演示11】存储与CPU信息");
  1057. var 存储 = AdbUtil.获取存储信息();
  1058. print.log("  存储信息:");
  1059. var 存储行 = 存储.split("\n");
  1060. for (var si = 0; si < 存储行.length; si++) {
  1061.     if (存储行[si].trim()) print.log("    " + 存储行[si].trim());
  1062. }

  1063. print.log("\n  CPU 占用 TOP 进程:");
  1064. var cpu = AdbUtil.获取CPU占用();
  1065. var cpu行 = cpu.split("\n");
  1066. for (var ci = 0; ci < Math.min(8, cpu行.length); ci++) {
  1067.     if (cpu行[ci].trim()) print.log("    " + cpu行[ci].trim());
  1068. }
  1069. print.log("✅ 演示11 完成");


  1070. // ===== 演示12:网络完整诊断 =====
  1071. 倒计时("演示12 / 网络完整诊断");
  1072. print.log("【演示12】网络完整诊断");
  1073. print.log("  WiFi IP: " + (AdbUtil.获取WiFiIP() || "未连接"));

  1074. var 路由 = AdbUtil.获取路由表();
  1075. print.log("  路由表:");
  1076. var 路由行 = 路由.split("\n");
  1077. for (var ri = 0; ri < Math.min(3, 路由行.length); ri++) {
  1078.     if (路由行[ri].trim()) print.log("    " + 路由行[ri].trim());
  1079. }

  1080. print.log("  网络接口信息:");
  1081. var 接口 = AdbUtil.获取网络接口();
  1082. var 接口行 = 接口.split("\n");
  1083. for (var ni = 0; ni < Math.min(5, 接口行.length); ni++) {
  1084.     if (接口行[ni].trim()) print.log("    " + 接口行[ni].trim());
  1085. }
  1086. print.log("✅ 演示12 完成");


  1087. // =============================================================================
  1088. // 速查表
  1089. // =============================================================================
  1090. print.log("\n========== AdbUtil 方法速查表 ==========\n");
  1091. print.log("设备信息:");
  1092. print.log("  AdbUtil.获取设备信息()      → {型号,品牌,安卓版本,SDK版本,制造商,序列号}");
  1093. print.log("  AdbUtil.获取屏幕信息()      → {分辨率,密度}");
  1094. print.log("  AdbUtil.获取电池信息()      → {电量,温度,状态,技术}");
  1095. print.log("  AdbUtil.获取内存信息()      → MemTotal/MemFree/MemAvailable");
  1096. print.log("  AdbUtil.获取存储信息()      → df 输出");
  1097. print.log("");
  1098. print.log("应用管理:");
  1099. print.log("  AdbUtil.列出第三方应用()    → [包名数组]");
  1100. print.log("  AdbUtil.应用是否安装(包名)  → true/false");
  1101. print.log("  AdbUtil.启动应用(包名)      → true/false");
  1102. print.log("  AdbUtil.停止应用(包名)      → true/false");
  1103. print.log("  AdbUtil.安装应用(路径,覆盖) → true/false");
  1104. print.log("  AdbUtil.卸载应用(包名)      → true/false");
  1105. print.log("  AdbUtil.获取应用信息(包名)  → {版本名,版本号,安装路径}");
  1106. print.log("");
  1107. print.log("文件操作:");
  1108. print.log("  AdbUtil.列目录(路径)        → ls 输出");
  1109. print.log("  AdbUtil.读文件(路径)        → 文件内容");
  1110. print.log("  AdbUtil.写文件(路径,内容)   → true/false");
  1111. print.log("  AdbUtil.追加写文件(路径,内容) → true/false");
  1112. print.log("  AdbUtil.复制文件(源,目标)   → true/false");
  1113. print.log("  AdbUtil.创建目录(路径)      → true/false");
  1114. print.log("  AdbUtil.删除(路径)          → true/false");
  1115. print.log("  AdbUtil.文件大小(路径)      → 字节数");
  1116. print.log("");
  1117. print.log("输入模拟:");
  1118. print.log("  AdbUtil.点击(x, y)          → true/false");
  1119. print.log("  AdbUtil.滑动(x1,y1,x2,y2,时长) → true/false");
  1120. print.log("  AdbUtil.长按(x, y, 时长)    → true/false");
  1121. print.log("  AdbUtil.输入文字(文字)      → true/false (仅ASCII)");
  1122. print.log("  AdbUtil.按键(键码)          → true/false");
  1123. print.log("  AdbUtil.返回键()            → 模拟返回");
  1124. print.log("  AdbUtil.Home键()            → 模拟Home");
  1125. print.log("");
  1126. print.log("前台检测+进程:");
  1127. print.log("  AdbUtil.获取前台包名()      → 'com.xxx.xxx'");
  1128. print.log("  AdbUtil.获取前台Activity()  → 'com.xxx/.Activity'");
  1129. print.log("  AdbUtil.是否在前台(包名)    → true/false");
  1130. print.log("  AdbUtil.进程是否存在(包名)  → true/false");
  1131. print.log("  AdbUtil.获取CPU占用()       → top 输出");
  1132. print.log("  AdbUtil.杀死进程(pid,root)  → true/false");
  1133. print.log("");
  1134. print.log("截图+网络:");
  1135. print.log("  AdbUtil.截图(路径)          → true/false");
  1136. print.log("  AdbUtil.Ping(地址,次数)    → ping 输出");
  1137. print.log("  AdbUtil.获取WiFiIP()        → '192.168.x.x'");
  1138. print.log("  AdbUtil.获取网络接口()      → ip addr 输出");
  1139. print.log("  AdbUtil.获取路由表()        → ip route 输出");
  1140. print.log("");
  1141. print.log("系统设置:");
  1142. print.log("  AdbUtil.获取设置(命名空间,键名,root) → 设置值");
  1143. print.log("  AdbUtil.设置值(命名空间,键名,值,root) → true/false");
  1144. print.log("  AdbUtil.获取屏幕超时()      → 毫秒数");
  1145. print.log("  AdbUtil.设置屏幕超时(毫秒)  → true/false");

  1146. print.log("\n✅ adb.exec() 实用工具库执行完毕");
  1147. print.log("提示:将本文件内容复制到你的项目中,即可通过 AdbUtil.方法名() 直接调用");
复制代码







欢迎光临 B2B网络软件 (http://bbs.niubt.cn/) Powered by Discuz! X3.2