18d307f52SEvan Baconimport * as osascript from '@expo/osascript';
28d307f52SEvan Baconimport { execFileSync } from 'child_process';
38d307f52SEvan Bacon
48d307f52SEvan Baconimport { Device } from './adb';
58d307f52SEvan Bacon
6*474a7a4bSEvan Baconconst debug = require('debug')('expo:start:platforms:android:activateWindow') as typeof console.log;
7*474a7a4bSEvan Bacon
88d307f52SEvan Baconfunction getUnixPID(port: number | string): string {
98d307f52SEvan Bacon  // Runs like `lsof -i:8081 -P -t -sTCP:LISTEN`
108d307f52SEvan Bacon  const args = [`-i:${port}`, '-P', '-t', '-sTCP:LISTEN'];
11*474a7a4bSEvan Bacon  debug('lsof ' + args.join(' '));
128d307f52SEvan Bacon  return execFileSync('lsof', args, {
138d307f52SEvan Bacon    encoding: 'utf8',
148d307f52SEvan Bacon    stdio: ['pipe', 'pipe', 'ignore'],
158d307f52SEvan Bacon  })
168d307f52SEvan Bacon    .split('\n')[0]
178d307f52SEvan Bacon    ?.trim?.();
188d307f52SEvan Bacon}
198d307f52SEvan Bacon
208d307f52SEvan Bacon/** Activate the Emulator window on macOS. */
218d307f52SEvan Baconexport async function activateWindowAsync(device: Pick<Device, 'type' | 'pid'>): Promise<boolean> {
22*474a7a4bSEvan Bacon  debug(`Activating window for device (pid: ${device.pid}, type: ${device.type})`);
238d307f52SEvan Bacon  if (
248d307f52SEvan Bacon    // only mac is supported for now.
258d307f52SEvan Bacon    process.platform !== 'darwin' ||
268d307f52SEvan Bacon    // can only focus emulators
278d307f52SEvan Bacon    device.type !== 'emulator'
288d307f52SEvan Bacon  ) {
298d307f52SEvan Bacon    return false;
308d307f52SEvan Bacon  }
318d307f52SEvan Bacon
328d307f52SEvan Bacon  // Google Emulator ID: `emulator-5554` -> `5554`
338d307f52SEvan Bacon  const androidPid = device.pid!.match(/-(\d+)/)?.[1];
348d307f52SEvan Bacon  if (!androidPid) {
3529975bfdSEvan Bacon    return false;
368d307f52SEvan Bacon  }
378d307f52SEvan Bacon  // Unix PID
388d307f52SEvan Bacon  const pid = getUnixPID(androidPid);
398d307f52SEvan Bacon
408d307f52SEvan Bacon  if (!pid) {
418d307f52SEvan Bacon    return false;
428d307f52SEvan Bacon  }
43*474a7a4bSEvan Bacon  debug(`Activate window for pid:`, pid);
448d307f52SEvan Bacon  try {
458d307f52SEvan Bacon    await osascript.execAsync(`
468d307f52SEvan Bacon    tell application "System Events"
478d307f52SEvan Bacon      set frontmost of the first process whose unix id is ${pid} to true
488d307f52SEvan Bacon    end tell`);
498d307f52SEvan Bacon    return true;
508d307f52SEvan Bacon  } catch {
518d307f52SEvan Bacon    // noop -- this feature is very specific and subject to failure.
528d307f52SEvan Bacon    return false;
538d307f52SEvan Bacon  }
548d307f52SEvan Bacon}
55