Skip to main content
Glama
AndroidAppFiles.kt2.94 kB
package maestro.android import dadb.Dadb import java.io.File import java.io.IOException import java.net.URI import java.nio.file.FileSystems import java.nio.file.Files import java.nio.file.Path import kotlin.io.path.createDirectories object AndroidAppFiles { fun pull(dadb: Dadb, packageName: String, zipOutFile: File) { zipOutFile.delete() val zipOutUri = URI("jar:${zipOutFile.toURI().scheme}", zipOutFile.absolutePath, null) FileSystems.newFileSystem(zipOutUri, mapOf("create" to "true")).use { fs -> // Create zip directories first listRemoteFiles(dadb, packageName, "-type d").forEach { remoteDir -> val dstLocation = remoteDir .removePrefix("/") .removeSuffix("/.") .removeSuffix("/") val dstPath = fs.getPath(dstLocation) dstPath.createDirectories() } // Create zip files listRemoteFiles(dadb, packageName, "-type f").forEach { remoteFile -> val dstLocation = remoteFile .removePrefix("/") .removeSuffix("/.") .removeSuffix("/") val dstPath = fs.getPath(dstLocation) pullAppFile(dadb, packageName, dstPath, remoteFile) } } } fun getApkFile(dadb: Dadb, appId: String): File { val apkPath = dadb.shell("pm list packages -f --user 0 | grep $appId | head -1") .output.substringAfterLast("package:").substringBefore("=$appId") apkPath.substringBefore("=$appId") val dst = File.createTempFile("tmp", ".apk") dadb.pull(dst, apkPath) return dst } fun push(dadb: Dadb, packageName: String, appFilesZip: File) { val remoteZip = "/data/local/tmp/app.zip" dadb.push(appFilesZip, remoteZip) try { shell(dadb, "run-as $packageName unzip -o -d / $remoteZip") } finally { shell(dadb, "rm $remoteZip") } } private fun pullAppFile(dadb: Dadb, packageName: String, localPath: Path, remotePath: String) { dadb.open("exec:run-as $packageName cat $remotePath").use { stream -> Files.copy(stream.source.inputStream(), localPath) } } private fun listRemoteFiles(dadb: Dadb, packageName: String, options: String): List<String> { val result = shell(dadb, "run-as $packageName find $options") val appDataDir = "/data/data/$packageName" return result.lines() .filter { it.isNotBlank() } .map { "$appDataDir/${it.removePrefix("./")}" } } private fun shell(dadb: Dadb, command: String): String { val response = dadb.shell(command) if (response.exitCode != 0) throw IOException("Shell command failed ($command):\n${response.allOutput}") return response.allOutput } }

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