diff --git a/README.md b/README.md
index 7e3af8a45..e55da2cd5 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# Bitkit Android (Native)
> [!CAUTION]
-> ⚠️This **NOT** the repository of the Bitkit app from the app stores!
-> ⚠️Work-in-progress
+> ⚠️This **NOT** the repository of the Bitkit app from the app stores!
+> ⚠️Work-in-progress
> The Bitkit app repository is here: **[github.com/synonymdev/bitkit](https://github.com/synonymdev/bitkit)**
---
diff --git a/app/src/main/java/to/bitkit/ui/MainActivity.kt b/app/src/main/java/to/bitkit/ui/MainActivity.kt
index 5362ea6d6..e87e3ea3e 100644
--- a/app/src/main/java/to/bitkit/ui/MainActivity.kt
+++ b/app/src/main/java/to/bitkit/ui/MainActivity.kt
@@ -10,6 +10,9 @@ import androidx.compose.animation.fadeOut
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -75,7 +78,11 @@ class MainActivity : FragmentActivity() {
installSplashScreen()
enableAppEdgeToEdge()
setContent {
- AppThemeSurface {
+ AppThemeSurface(
+ modifier = Modifier.semantics {
+ testTagsAsResourceId = true // see https://github.com/appium/appium/issues/15138
+ }
+ ) {
val scope = rememberCoroutineScope()
if (!walletViewModel.walletExists) {
val startupNavController = rememberNavController()
diff --git a/app/src/main/java/to/bitkit/ui/onboarding/CreateWalletScreen.kt b/app/src/main/java/to/bitkit/ui/onboarding/CreateWalletScreen.kt
index 440afb67a..7ffe3e7cb 100644
--- a/app/src/main/java/to/bitkit/ui/onboarding/CreateWalletScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/onboarding/CreateWalletScreen.kt
@@ -14,6 +14,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
@@ -33,10 +34,11 @@ import to.bitkit.ui.utils.withAccent
fun CreateWalletScreen(
onCreateClick: () -> Unit,
onRestoreClick: () -> Unit,
+ modifier: Modifier = Modifier,
) {
Box(
contentAlignment = Alignment.TopCenter,
- modifier = Modifier
+ modifier = modifier
.fillMaxSize()
) {
Image(
@@ -70,12 +72,16 @@ fun CreateWalletScreen(
PrimaryButton(
text = stringResource(R.string.onboarding__new_wallet),
onClick = onCreateClick,
- modifier = Modifier.weight(1f)
+ modifier = Modifier
+ .weight(1f)
+ .testTag("NewWallet")
)
SecondaryButton(
text = stringResource(R.string.onboarding__restore),
onClick = onRestoreClick,
- modifier = Modifier.weight(1f)
+ modifier = Modifier
+ .weight(1f)
+ .testTag("RestoreWallet")
)
}
Spacer(modifier = Modifier.height(16.dp))
@@ -83,7 +89,7 @@ fun CreateWalletScreen(
}
}
-@Preview(showBackground = true)
+@Preview(showSystemUi = true)
@Composable
private fun CreateWalletScreenPreview() {
AppThemeSurface {
diff --git a/app/src/main/java/to/bitkit/ui/onboarding/CreateWalletWithPassphraseScreen.kt b/app/src/main/java/to/bitkit/ui/onboarding/CreateWalletWithPassphraseScreen.kt
index e869412b2..75faf4ff9 100644
--- a/app/src/main/java/to/bitkit/ui/onboarding/CreateWalletWithPassphraseScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/onboarding/CreateWalletWithPassphraseScreen.kt
@@ -32,6 +32,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
@@ -122,19 +123,21 @@ fun CreateWalletWithPassphraseScreen(
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp)
+ .testTag("PassphraseInput")
)
Spacer(modifier = Modifier.height(32.dp))
PrimaryButton(
text = stringResource(R.string.onboarding__create_new_wallet),
onClick = { onCreateClick(bip39Passphrase) },
enabled = bip39Passphrase.isNotBlank(),
+ modifier = Modifier.testTag("CreateNewWallet")
)
Spacer(modifier = Modifier.height(32.dp))
}
}
}
-@Preview(showBackground = true)
+@Preview(showSystemUi = true)
@Composable
private fun CreateWalletWithPassphraseScreenPreview() {
AppThemeSurface {
diff --git a/app/src/main/java/to/bitkit/ui/onboarding/IntroScreen.kt b/app/src/main/java/to/bitkit/ui/onboarding/IntroScreen.kt
index a57083de2..606923dd1 100644
--- a/app/src/main/java/to/bitkit/ui/onboarding/IntroScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/onboarding/IntroScreen.kt
@@ -15,6 +15,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@@ -80,12 +81,16 @@ fun IntroScreen(
PrimaryButton(
text = stringResource(R.string.onboarding__get_started),
onClick = onStartClick,
- modifier = Modifier.weight(1f)
+ modifier = Modifier
+ .weight(1f)
+ .testTag("GetStarted")
)
SecondaryButton(
text = stringResource(R.string.onboarding__skip_intro),
onClick = onSkipClick,
- modifier = Modifier.weight(1f)
+ modifier = Modifier
+ .weight(1f)
+ .testTag("SkipIntro")
)
}
}
diff --git a/app/src/main/java/to/bitkit/ui/onboarding/OnboardingSlidesScreen.kt b/app/src/main/java/to/bitkit/ui/onboarding/OnboardingSlidesScreen.kt
index f138c8f67..7e72e7cd7 100644
--- a/app/src/main/java/to/bitkit/ui/onboarding/OnboardingSlidesScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/onboarding/OnboardingSlidesScreen.kt
@@ -33,6 +33,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
@@ -77,6 +78,7 @@ fun OnboardingSlidesScreen(
title = stringResource(R.string.onboarding__slide0_header),
titleAccentColor = Colors.Blue,
text = stringResource(R.string.onboarding__slide0_text),
+ modifier = Modifier.testTag("Slide0")
)
1 -> OnboardingTab(
@@ -85,6 +87,7 @@ fun OnboardingSlidesScreen(
titleAccentColor = Colors.Purple,
text = stringResource(R.string.onboarding__slide1_text),
disclaimerText = stringResource(R.string.onboarding__slide1_note), // TODO use GeoBlocking state
+ modifier = Modifier.testTag("Slide1")
)
2 -> OnboardingTab(
@@ -92,6 +95,7 @@ fun OnboardingSlidesScreen(
title = stringResource(R.string.onboarding__slide2_header),
titleAccentColor = Colors.Yellow,
text = stringResource(R.string.onboarding__slide2_text),
+ modifier = Modifier.testTag("Slide2")
)
3 -> OnboardingTab(
@@ -99,11 +103,13 @@ fun OnboardingSlidesScreen(
title = stringResource(R.string.onboarding__slide3_header),
titleAccentColor = Colors.Green,
text = stringResource(R.string.onboarding__slide3_text),
+ modifier = Modifier.testTag("Slide3")
)
4 -> CreateWalletScreen(
onCreateClick = onCreateClick,
onRestoreClick = onRestoreClick,
+ modifier = Modifier.testTag("Slide4")
)
}
}
@@ -153,7 +159,10 @@ fun OnboardingSlidesScreen(
title = { },
actions = {
if (pagerState.currentPage == 4) {
- TextButton(onClick = onAdvancedSetupClick) {
+ TextButton(
+ onClick = onAdvancedSetupClick,
+ modifier = Modifier.testTag("Passphrase")
+ ) {
Text(
text = stringResource(R.string.onboarding__advanced_setup),
fontSize = 17.sp,
@@ -162,9 +171,12 @@ fun OnboardingSlidesScreen(
)
}
} else {
- TextButton(onClick = {
- scope.launch { pagerState.animateScrollToPage(4) }
- }) {
+ TextButton(
+ onClick = {
+ scope.launch { pagerState.animateScrollToPage(pagerState.pageCount - 1) }
+ },
+ modifier = Modifier.testTag("SkipButton")
+ ) {
Text(
text = stringResource(R.string.onboarding__skip),
fontSize = 17.sp,
@@ -218,7 +230,7 @@ fun OnboardingTab(
}
}
-@Preview(showSystemUi = false)
+@Preview(showSystemUi = true)
@Composable
private fun OnboardingViewPreview() {
AppThemeSurface {
diff --git a/app/src/main/java/to/bitkit/ui/onboarding/RestoreWalletScreen.kt b/app/src/main/java/to/bitkit/ui/onboarding/RestoreWalletScreen.kt
index f802e0818..bf3796d84 100644
--- a/app/src/main/java/to/bitkit/ui/onboarding/RestoreWalletScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/onboarding/RestoreWalletScreen.kt
@@ -44,6 +44,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInParent
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
@@ -283,6 +284,7 @@ fun RestoreWalletView(
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp)
+ .testTag("PassphraseInput")
)
Spacer(modifier = Modifier.height(16.dp))
BodyS(
@@ -335,7 +337,9 @@ fun RestoreWalletView(
bip39Passphrase = ""
},
enabled = areButtonsEnabled,
- modifier = Modifier.weight(1f)
+ modifier = Modifier
+ .weight(1f)
+ .testTag("AdvancedButton")
)
}
PrimaryButton(
@@ -344,7 +348,9 @@ fun RestoreWalletView(
onRestoreClick(bip39Mnemonic, bip39Passphrase.takeIf { it.isNotEmpty() })
},
enabled = areButtonsEnabled,
- modifier = Modifier.weight(1f)
+ modifier = Modifier
+ .weight(1f)
+ .testTag("RestoreButton")
)
}
}
@@ -443,7 +449,7 @@ fun MnemonicInputField(
modifier = Modifier
.onFocusChanged { onFocusChanged(it.isFocused) }
.onGloballyPositioned { coordinates ->
- val position = coordinates.positionInParent().y.toInt() * 2 //double the scroll to ensure enough space
+ val position = coordinates.positionInParent().y.toInt() * 2 // double the scroll to ensure enough space
onPositionChanged(position)
}
)
diff --git a/app/src/main/java/to/bitkit/ui/onboarding/TermsOfUseScreen.kt b/app/src/main/java/to/bitkit/ui/onboarding/TermsOfUseScreen.kt
index 8ac7c347f..06055868d 100644
--- a/app/src/main/java/to/bitkit/ui/onboarding/TermsOfUseScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/onboarding/TermsOfUseScreen.kt
@@ -31,6 +31,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview
@@ -71,6 +72,7 @@ fun TermsOfUseScreen(
.fillMaxWidth()
.padding(horizontal = horizontalPadding)
.verticalScroll(rememberScrollState())
+ .testTag("TOS")
) {
Spacer(modifier = Modifier.height(16.dp))
Display(text = stringResource(R.string.onboarding__tos_header).withAccent())
@@ -102,7 +104,9 @@ fun TermsOfUseScreen(
.withAccentLink(Env.TERMS_OF_USE_URL),
isChecked = termsAccepted,
onCheckedChange = { termsAccepted = it },
- modifier = Modifier.padding(horizontal = horizontalPadding)
+ modifier = Modifier
+ .padding(horizontal = horizontalPadding)
+ .testTag("Check1")
)
CheckButton(
title = stringResource(R.string.onboarding__pp_checkbox),
@@ -110,7 +114,9 @@ fun TermsOfUseScreen(
.withAccentLink("https://bitkit.to/privacy-policy"),
isChecked = privacyAccepted,
onCheckedChange = { privacyAccepted = it },
- modifier = Modifier.padding(horizontal = horizontalPadding)
+ modifier = Modifier
+ .padding(horizontal = horizontalPadding)
+ .testTag("Check2")
)
Spacer(modifier = Modifier.height(24.dp))
@@ -119,7 +125,9 @@ fun TermsOfUseScreen(
text = stringResource(R.string.common__continue),
onClick = onNavigateToIntro,
enabled = termsAccepted && privacyAccepted,
- modifier = Modifier.padding(horizontal = horizontalPadding)
+ modifier = Modifier
+ .padding(horizontal = horizontalPadding)
+ .testTag("Continue")
)
}
}
@@ -185,7 +193,7 @@ private fun CheckmarkBox(isChecked: Boolean) {
}
}
-@Preview
+@Preview(showSystemUi = true)
@Composable
private fun TermsPreview() {
AppThemeSurface {
diff --git a/app/src/main/java/to/bitkit/ui/onboarding/WalletInitResultView.kt b/app/src/main/java/to/bitkit/ui/onboarding/WalletInitResultView.kt
index a3aae5f44..cb8a07f87 100644
--- a/app/src/main/java/to/bitkit/ui/onboarding/WalletInitResultView.kt
+++ b/app/src/main/java/to/bitkit/ui/onboarding/WalletInitResultView.kt
@@ -17,6 +17,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@@ -109,13 +110,20 @@ fun WalletInitResultView(
}
onButtonClick()
},
+ modifier = Modifier.testTag(
+ when (result) {
+ is WalletInitResult.Restored -> "GetStartedButton"
+ is WalletInitResult.Failed -> "TryAgainButton"
+ }
+ )
)
if (result is WalletInitResult.Failed && retryCount > 1 && onProceedWithoutRestore != null) {
Spacer(modifier = Modifier.height(12.dp))
SecondaryButton(
text = stringResource(R.string.onboarding__restore_no_backup_button),
- onClick = { showProceedDialog = true }
+ onClick = { showProceedDialog = true },
+ modifier = Modifier.testTag("ProceedWithoutBackupButton")
)
}
@@ -132,7 +140,8 @@ fun WalletInitResultView(
retryCount = 0
onProceedWithoutRestore?.invoke()
},
- onDismiss = { showProceedDialog = false }
+ onDismiss = { showProceedDialog = false },
+ modifier = Modifier.testTag("ProceedWithoutBackupDialog")
)
}
}
@@ -149,6 +158,10 @@ fun WalletInitResultViewRestoredPreview() {
@Composable
fun WalletInitResultViewErrorPreview() {
AppThemeSurface {
- WalletInitResultView(result = WalletInitResult.Failed(Error("Something went wrong")), onButtonClick = {})
+ WalletInitResultView(
+ result = WalletInitResult.Failed(Error("Something went wrong")),
+ onButtonClick = {},
+ onProceedWithoutRestore = {},
+ )
}
}
diff --git a/app/src/main/java/to/bitkit/ui/onboarding/WarningMultipleDevicesScreen.kt b/app/src/main/java/to/bitkit/ui/onboarding/WarningMultipleDevicesScreen.kt
index 158188803..45834b632 100644
--- a/app/src/main/java/to/bitkit/ui/onboarding/WarningMultipleDevicesScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/onboarding/WarningMultipleDevicesScreen.kt
@@ -7,10 +7,10 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@@ -30,7 +30,6 @@ fun WarningMultipleDevicesScreen(
onBackClick: () -> Unit,
onConfirmClick: () -> Unit,
) {
-
ScreenColumn {
AppTopBar(
titleText = null,
@@ -41,7 +40,7 @@ fun WarningMultipleDevicesScreen(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 32.dp)
- .systemBarsPadding()
+ .testTag("MultipleDevices")
) {
Image(
painter = painterResource(id = R.drawable.phone),
@@ -68,6 +67,7 @@ fun WarningMultipleDevicesScreen(
PrimaryButton(
text = stringResource(R.string.common__understood),
onClick = onConfirmClick,
+ modifier = Modifier.testTag("MultipleDevices-button")
)
Spacer(modifier = Modifier.height(24.dp))
diff --git a/app/src/main/java/to/bitkit/ui/theme/Theme.kt b/app/src/main/java/to/bitkit/ui/theme/Theme.kt
index a8b32b972..326046fd6 100644
--- a/app/src/main/java/to/bitkit/ui/theme/Theme.kt
+++ b/app/src/main/java/to/bitkit/ui/theme/Theme.kt
@@ -8,6 +8,7 @@ import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
+import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
val Gray100 = Color(0xFFF4F4F4)
@@ -51,11 +52,15 @@ private object ColorPalette {
@Composable
internal fun AppThemeSurface(
+ modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
val isSystemInDarkTheme = true // isSystemInDarkTheme() // use computed value for dark theme
AppTheme(inDarkTheme = isSystemInDarkTheme) {
- Surface(content = content)
+ Surface(
+ content = content,
+ modifier = modifier,
+ )
}
}