diff --git a/maestro-client/src/main/java/maestro/android/AndroidAppFiles.kt b/maestro-client/src/main/java/maestro/android/AndroidAppFiles.kt index bc91428e15..13b61057db 100644 --- a/maestro-client/src/main/java/maestro/android/AndroidAppFiles.kt +++ b/maestro-client/src/main/java/maestro/android/AndroidAppFiles.kt @@ -38,11 +38,15 @@ object AndroidAppFiles { } 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 apkPath = dadb.shell("pm path $appId").output.removePrefix("package:").trim() val dst = File.createTempFile("tmp", ".apk") - dadb.pull(dst, apkPath) + try { + dadb.pull(dst, apkPath) + } catch (e: IOException) { + val newApkPath = "/sdcard/$appId.apk" + dadb.shell("cp $apkPath $newApkPath") + dadb.pull(dst, newApkPath) + } return dst } diff --git a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt index 871b063b6d..d8f0c73239 100644 --- a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt +++ b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt @@ -46,6 +46,7 @@ import org.w3c.dom.Element import org.w3c.dom.Node import java.io.File import java.io.IOException +import java.net.URI import java.util.UUID import java.util.concurrent.CompletableFuture import java.util.concurrent.Executors @@ -503,55 +504,85 @@ class AndroidDriver( } override fun openLink(link: String, appId: String?, autoVerify: Boolean, browser: Boolean) { - if (browser) { - openBrowser(link) - } else { - dadb.shell("am start -a android.intent.action.VIEW -d \"$link\"") - } + if (autoVerify && !browser && appId != null) autoVerifyLinkFromSettings(appId, link) + + if (browser) openBrowser(link) + else dadb.shell("am start -a android.intent.action.VIEW -d \"$link\"") if (autoVerify) { - autoVerifyApp(appId) + if (appId != null && !browser) autoVerifyWithChooser(appId) + autoVerifyChromeOnboarding() } } - private fun autoVerifyApp(appId: String?) { - if (appId != null) { - autoVerifyWithAppName(appId) + private fun autoVerifyLinkFromSettings(appId: String, link: String) { + val apiLevel = getDeviceApiLevel() + if (apiLevel <= 30) return + val domain = runCatching { URI.create(link).toURL().host }.getOrNull() ?: return + val packageOption = "--package $appId" + val allowed = dadb.shell("pm set-app-links-allowed --user 0 $packageOption true") + if (allowed.exitCode > 0) { + LOGGER.debug("set-app-links-allowed failed for appId: $appId reason: ${allowed.errorOutput}") + return + } + val selected = dadb.shell("pm set-app-links-user-selection --user 0 $packageOption true $domain") + if (selected.exitCode > 0) { + LOGGER.debug("set-app-links-user-selection failed for appId: $appId domain: $domain reason: ${selected.errorOutput}") } - autoVerifyChromeAgreement() } - private fun autoVerifyWithAppName(appId: String) { + private fun autoVerifyWithChooser(appId: String) { val appNameResult = runCatching { val apkFile = AndroidAppFiles.getApkFile(dadb, appId) val appName = ApkFile(apkFile).apkMeta.name apkFile.delete() - appName + appName ?: appId // The app chooser shows the appId if no application label attribute is set } - if (appNameResult.isSuccess) { - val appName = appNameResult.getOrThrow() - waitUntilScreenIsStatic(3000) - val appNameElement = filterByText(appName) - if (appNameElement != null) { - tap(appNameElement.bounds.center()) - filterById("android:id/button_once")?.let { - tap(it.bounds.center()) - } - } else { - val openWithAppElement = filterByText(".*$appName.*") - if (openWithAppElement != null) { - filterById("android:id/button_once")?.let { - tap(it.bounds.center()) - } - } - } + if (appNameResult.isFailure) { + LOGGER.info("Aborting autoVerify. Could not get app name from APK metadata for $appId", appNameResult.exceptionOrNull()) + return + } + + fun selectChooserOptionOnce(wordElement: UiElement) { + tap(wordElement.bounds.center()) + filterById("android:id/button_once")?.let { tap(it.bounds.center()) } } + + waitUntilScreenIsStatic(3000) + + val appName = appNameResult.getOrThrow() + filterByText(".*$appName.*")?.let { + selectChooserOptionOnce(it) + return + } + + val appNameWord = appName.split(" ").first() + filterByText(".*$appNameWord.*")?.let { + selectChooserOptionOnce(it) + return + } + + LOGGER.info("Aborting autoVerify. Could not find app name element for $appName") } - private fun autoVerifyChromeAgreement() { - filterById("com.android.chrome:id/terms_accept")?.let { tap(it.bounds.center()) } + private fun autoVerifyChromeOnboarding() { + val chrome = "com.android.chrome" + if (!isPackageInstalled(chrome)) return + Thread.sleep(100) // Lets enough time for the transition to Chrome to start + waitUntilScreenIsStatic(3000) + // Welcome to Chrome screen "Accept & continue" + filterById("$chrome:id/send_report_checkbox")?.let { tap(it.bounds.center()) } + filterById("$chrome:id/negative_button")?.let { tap(it.bounds.center()) } + filterById("$chrome:id/terms_accept")?.let { tap(it.bounds.center()) } + waitForAppToSettle(null, null) + // Welcome to Chrome screen "Add account to device" + filterById("$chrome:id/signin_fre_dismiss_button")?.let { tap(it.bounds.center()) } waitForAppToSettle(null, null) - filterById("com.android.chrome:id/negative_button")?.let { tap(it.bounds.center()) } + // Turn on Sync screen + filterById("$chrome:id/negative_button")?.let { tap(it.bounds.center()) } + waitForAppToSettle(null, null) + // Chrome Notifications screen + filterById("$chrome:id/negative_button")?.let { tap(it.bounds.center()) } } private fun filterByText(textRegex: String): UiElement? { @@ -572,13 +603,12 @@ class AndroidDriver( installedPackages.contains("com.android.chrome") -> { dadb.shell("am start -a android.intent.action.VIEW -d \"$link\" com.android.chrome") } - installedPackages.contains("org.mozilla.firefox") -> { dadb.shell("am start -a android.intent.action.VIEW -d \"$link\" org.mozilla.firefox") } - else -> { dadb.shell("am start -a android.intent.action.VIEW -d \"$link\"") + autoVerifyWithChooser("org.chromium.webview_shell") } } }