1import * as osascript from '@expo/osascript';
2import { execFileSync } from 'child_process';
3
4import * as Log from '../../../log';
5import { Device } from './adb';
6
7function getUnixPID(port: number | string): string {
8  // Runs like `lsof -i:8081 -P -t -sTCP:LISTEN`
9  const args = [`-i:${port}`, '-P', '-t', '-sTCP:LISTEN'];
10  Log.debug('lsof ' + args.join(' '));
11  return execFileSync('lsof', args, {
12    encoding: 'utf8',
13    stdio: ['pipe', 'pipe', 'ignore'],
14  })
15    .split('\n')[0]
16    ?.trim?.();
17}
18
19/** Activate the Emulator window on macOS. */
20export async function activateWindowAsync(device: Pick<Device, 'type' | 'pid'>): Promise<boolean> {
21  Log.debug(`Activating window for device (pid: ${device.pid}, type: ${device.type})`);
22  if (
23    // only mac is supported for now.
24    process.platform !== 'darwin' ||
25    // can only focus emulators
26    device.type !== 'emulator'
27  ) {
28    return false;
29  }
30
31  // Google Emulator ID: `emulator-5554` -> `5554`
32  const androidPid = device.pid!.match(/-(\d+)/)?.[1];
33  if (!androidPid) {
34    return false;
35  }
36  // Unix PID
37  const pid = getUnixPID(androidPid);
38
39  if (!pid) {
40    return false;
41  }
42  Log.debug(`Activate window for pid:`, pid);
43  try {
44    await osascript.execAsync(`
45    tell application "System Events"
46      set frontmost of the first process whose unix id is ${pid} to true
47    end tell`);
48    return true;
49  } catch {
50    // noop -- this feature is very specific and subject to failure.
51    return false;
52  }
53}
54