|
| 1 | +import adb, { DeviceClient, Utils, MotionEventMap, Scrcpy } from "../src/index.js"; |
| 2 | +import Parser from "../src/adb/parser.js"; |
| 3 | + |
| 4 | +const NB_CLICK = 1000000; |
| 5 | + |
| 6 | +const Y_OFFSET = 0.02;//25; |
| 7 | + |
| 8 | +async function* getCenterTaps(width: number, height: number, count: number, asPercent = false) { |
| 9 | + const centerX = asPercent ? width / 2 : Math.floor(width / 2); |
| 10 | + const centerY = asPercent ? height / 2 - height * Y_OFFSET : Math.floor(height / 2 - height * Y_OFFSET); // Move up by 20% |
| 11 | + const radius = Math.min(width, height) * 0.15; // 40% diameter = 20% radius |
| 12 | + |
| 13 | + console.log(`Screen: ${width}x${height}, Center: ${centerX},${centerY}, Circle radius: ${radius}`); |
| 14 | + const STEPS = 16; |
| 15 | + for (let i = 0; i < count; i++) { |
| 16 | + const angle = (2 * Math.PI * (i % STEPS)) / STEPS; |
| 17 | + const x = asPercent ? centerX + radius * Math.cos(angle) : Math.floor(centerX + radius * Math.cos(angle)); |
| 18 | + const y = asPercent ? centerY + radius * Math.sin(angle) : Math.floor(centerY + radius * Math.sin(angle)); |
| 19 | + yield { x, y }; |
| 20 | + // await Utils.delay(100); |
| 21 | + } |
| 22 | +} |
| 23 | + |
| 24 | + |
| 25 | +async function* getGridPercent(count: number) { |
| 26 | + const XGap = 0.02; |
| 27 | + const YGap = 0.35; |
| 28 | + const deltaX = 0.07; |
| 29 | + const deltaY = 0.07; |
| 30 | + let x = XGap; |
| 31 | + let y = YGap; |
| 32 | + for (let i = 0; i < count; i++) { |
| 33 | + x += deltaX; |
| 34 | + if (x > 1 - XGap) { |
| 35 | + x = XGap; |
| 36 | + y += deltaY; |
| 37 | + if (y > 1 - YGap) { |
| 38 | + y = YGap; |
| 39 | + } |
| 40 | + } |
| 41 | + yield { x, y }; |
| 42 | + // await Utils.delay(100); |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | + |
| 47 | + |
| 48 | +async function* getCenterTapsForDevice(deviceClient: DeviceClient, count: number, asPercent = false) { |
| 49 | + if (asPercent) { |
| 50 | + // Use normalized coordinates (0-1) without retrieving actual screen size |
| 51 | + for await (const pos of getCenterTaps(1, 1, count, true)) { |
| 52 | + yield pos; |
| 53 | + } |
| 54 | + } else { |
| 55 | + const screenInfo = await deviceClient.execOut("wm size", "utf8"); |
| 56 | + const match = screenInfo.match(/Physical size: (\d+)x(\d+)/); |
| 57 | + if (!match) { |
| 58 | + throw new Error("Could not parse screen dimensions"); |
| 59 | + } |
| 60 | + const width = parseInt(match[1]); |
| 61 | + const height = parseInt(match[2]); |
| 62 | + for await (const pos of getCenterTaps(width, height, count, false)) { |
| 63 | + yield pos; |
| 64 | + } |
| 65 | + } |
| 66 | +} |
| 67 | + |
| 68 | +// async function* getFixedCenterForDevice(deviceClient: DeviceClient, count: number) { |
| 69 | +// const screenInfo = await deviceClient.execOut("wm size", "utf8"); |
| 70 | +// const match = screenInfo.match(/Physical size: (\d+)x(\d+)/); |
| 71 | +// if (!match) { |
| 72 | +// throw new Error("Could not parse screen dimensions"); |
| 73 | +// } |
| 74 | +// const width = parseInt(match[1]); |
| 75 | +// const height = parseInt(match[2]); |
| 76 | +// const centerX = Math.floor(width / 2); |
| 77 | +// const centerY = Math.floor(height / 2 - height * Y_OFFSET); // Move up by 20% |
| 78 | +// // const centerY = Math.floor(height / 2); |
| 79 | +// |
| 80 | +// console.log(`Screen: ${width}x${height}, Fixed center: ${centerX},${centerY}`); |
| 81 | +// |
| 82 | +// for (let i = 0; i < count; i++) { |
| 83 | +// yield { x: centerX, y: centerY }; |
| 84 | +// } |
| 85 | +// } |
| 86 | + |
| 87 | +const testClickExec = async (deviceClient: DeviceClient) => { |
| 88 | + try { |
| 89 | + for await (const pos of getCenterTapsForDevice(deviceClient, NB_CLICK)) { |
| 90 | + await deviceClient.execOut(`input tap ${pos.x} ${pos.y}`, "utf8"); |
| 91 | + } |
| 92 | + } catch (e) { |
| 93 | + console.error("Impossible to start", e); |
| 94 | + } |
| 95 | +}; |
| 96 | + |
| 97 | +const testClickShell = async (deviceClient: DeviceClient, coordinateGenerator: AsyncGenerator<{ x: number, y: number }>) => { |
| 98 | + try { |
| 99 | + while (true) { |
| 100 | + const result1 = await coordinateGenerator.next(); |
| 101 | + if (result1.done) break; |
| 102 | + |
| 103 | + const result2 = await coordinateGenerator.next(); |
| 104 | + if (result2.done) break; |
| 105 | + |
| 106 | + const pos1 = result1.value; |
| 107 | + const pos2 = result2.value; |
| 108 | + |
| 109 | + // const duplex = await deviceClient.shell(`input multitap 2 ${pos1.x} ${pos1.y} ${pos2.x} ${pos2.y}`); |
| 110 | + const duplex = await deviceClient.shell(`input tap ${pos1.x} ${pos1.y}`); |
| 111 | + await new Parser(duplex).readAll(); |
| 112 | + } |
| 113 | + console.log("All shell taps completed"); |
| 114 | + } catch (e) { |
| 115 | + console.error("Shell command failed", e); |
| 116 | + } |
| 117 | +}; |
| 118 | + |
| 119 | +const testClickScrcpy = async (deviceClient: DeviceClient, coordinateGenerator: AsyncGenerator<{ x: number, y: number }>) => { |
| 120 | + let scrcpy: Scrcpy | undefined; |
| 121 | + try { |
| 122 | + while (!scrcpy) { |
| 123 | + try { |
| 124 | + scrcpy = deviceClient.scrcpy({}); |
| 125 | + await Utils.delay(1); |
| 126 | + await scrcpy.start(); |
| 127 | + await scrcpy.width; |
| 128 | + } catch (e) { |
| 129 | + console.error('Scrcpy start failed, retrying...', e); |
| 130 | + await Utils.delay(1000); |
| 131 | + scrcpy = undefined; |
| 132 | + } |
| 133 | + } |
| 134 | + // const scrcpy |
| 135 | + // = deviceClient.scrcpy({}); |
| 136 | + // await Utils.delay(1); |
| 137 | + // await scrcpy.start(); |
| 138 | + await Utils.delay(1); |
| 139 | + const width = await scrcpy.width; |
| 140 | + const height = await scrcpy.height; |
| 141 | + const screenSize = { x: width, y: height }; |
| 142 | + console.log('Scrcpy started'); |
| 143 | + |
| 144 | + // Get screen dimensions |
| 145 | + // const screenInfo = await deviceClient.execOut("wm size", "utf8"); |
| 146 | + // const match = screenInfo.match(/Physical size: (\d+)x(\d+)/); |
| 147 | + // if (!match) { |
| 148 | + // throw new Error("Could not parse screen dimensions"); |
| 149 | + // } |
| 150 | + // const screenSize = { x: parseInt(match[1]), y: parseInt(match[1]) }; |
| 151 | + |
| 152 | + for await (const tap of coordinateGenerator) { |
| 153 | + // Convert percentage coordinates (0-1) to absolute pixels if needed |
| 154 | + const position = { |
| 155 | + x: tap.x <= 1 ? Math.floor(tap.x * width) : tap.x, |
| 156 | + y: tap.y <= 1 ? Math.floor(tap.y * height) : tap.y |
| 157 | + }; |
| 158 | + // Send ACTION_DOWN followed by ACTION_UP for each tap |
| 159 | + // MotionEvent.ACTION_DOWN |
| 160 | + await scrcpy.injectTouchEvent(MotionEventMap.ACTION_DOWN, 0n, position, screenSize, undefined, 0, MotionEventMap.BUTTON_PRIMARY); |
| 161 | + await Utils.delay(8); |
| 162 | + //await scrcpy.injectTouchEvent(MotionEventMap.ACTION_UP, 0n, position, screenSize, undefined, 0, 0); |
| 163 | + //await Utils.delay(1); |
| 164 | + } |
| 165 | + console.log('All scrcpy taps completed'); |
| 166 | + } catch (e) { |
| 167 | + console.error('Scrcpy command failed', e); |
| 168 | + } finally { |
| 169 | + } |
| 170 | + if (scrcpy) |
| 171 | + scrcpy.stop(); |
| 172 | +}; |
| 173 | + |
| 174 | + |
| 175 | +const main = async () => { |
| 176 | + // process.env.DEBUG = '*'; |
| 177 | + const adbClient = adb.createClient(); |
| 178 | + const devices = await adbClient.listDevices(); |
| 179 | + if (!devices.length) { |
| 180 | + console.error("Need at least one connected android device"); |
| 181 | + return; |
| 182 | + } |
| 183 | + const deviceClient = devices[0].getClient(); |
| 184 | + |
| 185 | + // { |
| 186 | + // let t1 = Date.now(); |
| 187 | + // await testClickExec(deviceClient); |
| 188 | + // t1 = Date.now() - t1; |
| 189 | + // console.log(`execOut = ${t1 / NB_CLICK}`); // 50.146 ms |
| 190 | + // } |
| 191 | + // |
| 192 | + if (false) { |
| 193 | + const promises: Promise<unknown>[] = []; |
| 194 | + let t2 = Date.now(); |
| 195 | + promises.push(testClickShell(deviceClient, getCenterTapsForDevice(deviceClient, NB_CLICK))); |
| 196 | + //promises.push(testClickShell(deviceClient, getFixedCenterForDevice(deviceClient, NB_CLICK)); |
| 197 | + await Promise.all(promises); |
| 198 | + t2 = Date.now() - t2; |
| 199 | + console.log(`shell = ${t2 / NB_CLICK}`); // 44.815 ms |
| 200 | + } |
| 201 | + |
| 202 | + if (true) { |
| 203 | + let t3 = Date.now(); |
| 204 | + // await testClickScrcpy(deviceClient, getCenterTapsForDevice(deviceClient, NB_CLICK, true)); |
| 205 | + await testClickScrcpy(deviceClient, getGridPercent(NB_CLICK)); |
| 206 | + t3 = Date.now() - t3; |
| 207 | + console.log(`scrcpy = ${t3 / NB_CLICK}`); |
| 208 | + } |
| 209 | + |
| 210 | + console.log(`Done`); |
| 211 | +}; |
| 212 | + |
| 213 | +process.on("unhandledRejection", (reason, promise) => { |
| 214 | + debugger; |
| 215 | + console.error("Unhandled Rejection at:", promise, "reason:", reason); |
| 216 | +}); |
| 217 | + |
| 218 | +process.on( |
| 219 | + "exit", |
| 220 | + (code) => console.log("Processus is closing, exit code:", code), |
| 221 | +); |
| 222 | +process.on("SIGINT", () => console.log("SIGINT reçu")); |
| 223 | +process.on("SIGTERM", () => console.log("SIGTERM reçu")); |
| 224 | + |
| 225 | +main().catch((e) => console.error("ERROR", e)).finally(() => { |
| 226 | + console.log("Processus terminé"); |
| 227 | +}); |
0 commit comments