From dc402ca33b45274d0f5e08c568d166cb101e06ab Mon Sep 17 00:00:00 2001 From: Tarek Belkahia Date: Sun, 1 Sep 2024 22:50:45 +0100 Subject: [PATCH 1/9] Prevent passing a null appName result (#1956) --- maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt index 871b063b6d..8c085fc958 100644 --- a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt +++ b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt @@ -526,7 +526,7 @@ class AndroidDriver( val apkFile = AndroidAppFiles.getApkFile(dadb, appId) val appName = ApkFile(apkFile).apkMeta.name apkFile.delete() - appName + appName ?: error("App has no label on main activity.") } if (appNameResult.isSuccess) { val appName = appNameResult.getOrThrow() From b02ed01974d7966fe9f719acf8fffcb4dac35991 Mon Sep 17 00:00:00 2001 From: Tarek Belkahia Date: Mon, 2 Sep 2024 03:04:37 +0100 Subject: [PATCH 2/9] Replace with simpler command --- .../src/main/java/maestro/android/AndroidAppFiles.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/maestro-client/src/main/java/maestro/android/AndroidAppFiles.kt b/maestro-client/src/main/java/maestro/android/AndroidAppFiles.kt index bc91428e15..5a486447eb 100644 --- a/maestro-client/src/main/java/maestro/android/AndroidAppFiles.kt +++ b/maestro-client/src/main/java/maestro/android/AndroidAppFiles.kt @@ -38,9 +38,7 @@ 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) return dst From 1d7a1c77e2af67ac2d4963c6c5cf465d5ac7abea Mon Sep 17 00:00:00 2001 From: Tarek Belkahia Date: Mon, 2 Sep 2024 03:07:37 +0100 Subject: [PATCH 3/9] Workaround adb pull throwing Permission denied on some versions --- .../src/main/java/maestro/android/AndroidAppFiles.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/maestro-client/src/main/java/maestro/android/AndroidAppFiles.kt b/maestro-client/src/main/java/maestro/android/AndroidAppFiles.kt index 5a486447eb..13b61057db 100644 --- a/maestro-client/src/main/java/maestro/android/AndroidAppFiles.kt +++ b/maestro-client/src/main/java/maestro/android/AndroidAppFiles.kt @@ -40,7 +40,13 @@ object AndroidAppFiles { fun getApkFile(dadb: Dadb, appId: String): File { 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 } From a6437efc01ab58fef2e5c3d1ad36060e66671161 Mon Sep 17 00:00:00 2001 From: Tarek Belkahia Date: Mon, 2 Sep 2024 03:09:48 +0100 Subject: [PATCH 4/9] Handle app link verification from API 30 --- .../java/maestro/drivers/AndroidDriver.kt | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt index 8c085fc958..e3550fd65a 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,17 +504,32 @@ 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) } } + private fun autoVerifyLinkFromSettings(appId: String, link: String) { + val apiLevel = dadb.shell("getprop ro.build.version.sdk").output.trim().toInt() + 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}") + } + } + private fun autoVerifyApp(appId: String?) { if (appId != null) { autoVerifyWithAppName(appId) From 3f3d53efd3ca8d08e082bb4052f5a4da9aefc182 Mon Sep 17 00:00:00 2001 From: Tarek Belkahia Date: Mon, 2 Sep 2024 03:11:00 +0100 Subject: [PATCH 5/9] Fix verification for chrome screens --- .../main/java/maestro/drivers/AndroidDriver.kt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt index e3550fd65a..426fd48e32 100644 --- a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt +++ b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt @@ -565,9 +565,23 @@ class AndroidDriver( } private fun autoVerifyChromeAgreement() { - filterById("com.android.chrome:id/terms_accept")?.let { tap(it.bounds.center()) } + 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) - filterById("com.android.chrome:id/negative_button")?.let { tap(it.bounds.center()) } + // Welcome to Chrome screen "Add account to device" + filterById("$chrome:id/signin_fre_dismiss_button")?.let { tap(it.bounds.center()) } + waitForAppToSettle(null, null) + // 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? { From f374da2096f549fb4da6aff4444f1ec2f447d7ea Mon Sep 17 00:00:00 2001 From: Tarek Belkahia Date: Mon, 2 Sep 2024 03:12:47 +0100 Subject: [PATCH 6/9] Fix app verification with chooser --- .../java/maestro/drivers/AndroidDriver.kt | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt index 426fd48e32..c5919d36ee 100644 --- a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt +++ b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt @@ -531,37 +531,37 @@ class AndroidDriver( } private fun autoVerifyApp(appId: String?) { - if (appId != null) { - autoVerifyWithAppName(appId) - } - autoVerifyChromeAgreement() - } - - private fun autoVerifyWithAppName(appId: String) { val appNameResult = runCatching { val apkFile = AndroidAppFiles.getApkFile(dadb, appId) val appName = ApkFile(apkFile).apkMeta.name apkFile.delete() - appName ?: error("App has no label on main activity.") + 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() { From eed7d935aae9c43bbd3a6ba0ff65e54a3435fdd0 Mon Sep 17 00:00:00 2001 From: Tarek Belkahia Date: Mon, 2 Sep 2024 03:13:20 +0100 Subject: [PATCH 7/9] Fix verification for chrome and versions with chooser --- .../src/main/java/maestro/drivers/AndroidDriver.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt index c5919d36ee..4b2e567c3f 100644 --- a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt +++ b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt @@ -510,7 +510,8 @@ class AndroidDriver( else dadb.shell("am start -a android.intent.action.VIEW -d \"$link\"") if (autoVerify) { - autoVerifyApp(appId) + if (appId != null && !browser) autoVerifyWithChooser(appId) + autoVerifyChromeOnboarding() } } @@ -530,7 +531,7 @@ class AndroidDriver( } } - private fun autoVerifyApp(appId: String?) { + private fun autoVerifyWithChooser(appId: String) { val appNameResult = runCatching { val apkFile = AndroidAppFiles.getApkFile(dadb, appId) val appName = ApkFile(apkFile).apkMeta.name @@ -564,7 +565,7 @@ class AndroidDriver( LOGGER.info("Aborting autoVerify. Could not find app name element for $appName") } - private fun autoVerifyChromeAgreement() { + 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 From 54a6057b5665649b98574197ced799fbc9b930de Mon Sep 17 00:00:00 2001 From: Tarek Belkahia Date: Mon, 2 Sep 2024 03:13:44 +0100 Subject: [PATCH 8/9] Fix chooser selection of emulator webshell browser --- maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt index 4b2e567c3f..04bd357d48 100644 --- a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt +++ b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt @@ -603,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") } } } From 7cf65a059249dbbfe869e129121aa2efe73b006e Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Mon, 2 Sep 2024 21:26:35 +0200 Subject: [PATCH 9/9] use getDeviceApiLevel() --- maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt index 04bd357d48..d8f0c73239 100644 --- a/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt +++ b/maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt @@ -516,7 +516,7 @@ class AndroidDriver( } private fun autoVerifyLinkFromSettings(appId: String, link: String) { - val apiLevel = dadb.shell("getprop ro.build.version.sdk").output.trim().toInt() + val apiLevel = getDeviceApiLevel() if (apiLevel <= 30) return val domain = runCatching { URI.create(link).toURL().host }.getOrNull() ?: return val packageOption = "--package $appId"