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