adb-interact.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. #!/usr/bin/env node
  2. const { execSync } = require('child_process')
  3. const path = require('path')
  4. const config = require(path.join(__dirname, '..', '..', 'configs', 'config.js'))
  5. const projectRoot = path.resolve(__dirname, '..', '..')
  6. /** 从 config 解析并返回 adb 可执行文件路径 */
  7. function getAdbPath() {
  8. return config.adbPath?.path
  9. ? path.resolve(projectRoot, config.adbPath.path)
  10. : path.join(projectRoot, 'lib', 'scrcpy-adb', 'adb.exe')
  11. }
  12. /** 根据 deviceId 返回 adb -s 参数前缀,无设备时返回空串 */
  13. function getDeviceFlag(deviceId) {
  14. return deviceId && deviceId.includes(':') ? `-s ${deviceId} ` : ''
  15. }
  16. /** 执行 adb shell 命令 */
  17. function runShell(adbPath, deviceFlag, shellArgs, timeout = 1000) {
  18. const quoted = adbPath.includes(' ') ? `"${adbPath}"` : adbPath
  19. execSync(`${quoted} ${deviceFlag}shell ${shellArgs}`, { encoding: 'utf-8', timeout })
  20. }
  21. /** 执行 adb shell 并返回标准输出 */
  22. function runShellWithOutput(adbPath, deviceFlag, shellArgs, timeout = 1000) {
  23. const quoted = adbPath.includes(' ') ? `"${adbPath}"` : adbPath
  24. return execSync(`${quoted} ${deviceFlag}shell ${shellArgs}`, { encoding: 'utf-8', timeout })
  25. }
  26. /** 处理 tap:点击坐标 */
  27. function handleTap(adbPath, argv) {
  28. const coordX = argv[3]
  29. const coordY = argv[4]
  30. const deviceId = argv[5] || ''
  31. if (!coordX || !coordY) process.exit(1)
  32. const flag = getDeviceFlag(deviceId)
  33. runShell(adbPath, flag, `input tap ${coordX} ${coordY}`, 1000)
  34. }
  35. /** 根据方向与距离计算滑动的起止坐标(基于屏幕中心),返回 [startX, startY, endX, endY] */
  36. function getSwipeCoordsByDirection(direction, distance, centerX, centerY) {
  37. const half = Math.floor(distance / 2)
  38. switch (direction) {
  39. case 'up-down':
  40. return [centerX, centerY - half, centerX, centerY + half]
  41. case 'down-up':
  42. return [centerX, centerY + half, centerX, centerY - half]
  43. case 'left-right':
  44. return [centerX - half, centerY, centerX + half, centerY]
  45. case 'right-left':
  46. return [centerX + half, centerY, centerX - half, centerY]
  47. default:
  48. process.exit(1)
  49. }
  50. }
  51. /** 处理 swipe:按方向与距离从屏幕中心滑动 */
  52. function handleSwipe(adbPath, argv) {
  53. const direction = argv[3]
  54. const distance = parseInt(argv[4], 10)
  55. const duration = parseInt(argv[5], 10)
  56. const deviceId = argv[6] || ''
  57. if (!direction || !distance || !duration) process.exit(1)
  58. const flag = getDeviceFlag(deviceId)
  59. const sizeOut = runShellWithOutput(adbPath, flag, 'wm size', 1000)
  60. const sizeMatch = sizeOut.match(/(\d+)x(\d+)/)
  61. const centerX = Math.floor(parseInt(sizeMatch[1], 10) / 2)
  62. const centerY = Math.floor(parseInt(sizeMatch[2], 10) / 2)
  63. const [startX, startY, endX, endY] = getSwipeCoordsByDirection(direction, distance, centerX, centerY)
  64. runShell(adbPath, flag, `input swipe ${startX} ${startY} ${endX} ${endY} ${duration}`, 2000)
  65. }
  66. /** 处理 swipe-coords:按坐标滑动 */
  67. function handleSwipeCoords(adbPath, argv) {
  68. const x1 = argv[3]
  69. const y1 = argv[4]
  70. const x2 = argv[5]
  71. const y2 = argv[6]
  72. const duration = argv[7] || 300
  73. const deviceId = argv[8] || ''
  74. if (!x1 || !y1 || !x2 || !y2) process.exit(1)
  75. const flag = getDeviceFlag(deviceId)
  76. runShell(adbPath, flag, `input swipe ${x1} ${y1} ${x2} ${y2} ${duration}`, 2000)
  77. }
  78. /** 处理 keyevent:按键 */
  79. function handleKeyevent(adbPath, argv) {
  80. const keyCode = argv[3]
  81. const deviceId = argv[4] || ''
  82. if (!keyCode) process.exit(1)
  83. const flag = getDeviceFlag(deviceId)
  84. runShell(adbPath, flag, `input keyevent ${keyCode}`, 1000)
  85. }
  86. /** 处理 text:输入文本 */
  87. function handleText(adbPath, argv) {
  88. const text = argv[3]
  89. const deviceId = argv[4] || ''
  90. if (text === undefined) process.exit(1)
  91. const flag = getDeviceFlag(deviceId)
  92. const escaped = String(text).replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`')
  93. runShell(adbPath, flag, `input text "${escaped}"`, 5000)
  94. }
  95. const action = process.argv[2]
  96. if (!action) process.exit(1)
  97. const adbPath = getAdbPath()
  98. switch (action) {
  99. case 'tap':
  100. handleTap(adbPath, process.argv)
  101. break
  102. case 'swipe':
  103. handleSwipe(adbPath, process.argv)
  104. break
  105. case 'swipe-coords':
  106. handleSwipeCoords(adbPath, process.argv)
  107. break
  108. case 'keyevent':
  109. handleKeyevent(adbPath, process.argv)
  110. break
  111. case 'text':
  112. handleText(adbPath, process.argv)
  113. break
  114. default:
  115. process.exit(1)
  116. }
  117. process.exit(0)