test-adbkeyboard-send.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. #!/usr/bin/env node
  2. /**
  3. * 测试 ADB Keyboard 发送文字:尝试多种 broadcast 方式直到成功。
  4. * 用法: node test-adbkeyboard-send.js <device_id> [文本]
  5. * 例: node test-adbkeyboard-send.js 192.168.42.129:5555 创建小红书笔记
  6. */
  7. const path = require('path')
  8. const fs = require('fs')
  9. const { spawnSync } = require('child_process')
  10. const projectRoot = path.resolve(__dirname, '..', '..')
  11. const config = require(path.join(projectRoot, 'configs', 'config.js'))
  12. const adbPath = config.adbPath?.path ? path.resolve(projectRoot, config.adbPath.path) : path.join(projectRoot, 'lib', 'scrcpy-adb', 'adb.exe')
  13. const PKG = 'com.android.adbkeyboard'
  14. const TEST_TEXT = process.argv[3] || '测试'
  15. function deviceArgs(device) {
  16. return device && String(device).includes(':') ? ['-s', device] : []
  17. }
  18. function shell(device, cmd, timeout = 5000) {
  19. const args = [...deviceArgs(device), 'shell', cmd]
  20. const r = spawnSync(adbPath, args, { encoding: 'utf-8', timeout })
  21. return { ok: r.status === 0, out: (r.stdout || '').trim(), err: (r.stderr || '').trim() }
  22. }
  23. function broadcast(device, action, extraKey, value, component) {
  24. const args = [...deviceArgs(device), 'shell', 'am', 'broadcast', '-a', action, '--es', extraKey, value]
  25. if (component) args.splice(args.length - 2, 0, '-n', component)
  26. const r = spawnSync(adbPath, args, { encoding: 'utf-8', timeout: 10000 })
  27. return r.status === 0
  28. }
  29. function getReceivers(device) {
  30. const { out } = shell(device, `dumpsys package ${PKG}`, 8000)
  31. const receivers = []
  32. const re = /Receiver #\d+.*?ComponentInfo\{([^}]+)\}/gs
  33. let m
  34. while ((m = re.exec(out)) !== null) {
  35. const comp = m[1].trim()
  36. if (comp.startsWith(PKG)) receivers.push(comp)
  37. }
  38. const alt = out.match(new RegExp(PKG + '/[^\\s\\n\\r]+', 'g'))
  39. if (alt) alt.forEach(c => { if (!receivers.includes(c)) receivers.push(c) })
  40. return [...new Set(receivers)]
  41. }
  42. function setIme(device, imeId) {
  43. return shell(device, `ime set ${imeId}`, 5000).ok
  44. }
  45. function getCurrentIme(device) {
  46. return shell(device, 'settings get secure default_input_method', 3000).out
  47. }
  48. function findAdbKeyboardIme(device) {
  49. const { out } = shell(device, 'ime list -a', 5000)
  50. const lines = (out || '').split(/\r?\n/)
  51. for (const line of lines) {
  52. const t = line.trim().replace(/:$/, '')
  53. if (t.includes('/') && t.toLowerCase().includes('adbkeyboard')) return t
  54. }
  55. return `${PKG}/.AdbIME`
  56. }
  57. function main() {
  58. const device = process.argv[2]
  59. if (!device) {
  60. console.error('用法: node test-adbkeyboard-send.js <device_id> [文本]')
  61. process.exit(2)
  62. }
  63. const prevIme = getCurrentIme(device)
  64. const imeId = findAdbKeyboardIme(device)
  65. console.log('当前 IME:', prevIme)
  66. console.log('切换到:', imeId)
  67. if (!setIme(device, imeId)) {
  68. console.error('切换 IME 失败')
  69. process.exit(1)
  70. }
  71. const base64 = Buffer.from(TEST_TEXT, 'utf8').toString('base64')
  72. const methods = [
  73. { name: 'ADB_INPUT_B64 隐式', fn: () => broadcast(device, 'ADB_INPUT_B64', 'msg', base64, null) },
  74. { name: `ADB_INPUT_B64 -n ${PKG}/.AdbReceiver`, fn: () => broadcast(device, 'ADB_INPUT_B64', 'msg', base64, `${PKG}/.AdbReceiver`) },
  75. { name: `ADB_INPUT_B64 -n ${PKG}/.Receiver`, fn: () => broadcast(device, 'ADB_INPUT_B64', 'msg', base64, `${PKG}/.Receiver`) },
  76. { name: `ADB_INPUT_B64 -n ${imeId}`, fn: () => broadcast(device, 'ADB_INPUT_B64', 'msg', base64, imeId) },
  77. { name: 'ADB_INPUT_TEXT 隐式', fn: () => broadcast(device, 'ADB_INPUT_TEXT', 'msg', TEST_TEXT, null) },
  78. { name: `ADB_INPUT_TEXT -n ${PKG}/.AdbReceiver`, fn: () => broadcast(device, 'ADB_INPUT_TEXT', 'msg', TEST_TEXT, `${PKG}/.AdbReceiver`) },
  79. ]
  80. const receivers = getReceivers(device)
  81. console.log('包内 Receiver 组件:', receivers.length ? receivers : '(未解析到)')
  82. receivers.forEach(comp => {
  83. methods.push({ name: `ADB_INPUT_B64 -n ${comp}`, fn: () => broadcast(device, 'ADB_INPUT_B64', 'msg', base64, comp) })
  84. })
  85. for (const m of methods) {
  86. const ok = m.fn()
  87. console.log(ok ? '[OK]' : '[FAIL]', m.name)
  88. if (ok) {
  89. if (prevIme) setIme(device, prevIme)
  90. console.log('发送成功,已切回原 IME。请查看设备输入框是否出现文字:', TEST_TEXT)
  91. process.exit(0)
  92. }
  93. }
  94. if (prevIme) setIme(device, prevIme)
  95. console.error('所有方式均失败')
  96. process.exit(1)
  97. }
  98. main()