From 549a56509cf92b3708d9e19ad24646fa15d842ef Mon Sep 17 00:00:00 2001 From: jacquargia Date: Fri, 6 Feb 2026 11:10:34 +0100 Subject: [PATCH 1/5] 4025:test: add demo app and maestro tests --- gradle/libs.versions.toml | 2 + install-app-lib/installappdemo/.gitignore | 1 + .../installappdemo/build.gradle.kts | 68 +++++++++ .../src/main/AndroidManifest.xml | 18 +++ .../e/apps/installapp/demo/MainActivity.kt | 143 ++++++++++++++++++ .../install-app-lib/install-cleanapk-app.yaml | 7 + settings.gradle | 1 + 7 files changed, 240 insertions(+) create mode 100644 install-app-lib/installappdemo/.gitignore create mode 100644 install-app-lib/installappdemo/build.gradle.kts create mode 100644 install-app-lib/installappdemo/src/main/AndroidManifest.xml create mode 100644 install-app-lib/installappdemo/src/main/java/foundation/e/apps/installapp/demo/MainActivity.kt create mode 100644 maestro/install-app-lib/install-cleanapk-app.yaml diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 55a276a24..d32637957 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -141,6 +141,8 @@ telemetry = { module = "foundation.e.lib:telemetry", version.ref = "telemetry" } timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } truth = { module = "com.google.truth:truth", version.ref = "truth" } viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "viewpager2" } +ui = { group = "androidx.compose.ui", name = "ui" } +ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" } work-testing = { group = "androidx.work", name = "work-testing", version.ref = "workTesting" } diff --git a/install-app-lib/installappdemo/.gitignore b/install-app-lib/installappdemo/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/install-app-lib/installappdemo/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/install-app-lib/installappdemo/build.gradle.kts b/install-app-lib/installappdemo/build.gradle.kts new file mode 100644 index 000000000..612bfcf89 --- /dev/null +++ b/install-app-lib/installappdemo/build.gradle.kts @@ -0,0 +1,68 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.compose.compiler) +} + +android { + namespace = "foundation.e.apps.installapp.demo" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + applicationId = "foundation.e.apps.installapp.demo" + minSdk = libs.versions.minSdk.get().toInt() + targetSdk = libs.versions.targetSdk.get().toInt() + versionCode = 1 + versionName = "1.0" + } + + signingConfigs { + create("platformConfig") { + storeFile = file("../../app/keystore/platform.jks") + storePassword = "platform" + keyAlias = "platform" + keyPassword = "platform" + } + } + + buildTypes { + debug { + signingConfig = signingConfigs.get("platformConfig") + } + + release { + isMinifyEnabled = false + } + } + compileOptions { + sourceCompatibility = JavaVersion.toVersion(libs.versions.jvmTarget.get()) + targetCompatibility = JavaVersion.toVersion(libs.versions.jvmTarget.get()) + } + kotlinOptions { + jvmTarget = libs.versions.jvmTarget.get() + } + buildFeatures { + compose = true + } +} + +dependencies { + implementation(project(":install-app-lib")) +// implementation("foundation.e.apps:install-app-lib:1.0.0") + + implementation(libs.core.ktx) + implementation(libs.lifecycle.runtime.ktx) + implementation(libs.activity.compose) + implementation(platform(libs.compose.bom)) + implementation(libs.ui) + implementation(libs.ui.graphics) + implementation(libs.compose.ui.tooling.preview) + implementation(libs.compose.material3) + testImplementation(libs.junit) + androidTestImplementation(libs.ext.junit) + androidTestImplementation(libs.espresso.core) + androidTestImplementation(platform(libs.compose.bom)) + androidTestImplementation(libs.compose.ui.test.junit4) + debugImplementation(libs.compose.ui.tooling) + debugImplementation(libs.compose.ui.test.manifest) +} diff --git a/install-app-lib/installappdemo/src/main/AndroidManifest.xml b/install-app-lib/installappdemo/src/main/AndroidManifest.xml new file mode 100644 index 000000000..b5b925508 --- /dev/null +++ b/install-app-lib/installappdemo/src/main/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/install-app-lib/installappdemo/src/main/java/foundation/e/apps/installapp/demo/MainActivity.kt b/install-app-lib/installappdemo/src/main/java/foundation/e/apps/installapp/demo/MainActivity.kt new file mode 100644 index 000000000..3d9e4ca35 --- /dev/null +++ b/install-app-lib/installappdemo/src/main/java/foundation/e/apps/installapp/demo/MainActivity.kt @@ -0,0 +1,143 @@ +package foundation.e.apps.installapp.demo + +import android.os.Bundle +import android.util.Log +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.lifecycleScope +import foundation.e.apps.installapp.AppInstaller +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch + +class MainActivity : ComponentActivity() { + private val status = MutableStateFlow("Not-connected") + private val installStatus = MutableStateFlow(null) + private var installJob: Job? = null + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + //AppLoungeTheme { + Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> + InstallAppScreen( + modifier = Modifier.padding(innerPadding), + status, + installStatus, + ::installApp, + ::stopInstallApp + ) + } + //} + } + } + + + private fun installApp(packageName: String) { + installStatus.value = null + val appInstaller = AppInstaller(this) + +// installJob = appInstaller.installByPackageName(packageName, installSource) +// .map { status.value = it.name } +// .launchIn(lifecycleScope) + + installJob = lifecycleScope.launch { + appInstaller.status.map { + Log.d("DebugGJ", "install ${packageName} status: $it ") + //lastStatus = it + status.value = it.name + }.launchIn(lifecycleScope) + + installStatus.value = appInstaller.installByPackageName(packageName).name + } + } + + + private fun stopInstallApp() { + installJob?.cancel() + } +} + +@Composable() +private fun InstallAppScreen( + modifier: Modifier, + statusFlow: Flow, + installStatusFlow: Flow, + installApp: (String)-> Unit, + stopInstallApp: () -> Unit) { + + var packageName by remember { mutableStateOf("") } + val status by statusFlow.collectAsState("not connected") + val installStatus by installStatusFlow.collectAsState(null) + + Column( + modifier = modifier + .fillMaxWidth() + .padding(16.dp) + ) { + TextField( + value = packageName, + onValueChange = { packageName = it }, + label = { Text(text = "Package name") }, + modifier = Modifier.fillMaxWidth() + ) + + Button(onClick = { + installApp(packageName) + }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp)) { + Text("Install") + } + + Button(onClick = { + stopInstallApp() + }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp)) { + Text("STOP") + } + + Row(modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp)) { + Text("Status: ") + Text(status) + } + installStatus?.let { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp) + ) { + Text("Install status: ") + Text(it) + } + } + } +} diff --git a/maestro/install-app-lib/install-cleanapk-app.yaml b/maestro/install-app-lib/install-cleanapk-app.yaml new file mode 100644 index 000000000..db7d6a365 --- /dev/null +++ b/maestro/install-app-lib/install-cleanapk-app.yaml @@ -0,0 +1,7 @@ +appId: foundation.e.apps.installapp.demo +env: + APK: "com.woefe.shoppinglist" + # "com.woefe.shoppinglist", "com.icedblueberry.shoppinglisteasy") +--- + +- launchApp diff --git a/settings.gradle b/settings.gradle index 3da8de187..afa5364b2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -65,3 +65,4 @@ include ':app' include ':parental-control-data' include ':auth-data-lib' include ':install-app-lib' +include ':install-app-lib:installappdemo' -- GitLab From 78a7a3382597d0b42b1ee373ddb2baca6c1959a8 Mon Sep 17 00:00:00 2001 From: jacquarg Date: Tue, 24 Feb 2026 18:01:04 +0100 Subject: [PATCH 2/5] test:4025: Customize maestro tests --- .../install-app-lib/abort_install_apk.yaml | 15 +++++++ .../first_start_anonymous.yaml | 9 +++++ .../install-app-lib/first_start_nogoogle.yaml | 9 +++++ .../install-app-lib/install-cleanapk-app.yaml | 6 ++- maestro/install-app-lib/install_apk.yaml | 23 +++++++++++ maestro/install-app-lib/run_cleanapk_tests.sh | 40 +++++++++++++++++++ .../install-app-lib/run_playstore_tests.sh | 31 ++++++++++++++ 7 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 maestro/install-app-lib/abort_install_apk.yaml create mode 100644 maestro/install-app-lib/first_start_anonymous.yaml create mode 100644 maestro/install-app-lib/first_start_nogoogle.yaml create mode 100644 maestro/install-app-lib/install_apk.yaml create mode 100755 maestro/install-app-lib/run_cleanapk_tests.sh create mode 100755 maestro/install-app-lib/run_playstore_tests.sh diff --git a/maestro/install-app-lib/abort_install_apk.yaml b/maestro/install-app-lib/abort_install_apk.yaml new file mode 100644 index 000000000..de4147b86 --- /dev/null +++ b/maestro/install-app-lib/abort_install_apk.yaml @@ -0,0 +1,15 @@ +# Scenario: install-app-lib abort with EXPECTED_STATE on install app by PACKAGE_NAME +# Given InstallAppDemo is installed +# When user set PACKAGE_NAME in packageName field +# And click Install +# Then the status finally change to EXPECTED_STATE + +appId: foundation.e.apps.installapp.demo +--- + +- launchApp +- tapOn: Package name +- inputText: ${PACKAGE_NAME} +- tapOn: Install + +- assertVisible: ${EXPECTED_STATE} diff --git a/maestro/install-app-lib/first_start_anonymous.yaml b/maestro/install-app-lib/first_start_anonymous.yaml new file mode 100644 index 000000000..9fa4013a8 --- /dev/null +++ b/maestro/install-app-lib/first_start_anonymous.yaml @@ -0,0 +1,9 @@ +appId: foundation.e.apps + +--- + +- launchApp: foundation.e.apps +- tapOn: + id: foundation.e.apps:id/agreeBT +- tapOn: + id: foundation.e.apps:id/anonymousBT diff --git a/maestro/install-app-lib/first_start_nogoogle.yaml b/maestro/install-app-lib/first_start_nogoogle.yaml new file mode 100644 index 000000000..3bf81347b --- /dev/null +++ b/maestro/install-app-lib/first_start_nogoogle.yaml @@ -0,0 +1,9 @@ +appId: foundation.e.apps + +--- + +- launchApp: foundation.e.apps +- tapOn: + id: foundation.e.apps:id/agreeBT +- tapOn: + id: foundation.e.apps:id/noGoogleBT diff --git a/maestro/install-app-lib/install-cleanapk-app.yaml b/maestro/install-app-lib/install-cleanapk-app.yaml index db7d6a365..139a9e350 100644 --- a/maestro/install-app-lib/install-cleanapk-app.yaml +++ b/maestro/install-app-lib/install-cleanapk-app.yaml @@ -4,4 +4,8 @@ env: # "com.woefe.shoppinglist", "com.icedblueberry.shoppinglisteasy") --- -- launchApp +- launchApp: foundation.e.apps.installapp.demo +- tapOn: Package name +- inputText: com.woefe.shoppinglist +- tapOn: Install +- assertVisible: INSTALLED diff --git a/maestro/install-app-lib/install_apk.yaml b/maestro/install-app-lib/install_apk.yaml new file mode 100644 index 000000000..05b6f627b --- /dev/null +++ b/maestro/install-app-lib/install_apk.yaml @@ -0,0 +1,23 @@ +# Scenario: Install app by PACKAGE_NAME through install-app-lib +# Given AppLounge is configured with anonymous or Google account +# And InstallAppDemo is installed +# And App with PACKAGE_NAME is not installed +# When user set PACKAGE_NAME in packageName field +# And click Install +# Then the app starts to download +# And status move to DOWNLOADING, INSTALLING and finally INSTALLED + +appId: foundation.e.apps.installapp.demo +#env: +# PACKAGE_NAME: "com.woefe.shoppinglist" +# # "com.woefe.shoppinglist", "com.icedblueberry.shoppinglisteasy") +--- + +- launchApp +- tapOn: Package name +- inputText: ${PACKAGE_NAME} +- tapOn: Install + +- assertVisible: DOWNLOADING +- assertVisible: INSTALLING +- assertVisible: INSTALLED diff --git a/maestro/install-app-lib/run_cleanapk_tests.sh b/maestro/install-app-lib/run_cleanapk_tests.sh new file mode 100755 index 000000000..2163391f5 --- /dev/null +++ b/maestro/install-app-lib/run_cleanapk_tests.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +cd "$(dirname "$0")" + +# Wake up the device if screen black (not password configured) +screen_info=`adb shell dumpsys power | grep mHoldingDisplaySuspendBlocker` +if [[ $screen_info == *mHoldingDisplaySuspendBlocker=false* ]] +then + adb shell input keyevent 26 +fi + +adb shell input keyevent 82 + +adb shell svc wifi enable +## End wake up device. + +# +adb shell pm clear foundation.e.apps + +# Scenario: Return TODO_error when AppLounge not ready yet +maestro test -e PACKAGE_NAME=no.an.existing.packangename -e EXPECTED_STATE=UNAVAILABLE abort_install_apk.yaml || exit 1 + +# Scenario: Setup AppLounge with anonymous Google account +maestro test first_start_nogoogle.yaml || exit 1 + +# Scenario: Install FDroid App through install-app-lib +adb uninstall com.woefe.shoppinglist +maestro test -e PACKAGE_NAME=com.woefe.shoppinglist install_apk.yaml || exit 1 + +# Scenario: Return UNAVAILABLE when app unavailable on stores +maestro test -e PACKAGE_NAME=no.an.existing.packangename -e EXPECTED_STATE=UNAVAILABLE abort_install_apk.yaml || exit 1 + +# Scenario: Install PWA App through install-app-lib +maestro test -e PACKAGE_NAME=dice.richardekwonye.com EXPECTED_STATE=INSTALLED abort_install_apk.yaml || exit 1 + +# Scenario: Return error when no network available +adb shell svc wifi disable +maestro test -e PACKAGE_NAME=no.an.existing.packangename -e EXPECTED_STATE=INSTALLATION_ISSUE abort_install_apk.yaml || exit 1 + +adb shell svc wifi enable diff --git a/maestro/install-app-lib/run_playstore_tests.sh b/maestro/install-app-lib/run_playstore_tests.sh new file mode 100755 index 000000000..beb5b0d84 --- /dev/null +++ b/maestro/install-app-lib/run_playstore_tests.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +cd "$(dirname "$0")" + +# Wake up the device if screen black (not password configured) +screen_info=`adb shell dumpsys power | grep mHoldingDisplaySuspendBlocker` +if [[ $screen_info == *mHoldingDisplaySuspendBlocker=false* ]] +then + adb shell input keyevent 26 +fi + +adb shell input keyevent 82 + +adb shell svc wifi enable +## End wake up device. + +adb shell pm clear foundation.e.apps + +# Scenario: Setup AppLounge with anonymous Google account +maestro test first_start_anonymous.yaml || exit 1 + +# Scenario: Install PlayStore App through install-app-lib +adb uninstall com.icedblueberry.shoppinglisteasy +maestro test -e PACKAGE_NAME=com.icedblueberry.shoppinglisteasy install_apk.yaml || exit 1 + +# Scenario: Return UNAVAILABLE when app unavailable on stores +maestro test -e PACKAGE_NAME=no.an.existing.packangename -e EXPECTED_STATE=UNAVAILABLE abort_install_apk.yaml || exit 1 + + +# Scenario: Return error when app not purchased in AppLounge +maestro test -e PACKAGE_NAME=nz.co.codepoint.minimetro -e EXPECTED_STATE=PURCHASE_NEEDED abort_install_apk.yaml || exit 1 -- GitLab From c44fd53b58a384e3f452bed049b5b38282d8105f Mon Sep 17 00:00:00 2001 From: jacquarg Date: Wed, 25 Feb 2026 16:46:50 +0100 Subject: [PATCH 3/5] chore:4025: fix linter --- .../installappdemo/build.gradle.kts | 5 -- .../src/main/AndroidManifest.xml | 3 +- .../e/apps/installapp/demo/MainActivity.kt | 68 +++++++++---------- .../src/main/res/drawable/app_icon.xml | 9 +++ 4 files changed, 42 insertions(+), 43 deletions(-) create mode 100644 install-app-lib/installappdemo/src/main/res/drawable/app_icon.xml diff --git a/install-app-lib/installappdemo/build.gradle.kts b/install-app-lib/installappdemo/build.gradle.kts index 612bfcf89..753c58943 100644 --- a/install-app-lib/installappdemo/build.gradle.kts +++ b/install-app-lib/installappdemo/build.gradle.kts @@ -58,11 +58,6 @@ dependencies { implementation(libs.ui.graphics) implementation(libs.compose.ui.tooling.preview) implementation(libs.compose.material3) - testImplementation(libs.junit) - androidTestImplementation(libs.ext.junit) - androidTestImplementation(libs.espresso.core) - androidTestImplementation(platform(libs.compose.bom)) - androidTestImplementation(libs.compose.ui.test.junit4) debugImplementation(libs.compose.ui.tooling) debugImplementation(libs.compose.ui.test.manifest) } diff --git a/install-app-lib/installappdemo/src/main/AndroidManifest.xml b/install-app-lib/installappdemo/src/main/AndroidManifest.xml index b5b925508..3dd3efdb7 100644 --- a/install-app-lib/installappdemo/src/main/AndroidManifest.xml +++ b/install-app-lib/installappdemo/src/main/AndroidManifest.xml @@ -4,7 +4,8 @@ + android:supportsRtl="true" + android:icon="@drawable/app_icon"> - InstallAppScreen( - modifier = Modifier.padding(innerPadding), - status, - installStatus, - ::installApp, - ::stopInstallApp - ) - } - //} + Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> + InstallAppScreen( + modifier = Modifier.padding(innerPadding), + status, + installStatus, + ::installApp, + ::stopInstallApp + ) + } } } - private fun installApp(packageName: String) { installStatus.value = null val appInstaller = AppInstaller(this) -// installJob = appInstaller.installByPackageName(packageName, installSource) -// .map { status.value = it.name } -// .launchIn(lifecycleScope) - installJob = lifecycleScope.launch { - appInstaller.status.map { - Log.d("DebugGJ", "install ${packageName} status: $it ") - //lastStatus = it - status.value = it.name - }.launchIn(lifecycleScope) - + appInstaller.status.map { status.value = it.name }.launchIn(lifecycleScope) installStatus.value = appInstaller.installByPackageName(packageName).name } } @@ -82,12 +69,13 @@ class MainActivity : ComponentActivity() { } @Composable() -private fun InstallAppScreen( +fun InstallAppScreen( modifier: Modifier, statusFlow: Flow, installStatusFlow: Flow, - installApp: (String)-> Unit, - stopInstallApp: () -> Unit) { + installApp: (String) -> Unit, + stopInstallApp: () -> Unit +) { var packageName by remember { mutableStateOf("") } val status by statusFlow.collectAsState("not connected") @@ -105,27 +93,33 @@ private fun InstallAppScreen( modifier = Modifier.fillMaxWidth() ) - Button(onClick = { - installApp(packageName) - }, + Button( + onClick = { + installApp(packageName) + }, modifier = Modifier .fillMaxWidth() - .padding(top = 12.dp)) { + .padding(top = 12.dp) + ) { Text("Install") } - Button(onClick = { - stopInstallApp() - }, + Button( + onClick = { + stopInstallApp() + }, modifier = Modifier .fillMaxWidth() - .padding(top = 12.dp)) { + .padding(top = 12.dp) + ) { Text("STOP") } - Row(modifier = Modifier - .fillMaxWidth() - .padding(top = 12.dp)) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp) + ) { Text("Status: ") Text(status) } diff --git a/install-app-lib/installappdemo/src/main/res/drawable/app_icon.xml b/install-app-lib/installappdemo/src/main/res/drawable/app_icon.xml new file mode 100644 index 000000000..51bc4f213 --- /dev/null +++ b/install-app-lib/installappdemo/src/main/res/drawable/app_icon.xml @@ -0,0 +1,9 @@ + + + -- GitLab From 2d5ebd5ab9473374dd4911b27c5988873a4bc9dc Mon Sep 17 00:00:00 2001 From: jacquargia Date: Mon, 2 Mar 2026 16:04:13 +0100 Subject: [PATCH 4/5] test:4025: Update GPlay tests. --- .../e/apps/installapp/demo/MainActivity.kt | 46 +++++++++++++++---- .../check_applounge_configuration.yaml | 12 +++++ .../first_start_google_account.yaml | 20 ++++++++ maestro/install-app-lib/run_cleanapk_tests.sh | 10 ++-- .../install-app-lib/run_playstore_tests.sh | 13 ++++-- 5 files changed, 85 insertions(+), 16 deletions(-) create mode 100644 maestro/install-app-lib/check_applounge_configuration.yaml create mode 100644 maestro/install-app-lib/first_start_google_account.yaml diff --git a/install-app-lib/installappdemo/src/main/java/foundation/e/apps/installapp/demo/MainActivity.kt b/install-app-lib/installappdemo/src/main/java/foundation/e/apps/installapp/demo/MainActivity.kt index 2cf9b8d90..41bbf7ed1 100644 --- a/install-app-lib/installappdemo/src/main/java/foundation/e/apps/installapp/demo/MainActivity.kt +++ b/install-app-lib/installappdemo/src/main/java/foundation/e/apps/installapp/demo/MainActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize @@ -20,9 +21,11 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.layoutId import androidx.compose.ui.unit.dp import androidx.lifecycle.lifecycleScope import foundation.e.apps.installapp.AppInstaller +import foundation.e.apps.installapp.AppLoungeConfiguration import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -33,6 +36,7 @@ import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { private val status = MutableStateFlow("Not-connected") private val installStatus = MutableStateFlow(null) + private val appLoungeConfigurationFlow = MutableStateFlow(null) private var installJob: Job? = null @@ -43,6 +47,7 @@ class MainActivity : ComponentActivity() { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> InstallAppScreen( modifier = Modifier.padding(innerPadding), + appLoungeConfigurationFlow, status, installStatus, ::installApp, @@ -50,6 +55,8 @@ class MainActivity : ComponentActivity() { ) } } + + getAppLoungeConfiguration() } private fun installApp(packageName: String) { @@ -62,15 +69,22 @@ class MainActivity : ComponentActivity() { } } - private fun stopInstallApp() { installJob?.cancel() } + + private fun getAppLoungeConfiguration() { + val appInstaller = AppInstaller(this) + lifecycleScope.launch { + appLoungeConfigurationFlow.value = appInstaller.getAppLoungeConfiguration() + } + } } @Composable() fun InstallAppScreen( modifier: Modifier, + appLoungeConfigurationFlow: Flow, statusFlow: Flow, installStatusFlow: Flow, installApp: (String) -> Unit, @@ -80,12 +94,28 @@ fun InstallAppScreen( var packageName by remember { mutableStateOf("") } val status by statusFlow.collectAsState("not connected") val installStatus by installStatusFlow.collectAsState(null) + val appLoungeConfiguration by appLoungeConfigurationFlow.collectAsState(null) Column( modifier = modifier .fillMaxWidth() - .padding(16.dp) + .padding(16.dp), + verticalArrangement = spacedBy(8.dp) ) { + Text("AppLounge configuration") + appLoungeConfiguration?.let { + Text("GPlayAccountType:") + Text( + it.gPlayAccountType.name, + Modifier.layoutId("GPlayAccountType") + ) + Text("SearchableSources:") + Text( + it.searchableSources.joinToString("-"), + Modifier.layoutId("SearchableSources"), + ) + } + TextField( value = packageName, onValueChange = { packageName = it }, @@ -94,12 +124,8 @@ fun InstallAppScreen( ) Button( - onClick = { - installApp(packageName) - }, - modifier = Modifier - .fillMaxWidth() - .padding(top = 12.dp) + onClick = { installApp(packageName) }, + modifier = Modifier.fillMaxWidth().padding(top = 12.dp) ) { Text("Install") } @@ -121,7 +147,7 @@ fun InstallAppScreen( .padding(top = 12.dp) ) { Text("Status: ") - Text(status) + Text(status, Modifier.layoutId("status")) } installStatus?.let { Row( @@ -130,7 +156,7 @@ fun InstallAppScreen( .padding(top = 12.dp) ) { Text("Install status: ") - Text(it) + Text(it, Modifier.layoutId("InstallStatus")) } } } diff --git a/maestro/install-app-lib/check_applounge_configuration.yaml b/maestro/install-app-lib/check_applounge_configuration.yaml new file mode 100644 index 000000000..858590303 --- /dev/null +++ b/maestro/install-app-lib/check_applounge_configuration.yaml @@ -0,0 +1,12 @@ +# Scenario: install-app-lib show AppLounge configuration +# Given InstallAppDemo is installed +# When open InstallAppDemo +# Then GPlayAccountType shows GPLAY_ACCOUNT_TYPE +# And SeachableSrouces shows SEARCHABLE_SOURCES + +appId: foundation.e.apps.installapp.demo +--- + +- launchApp +- assertVisible: ${GPLAY_ACCOUNT_TYPE} +- assertVisible: ${SEARCHABLE_SOURCES} diff --git a/maestro/install-app-lib/first_start_google_account.yaml b/maestro/install-app-lib/first_start_google_account.yaml new file mode 100644 index 000000000..edd27259a --- /dev/null +++ b/maestro/install-app-lib/first_start_google_account.yaml @@ -0,0 +1,20 @@ +appId: foundation.e.apps + +--- + +- launchApp: foundation.e.apps +- tapOn: + id: foundation.e.apps:id/agreeBT +- tapOn: + id: foundation.e.apps:id/googleBT +- tapOn: PROCEED TO GOOGLE LOGIN +- tapOn: Sign in with Google in AppLounge only +- tapOn: Email or phone +- inputText: ${GOOGLE_ACCOUNT} +- tapOn: Next +- extendedWaitUntil: + visible: Show password + timeout: 10000 +- inputText: ${GOOGLE_PASSWORD} +- tapOn: Next +- tapOn: I agree diff --git a/maestro/install-app-lib/run_cleanapk_tests.sh b/maestro/install-app-lib/run_cleanapk_tests.sh index 2163391f5..179e3f93b 100755 --- a/maestro/install-app-lib/run_cleanapk_tests.sh +++ b/maestro/install-app-lib/run_cleanapk_tests.sh @@ -14,15 +14,19 @@ adb shell input keyevent 82 adb shell svc wifi enable ## End wake up device. -# adb shell pm clear foundation.e.apps -# Scenario: Return TODO_error when AppLounge not ready yet +maestro test -e GPLAY_ACCOUNT_TYPE=NOT_CONFIGURED -e SEARCHABLE_SOURCES=PLAY_STORE-OPEN_SOURCE-PWA check_applounge_configuration.yaml || exit 1 + +# Scenario: Return UNAVAILABLE when AppLounge not ready yet maestro test -e PACKAGE_NAME=no.an.existing.packangename -e EXPECTED_STATE=UNAVAILABLE abort_install_apk.yaml || exit 1 # Scenario: Setup AppLounge with anonymous Google account maestro test first_start_nogoogle.yaml || exit 1 +# Scenario: AppLounge should be configured with no google now +maestro test -e GPLAY_ACCOUNT_TYPE=NO_GOOGLE -e SEARCHABLE_SOURCES=OPEN_SOURCE-PWA check_applounge_configuration.yaml || exit 1 + # Scenario: Install FDroid App through install-app-lib adb uninstall com.woefe.shoppinglist maestro test -e PACKAGE_NAME=com.woefe.shoppinglist install_apk.yaml || exit 1 @@ -31,7 +35,7 @@ maestro test -e PACKAGE_NAME=com.woefe.shoppinglist install_apk.yaml || exit 1 maestro test -e PACKAGE_NAME=no.an.existing.packangename -e EXPECTED_STATE=UNAVAILABLE abort_install_apk.yaml || exit 1 # Scenario: Install PWA App through install-app-lib -maestro test -e PACKAGE_NAME=dice.richardekwonye.com EXPECTED_STATE=INSTALLED abort_install_apk.yaml || exit 1 +maestro test -e PACKAGE_NAME=dice.richardekwonye.com -e EXPECTED_STATE=INSTALLED abort_install_apk.yaml || exit 1 # Scenario: Return error when no network available adb shell svc wifi disable diff --git a/maestro/install-app-lib/run_playstore_tests.sh b/maestro/install-app-lib/run_playstore_tests.sh index beb5b0d84..43ea9d122 100755 --- a/maestro/install-app-lib/run_playstore_tests.sh +++ b/maestro/install-app-lib/run_playstore_tests.sh @@ -1,7 +1,13 @@ #!/bin/bash +# Usage: with plugged in device, with AppLounge and applibdemo installed, +# $ run_playstore_test GOOGLE_ACCOUNT GOOGLE_PASSWORD + cd "$(dirname "$0")" +GOOGLE_ACCOUNT=$1 +GOOGLE_PASSWORD=$2 + # Wake up the device if screen black (not password configured) screen_info=`adb shell dumpsys power | grep mHoldingDisplaySuspendBlocker` if [[ $screen_info == *mHoldingDisplaySuspendBlocker=false* ]] @@ -17,7 +23,9 @@ adb shell svc wifi enable adb shell pm clear foundation.e.apps # Scenario: Setup AppLounge with anonymous Google account -maestro test first_start_anonymous.yaml || exit 1 +maestro test -e GOOGLE_ACCOUNT=$GOOGLE_ACCOUNT -e GOOGLE_PASSWORD=$GOOGLE_PASSWORD first_start_google_account.yaml || exit 1 + +maestro test -e GPLAY_ACCOUNT_TYPE=GOOGLE -e SEARCHABLE_SOURCES=PLAY_STORE-OPEN_SOURCE-PWA check_applounge_configuration.yaml || exit 1 # Scenario: Install PlayStore App through install-app-lib adb uninstall com.icedblueberry.shoppinglisteasy @@ -26,6 +34,5 @@ maestro test -e PACKAGE_NAME=com.icedblueberry.shoppinglisteasy install_apk.yaml # Scenario: Return UNAVAILABLE when app unavailable on stores maestro test -e PACKAGE_NAME=no.an.existing.packangename -e EXPECTED_STATE=UNAVAILABLE abort_install_apk.yaml || exit 1 - # Scenario: Return error when app not purchased in AppLounge -maestro test -e PACKAGE_NAME=nz.co.codepoint.minimetro -e EXPECTED_STATE=PURCHASE_NEEDED abort_install_apk.yaml || exit 1 +maestro test -e PACKAGE_NAME=com.MOBGames.PoppyMobileChap1 -e EXPECTED_STATE=PURCHASE_NEEDED abort_install_apk.yaml || exit 1 -- GitLab From a8368219daed7255edd413603e3f22efaed2a8d4 Mon Sep 17 00:00:00 2001 From: jacquargia Date: Wed, 4 Mar 2026 10:48:37 +0100 Subject: [PATCH 5/5] test:4025: fix demo app after API upgrade. --- .../foundation/e/apps/installapp/demo/MainActivity.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/install-app-lib/installappdemo/src/main/java/foundation/e/apps/installapp/demo/MainActivity.kt b/install-app-lib/installappdemo/src/main/java/foundation/e/apps/installapp/demo/MainActivity.kt index 41bbf7ed1..32daded84 100644 --- a/install-app-lib/installappdemo/src/main/java/foundation/e/apps/installapp/demo/MainActivity.kt +++ b/install-app-lib/installappdemo/src/main/java/foundation/e/apps/installapp/demo/MainActivity.kt @@ -29,12 +29,10 @@ import foundation.e.apps.installapp.AppLoungeConfiguration import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { - private val status = MutableStateFlow("Not-connected") + private val progress = MutableStateFlow("Not-connected") private val installStatus = MutableStateFlow(null) private val appLoungeConfigurationFlow = MutableStateFlow(null) private var installJob: Job? = null @@ -48,7 +46,7 @@ class MainActivity : ComponentActivity() { InstallAppScreen( modifier = Modifier.padding(innerPadding), appLoungeConfigurationFlow, - status, + progress, installStatus, ::installApp, ::stopInstallApp @@ -64,8 +62,9 @@ class MainActivity : ComponentActivity() { val appInstaller = AppInstaller(this) installJob = lifecycleScope.launch { - appInstaller.status.map { status.value = it.name }.launchIn(lifecycleScope) - installStatus.value = appInstaller.installByPackageName(packageName).name + installStatus.value = appInstaller.installByPackageName(packageName, { + progress.value = it.name + }).name } } -- GitLab