Skip to main content
Glama
DeviceCreateUtil.kt7.98 kB
package maestro.cli.device import maestro.device.DeviceService import maestro.device.Device import maestro.device.Platform import maestro.cli.CliError import maestro.cli.util.* import maestro.device.util.AvdDevice object DeviceCreateUtil { fun getOrCreateDevice( platform: Platform, osVersion: Int? = null, language: String? = null, country: String? = null, forceCreate: Boolean = false, shardIndex: Int? = null, ): Device.AvailableForLaunch = when (platform) { Platform.ANDROID -> getOrCreateAndroidDevice(osVersion, language, country, forceCreate, shardIndex) Platform.IOS -> getOrCreateIosDevice(osVersion, language, country, forceCreate, shardIndex) else -> throw CliError("Unsupported platform $platform. Please specify one of: android, ios") } fun getOrCreateIosDevice( version: Int?, language: String?, country: String?, forceCreate: Boolean, shardIndex: Int? = null ): Device.AvailableForLaunch { @Suppress("NAME_SHADOWING") val version = version ?: DeviceConfigIos.defaultVersion if (version !in DeviceConfigIos.versions) { throw CliError("Provided iOS version is not supported. Please use one of ${DeviceConfigIos.versions}") } val runtime = DeviceConfigIos.runtimes[version] if (runtime == null) { throw CliError("Provided iOS runtime is not supported $runtime") } val deviceName = DeviceConfigIos.generateDeviceName(version) + shardIndex?.let { "_${it + 1}" }.orEmpty() val device = DeviceConfigIos.device // check connected device if (DeviceService.isDeviceConnected(deviceName, Platform.IOS) != null && shardIndex == null && !forceCreate) { throw CliError("A device with name $deviceName is already connected") } // check existing device val existingDeviceId = DeviceService.isDeviceAvailableToLaunch(deviceName, Platform.IOS)?.let { if (forceCreate) { DeviceService.deleteIosDevice(it.modelId) null } else it.modelId } if (existingDeviceId != null) PrintUtils.message("Using existing device $deviceName (${existingDeviceId}).") else PrintUtils.message("Attempting to create iOS simulator: $deviceName ") val deviceUUID = try { existingDeviceId ?: DeviceService.createIosDevice(deviceName, device, runtime).toString() } catch (e: IllegalStateException) { val error = e.message ?: "" if (error.contains("Invalid runtime")) { val msg = """ Required runtime to create the simulator is not installed: $runtime To install additional iOS runtimes checkout this guide: * https://developer.apple.com/documentation/xcode/installing-additional-simulator-runtimes """.trimIndent() throw CliError(msg) } else if (error.contains("xcrun: error: unable to find utility \"simctl\"")) { val msg = """ The xcode-select CLI tools are not installed, install with xcode-select --install If the xcode-select CLI tools are already installed, the path may be broken. Try running sudo xcode-select -r to repair the path and re-run this command """.trimIndent() throw CliError(msg) } else if (error.contains("Invalid device type")) { throw CliError("Device type $device is either not supported or not found.") } else { throw CliError(error) } } if (existingDeviceId == null) PrintUtils.message("Created simulator with name $deviceName and UUID $deviceUUID") return Device.AvailableForLaunch( modelId = deviceUUID, description = deviceName, platform = Platform.IOS, language = language, country = country, deviceType = Device.DeviceType.SIMULATOR ) } fun getOrCreateAndroidDevice( version: Int?, language: String?, country: String?, forceCreate: Boolean, shardIndex: Int? = null ): Device.AvailableForLaunch { @Suppress("NAME_SHADOWING") val version = version ?: DeviceConfigAndroid.defaultVersion if (version !in DeviceConfigAndroid.versions) { throw CliError("Provided Android version is not supported. Please use one of ${DeviceConfigAndroid.versions}") } val architecture = EnvUtils.getMacOSArchitecture() val pixels = DeviceService.getAvailablePixelDevices() val pixel = DeviceConfigAndroid.choosePixelDevice(pixels) ?: AvdDevice("-1", "Pixel 6", "pixel_6") val config = try { DeviceConfigAndroid.createConfig(version, pixel, architecture) } catch (e: IllegalStateException) { throw CliError(e.message ?: "Unable to create android device config") } val systemImage = config.systemImage val deviceName = config.deviceName + shardIndex?.let { "_${it + 1}" }.orEmpty() // check connected device if (DeviceService.isDeviceConnected(deviceName, Platform.ANDROID) != null && shardIndex == null && !forceCreate) throw CliError("A device with name $deviceName is already connected") // existing device val existingDevice = if (forceCreate) null else DeviceService.isDeviceAvailableToLaunch(deviceName, Platform.ANDROID)?.modelId // dependencies if (existingDevice == null && !DeviceService.isAndroidSystemImageInstalled(systemImage)) { PrintUtils.err("The required system image $systemImage is not installed.") PrintUtils.message("Would you like to install it? y/n") val r = readlnOrNull()?.lowercase() if (r == "y" || r == "yes") { PrintUtils.message("Attempting to install $systemImage via Android SDK Manager...\n") if (!DeviceService.installAndroidSystemImage(systemImage)) { val message = """ Unable to install required dependencies. You can install the system image manually by running this command: ${DeviceService.getAndroidSystemImageInstallCommand(systemImage)} """.trimIndent() throw CliError(message) } } else { val message = """ To install the system image manually, you can run this command: ${DeviceService.getAndroidSystemImageInstallCommand(systemImage)} """.trimIndent() throw CliError(message) } } if (existingDevice != null) PrintUtils.message("Using existing device $deviceName.") else PrintUtils.message("Attempting to create Android emulator: $deviceName ") val deviceLaunchId = try { existingDevice ?: DeviceService.createAndroidDevice( deviceName = config.deviceName, device = config.device, systemImage = config.systemImage, tag = config.tag, abi = config.abi, force = forceCreate, shardIndex = shardIndex, ) } catch (e: IllegalStateException) { throw CliError("${e.message}") } if (existingDevice == null) PrintUtils.message("Created Android emulator: $deviceName ($systemImage)") return Device.AvailableForLaunch( modelId = deviceLaunchId, description = deviceLaunchId, platform = Platform.ANDROID, language = language, country = country, deviceType = Device.DeviceType.EMULATOR, ) } }

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/mobile-dev-inc/Maestro'

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