Skip to main content
Glama
appLifecycleMonitor.ts4.87 kB
import { EventEmitter } from "events"; import { AdbUtils } from "./android-cmdline-tools/adb"; import { logger } from "./logger"; import { BootedDevice } from "../models"; export interface AppLifecycleEvent { type: "launch" | "terminate" | "background" | "foreground" | "crash"; device: BootedDevice; appId: string; timestamp: Date; previousApp?: string; metadata?: Record<string, any>; } export interface AppLifecycleEventListener { (event: AppLifecycleEvent): Promise<void>; } export class AppLifecycleMonitor extends EventEmitter { private trackedPackages: Set<string> = new Set(); private runningPackages: Set<string> = new Set(); private static instance: AppLifecycleMonitor; private constructor() { super(); } public static getInstance(): AppLifecycleMonitor { if (!AppLifecycleMonitor.instance) { AppLifecycleMonitor.instance = new AppLifecycleMonitor(); } return AppLifecycleMonitor.instance; } /** * Add a package to track for lifecycle events */ public async trackPackage(device: BootedDevice, packageName: string) { this.trackedPackages.add(packageName); await this.checkForChanges(device); logger.info(`Now tracking package: ${packageName}`); } /** * Remove a package from tracking */ public async untrackPackage(device: BootedDevice, packageName: string) { this.trackedPackages.delete(packageName); this.runningPackages.delete(packageName); await this.checkForChanges(device); logger.info(`Stopped tracking package: ${packageName}`); } /** * Get all tracked packages */ public getTrackedPackages(): string[] { return Array.from(this.trackedPackages); } /** * Check if a specific package is currently running */ public async isPackageRunning(device: BootedDevice, packageName: string): Promise<boolean> { try { const adbUtils = new AdbUtils(device); const result = await adbUtils.executeCommand(`shell pidof ${packageName}`); // pidof returns empty stdout if package is not running return result.stdout.trim().length > 0; } catch (error) { return false; } } /** * Get list of currently running tracked packages */ public getRunningPackages(): string[] { return Array.from(this.runningPackages); } /** * Add event listener for specific event types */ public addEventListener(type: string, listener: AppLifecycleEventListener): void { this.on(type, listener); } /** * Remove event listener */ public removeEventListener(type: string, listener: AppLifecycleEventListener): void { this.off(type, listener); } /** * Poll for app state changes */ public async checkForChanges(device: BootedDevice): Promise<void> { const previousRunning = new Set(this.runningPackages); await this.updateRunningPackages(device); // Check for newly launched packages for (const packageName of this.runningPackages) { if (!previousRunning.has(packageName)) { await this.handlePackageLaunched(device, packageName); } } // Check for terminated packages for (const packageName of previousRunning) { if (!this.runningPackages.has(packageName)) { await this.handlePackageTerminated(device, packageName); } } } /** * Update the set of currently running tracked packages */ private async updateRunningPackages(device: BootedDevice) { for (const packageName of this.trackedPackages) { if (await this.isPackageRunning(device, packageName)) { this.runningPackages.add(packageName); } else { this.runningPackages.delete(packageName); } } } /** * Handle package launch event */ private async handlePackageLaunched(device: BootedDevice, packageName: string): Promise<void> { const event: AppLifecycleEvent = { type: "launch", device: device, appId: packageName, timestamp: new Date(), metadata: { detectionMethod: "pidof" } }; logger.info(`Package launched: ${packageName}`); await this.emitEvent(event); } /** * Handle package termination event */ private async handlePackageTerminated(device: BootedDevice, packageName: string): Promise<void> { const event: AppLifecycleEvent = { type: "terminate", device: device, appId: packageName, timestamp: new Date(), metadata: { detectionMethod: "pidof" } }; logger.info(`Package terminated: ${packageName}`); await this.emitEvent(event); } /** * Emit an app lifecycle event */ private async emitEvent(event: AppLifecycleEvent): Promise<void> { try { this.emit(event.type, event); } catch (error) { logger.error(`Error emitting app lifecycle event: ${error}`); } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zillow/auto-mobile'

If you have feedback or need assistance with the MCP directory API, please join our Discord server