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, + ) } }