diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardFilled.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardFilled.kt index 27bc159878b..70802a44547 100644 --- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardFilled.kt +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardFilled.kt @@ -8,11 +8,9 @@ import androidx.compose.material3.Card as Material3Card @Composable fun CardFilled( modifier: Modifier = Modifier, - onClick: () -> Unit = {}, content: @Composable ColumnScope.() -> Unit, ) { Material3Card( - onClick = onClick, modifier = modifier, content = content, ) diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/text/TextBodyMedium.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/text/TextBodyMedium.kt index 105c68bc0fd..129b8fdaab9 100644 --- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/text/TextBodyMedium.kt +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/text/TextBodyMedium.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.style.LineHeightStyle import androidx.compose.ui.text.style.TextAlign import app.k9mail.core.ui.compose.theme2.MainTheme import androidx.compose.material3.Text as Material3Text @@ -39,3 +40,20 @@ fun TextBodyMedium( style = MainTheme.typography.bodyMedium, ) } + +@Composable +fun TextBodyMedium( + text: String, + lineHeightStyle: LineHeightStyle, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + textAlign: TextAlign? = null, +) { + Material3Text( + text = text, + modifier = modifier, + color = color, + textAlign = textAlign, + style = MainTheme.typography.bodyMedium.copy(lineHeightStyle = lineHeightStyle), + ) +} diff --git a/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavHost.kt b/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavHost.kt index c06c7495534..19d052c4805 100644 --- a/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavHost.kt +++ b/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavHost.kt @@ -88,8 +88,9 @@ fun OnboardingNavHost( composable(route = NESTED_NAVIGATION_ROUTE_MIGRATION) { onboardingMigrationManager.OnboardingMigrationScreen( - onQrCodeScanClick = { navController.navigateToSettingsImportQrCode() }, - onAddAccountClick = { navController.navigateToAccountSetup() }, + onQrCodeScan = { navController.navigateToSettingsImportQrCode() }, + onAddAccount = { navController.navigateToAccountSetup() }, + onImport = { navController.navigateToSettingsImport() }, ) } diff --git a/feature/onboarding/migration/api/src/main/kotlin/app/k9mail/feature/onboarding/migration/api/OnboardingMigrationManager.kt b/feature/onboarding/migration/api/src/main/kotlin/app/k9mail/feature/onboarding/migration/api/OnboardingMigrationManager.kt index a750ef22457..1234da1bb07 100644 --- a/feature/onboarding/migration/api/src/main/kotlin/app/k9mail/feature/onboarding/migration/api/OnboardingMigrationManager.kt +++ b/feature/onboarding/migration/api/src/main/kotlin/app/k9mail/feature/onboarding/migration/api/OnboardingMigrationManager.kt @@ -7,7 +7,8 @@ interface OnboardingMigrationManager { @Composable fun OnboardingMigrationScreen( - onQrCodeScanClick: () -> Unit, - onAddAccountClick: () -> Unit, + onQrCodeScan: () -> Unit, + onAddAccount: () -> Unit, + onImport: () -> Unit, ) } diff --git a/feature/onboarding/migration/noop/src/main/kotlin/app/k9mail/feature/onboarding/migration/noop/NoOpOnboardingMigrationManager.kt b/feature/onboarding/migration/noop/src/main/kotlin/app/k9mail/feature/onboarding/migration/noop/NoOpOnboardingMigrationManager.kt index e535451905c..bc37a1b321e 100644 --- a/feature/onboarding/migration/noop/src/main/kotlin/app/k9mail/feature/onboarding/migration/noop/NoOpOnboardingMigrationManager.kt +++ b/feature/onboarding/migration/noop/src/main/kotlin/app/k9mail/feature/onboarding/migration/noop/NoOpOnboardingMigrationManager.kt @@ -8,7 +8,8 @@ class NoOpOnboardingMigrationManager : OnboardingMigrationManager { @Composable override fun OnboardingMigrationScreen( - onQrCodeScanClick: () -> Unit, - onAddAccountClick: () -> Unit, + onQrCodeScan: () -> Unit, + onAddAccount: () -> Unit, + onImport: () -> Unit, ) = Unit } diff --git a/feature/onboarding/migration/thunderbird/src/debug/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationScreenPreview.kt b/feature/onboarding/migration/thunderbird/src/debug/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationScreenPreview.kt index 67874ef9de3..63064a0b112 100644 --- a/feature/onboarding/migration/thunderbird/src/debug/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationScreenPreview.kt +++ b/feature/onboarding/migration/thunderbird/src/debug/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationScreenPreview.kt @@ -12,8 +12,9 @@ internal fun TbOnboardingMigrationScreenPreview() { ThunderbirdTheme2 { Surface { TbOnboardingMigrationScreen( - onQrCodeScanClick = {}, - onAddAccountClick = {}, + onQrCodeScan = {}, + onAddAccount = {}, + onImport = {}, brandNameProvider = object : BrandNameProvider { override val brandName: String = "Thunderbird" }, diff --git a/feature/onboarding/migration/thunderbird/src/main/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationManager.kt b/feature/onboarding/migration/thunderbird/src/main/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationManager.kt index ef4691f5e6b..fbd2c019058 100644 --- a/feature/onboarding/migration/thunderbird/src/main/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationManager.kt +++ b/feature/onboarding/migration/thunderbird/src/main/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationManager.kt @@ -8,12 +8,14 @@ class TbOnboardingMigrationManager : OnboardingMigrationManager { @Composable override fun OnboardingMigrationScreen( - onQrCodeScanClick: () -> Unit, - onAddAccountClick: () -> Unit, + onQrCodeScan: () -> Unit, + onAddAccount: () -> Unit, + onImport: () -> Unit, ) { TbOnboardingMigrationScreen( - onQrCodeScanClick, - onAddAccountClick, + onQrCodeScan, + onAddAccount, + onImport, ) } } diff --git a/feature/onboarding/migration/thunderbird/src/main/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationScreen.kt b/feature/onboarding/migration/thunderbird/src/main/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationScreen.kt index d2072184a62..d2deab666c1 100644 --- a/feature/onboarding/migration/thunderbird/src/main/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationScreen.kt +++ b/feature/onboarding/migration/thunderbird/src/main/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationScreen.kt @@ -2,6 +2,7 @@ package app.k9mail.feature.onboarding.migration.thunderbird import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -14,20 +15,25 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.LineHeightStyle import app.k9mail.core.common.provider.BrandNameProvider import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonFilled +import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonOutlined import app.k9mail.core.ui.compose.designsystem.atom.card.CardFilled import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer import app.k9mail.core.ui.compose.theme2.MainTheme import app.k9mail.feature.account.common.ui.AppTitleTopHeader +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf import org.koin.compose.koinInject @Composable internal fun TbOnboardingMigrationScreen( - onQrCodeScanClick: () -> Unit, - onAddAccountClick: () -> Unit, + onQrCodeScan: () -> Unit, + onAddAccount: () -> Unit, + onImport: () -> Unit, modifier: Modifier = Modifier, brandNameProvider: BrandNameProvider = koinInject(), ) { @@ -47,41 +53,73 @@ internal fun TbOnboardingMigrationScreen( title = brandNameProvider.brandName, ) - Spacer(modifier = Modifier.height(MainTheme.spacings.double)) + Spacer( + modifier = Modifier + .height(MainTheme.spacings.double) + .weight(1f), + ) - TextCard(title = stringResource(R.string.onboarding_migration_thunderbird_qr_code_import_title)) { - TextBodyMedium( - text = stringResource(R.string.onboarding_migration_thunderbird_qr_code_import_description), - modifier = Modifier - .padding(bottom = MainTheme.spacings.double), - ) + AlreadyUsingThunderbirdCard(onQrCodeScan) - ButtonFilled( - text = stringResource(R.string.onboarding_migration_thunderbird_qr_code_import_button_text), - onClick = onQrCodeScanClick, - modifier = Modifier - .testTag("QrCodeImportButton") - .align(Alignment.CenterHorizontally), + Spacer(modifier = Modifier.height(MainTheme.spacings.triple)) + + TextGroup(title = stringResource(R.string.onboarding_migration_thunderbird_new_account_title)) { + ButtonOutlined( + text = stringResource(R.string.onboarding_migration_thunderbird_new_account_button_text), + onClick = onAddAccount, + modifier = Modifier.testTag("AddAccountButton"), ) } - Spacer(modifier = Modifier.height(MainTheme.spacings.double)) - - TextCard(title = stringResource(R.string.onboarding_migration_thunderbird_new_account_title)) { - ButtonFilled( - text = stringResource(R.string.onboarding_migration_thunderbird_new_account_button_text), - onClick = onAddAccountClick, - modifier = Modifier - .testTag("AddAccountButton") - .align(Alignment.CenterHorizontally), + TextGroup(title = stringResource(R.string.onboarding_migration_thunderbird_import_title)) { + ButtonOutlined( + text = stringResource(R.string.onboarding_migration_thunderbird_import_button_text), + onClick = onImport, + modifier = Modifier.testTag("ImportButton"), ) } - Spacer(modifier = Modifier.height(MainTheme.spacings.double)) + Spacer( + modifier = Modifier + .height(MainTheme.spacings.double) + .weight(1f), + ) } } } +@Composable +private fun AlreadyUsingThunderbirdCard(onQrCodeScan: () -> Unit) { + TextCard(title = stringResource(R.string.onboarding_migration_thunderbird_qr_code_import_title)) { + TextBodyMedium( + text = stringResource(R.string.onboarding_migration_thunderbird_qr_code_import_text), + modifier = Modifier + .padding(bottom = MainTheme.spacings.double), + ) + + TextBodyMediumFullLineHeight( + text = stringResource(R.string.onboarding_migration_thunderbird_qr_code_import_instructions_intro), + ) + + BulletList( + items = persistentListOf( + stringResource(R.string.onboarding_migration_thunderbird_qr_code_import_instructions_bullet_1), + stringResource(R.string.onboarding_migration_thunderbird_qr_code_import_instructions_bullet_2), + ), + modifier = Modifier + .padding(bottom = MainTheme.spacings.double), + ) + + ButtonFilled( + text = stringResource(R.string.onboarding_migration_thunderbird_qr_code_import_button_text), + onClick = onQrCodeScan, + modifier = Modifier + .testTag("QrCodeImportButton") + .align(Alignment.CenterHorizontally), + ) + } +} + @Composable private fun TextCard( title: String, @@ -108,3 +146,53 @@ private fun TextCard( } } } + +@Composable +private fun TextGroup( + title: String, + content: @Composable ColumnScope.() -> Unit, +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxWidth() + .padding(MainTheme.spacings.double), + ) { + TextTitleMedium( + text = title, + color = MainTheme.colors.primary, + modifier = Modifier + .padding(bottom = MainTheme.spacings.default), + ) + + content() + } +} + +@Composable +private fun BulletList( + items: ImmutableList, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier) { + for (item in items) { + Row { + TextBodyMediumFullLineHeight(text = " \u2022 ") + TextBodyMediumFullLineHeight(text = item) + } + } + } +} + +@Composable +private fun TextBodyMediumFullLineHeight(text: String) { + // Disable line height trimming so that the space between TextBodyMediumFullLineHeight instances following each + // other is the same as the space between lines of text inside a single TextBodyMedium. + TextBodyMedium( + text = text, + lineHeightStyle = LineHeightStyle( + alignment = LineHeightStyle.Alignment.Proportional, + trim = LineHeightStyle.Trim.None, + ), + ) +} diff --git a/feature/onboarding/migration/thunderbird/src/main/res/values/strings.xml b/feature/onboarding/migration/thunderbird/src/main/res/values/strings.xml index b907f8a426d..d373a1bdf61 100644 --- a/feature/onboarding/migration/thunderbird/src/main/res/values/strings.xml +++ b/feature/onboarding/migration/thunderbird/src/main/res/values/strings.xml @@ -1,8 +1,13 @@ Already using Thunderbird on desktop? - You can easily import your account settings.\n\nIn your Thunderbird desktop client, go to the top menu bar, click on ‘Settings’, and select ‘Export for Mobile’.\n\nA new tab with QR codes will appear. Use your Android device to scan the codes. + Easily import your account settings by scanning a QR code. + In Thunderbird desktop, go to: + ‘Settings’ in the Spaces Toolbar (bottom left) + Select ‘Export to Mobile’ and follow the instructions Import settings New to Thunderbird? Add an email account now + Moving from another app or device? + Import your account settings now diff --git a/feature/onboarding/migration/thunderbird/src/test/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationScreenKtTest.kt b/feature/onboarding/migration/thunderbird/src/test/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationScreenKtTest.kt index 2b0e8a22eef..395c0ba6ba2 100644 --- a/feature/onboarding/migration/thunderbird/src/test/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationScreenKtTest.kt +++ b/feature/onboarding/migration/thunderbird/src/test/kotlin/app/k9mail/feature/onboarding/migration/thunderbird/TbOnboardingMigrationScreenKtTest.kt @@ -12,13 +12,13 @@ import org.junit.Test class TbOnboardingMigrationScreenKtTest : ComposeTest() { @Test - fun `pressing QrCodeImportButton should call onQrCodeScanClick`() = runComposeTest { + fun `pressing QrCodeImportButton should call onQrCodeScan`() = runComposeTest { var qrCodeScanClickCounter = 0 - var addAccountClickCounter = 0 setContentWithTheme { TbOnboardingMigrationScreen( - onQrCodeScanClick = { qrCodeScanClickCounter++ }, - onAddAccountClick = { addAccountClickCounter++ }, + onQrCodeScan = { qrCodeScanClickCounter++ }, + onAddAccount = { error("Should not be called") }, + onImport = { error("Should not be called") }, brandNameProvider = FakeBrandNameProvider, ) } @@ -28,17 +28,16 @@ class TbOnboardingMigrationScreenKtTest : ComposeTest() { .performClick() assertThat(qrCodeScanClickCounter).isEqualTo(1) - assertThat(addAccountClickCounter).isEqualTo(0) } @Test - fun `pressing AddAccountButton button should call onAddAccountClick`() = runComposeTest { - var qrCodeScanClickCounter = 0 + fun `pressing AddAccountButton button should call onAddAccount`() = runComposeTest { var addAccountClickCounter = 0 setContentWithTheme { TbOnboardingMigrationScreen( - onQrCodeScanClick = { qrCodeScanClickCounter++ }, - onAddAccountClick = { addAccountClickCounter++ }, + onQrCodeScan = { error("Should not be called") }, + onAddAccount = { addAccountClickCounter++ }, + onImport = { error("Should not be called") }, brandNameProvider = FakeBrandNameProvider, ) } @@ -48,7 +47,25 @@ class TbOnboardingMigrationScreenKtTest : ComposeTest() { .performClick() assertThat(addAccountClickCounter).isEqualTo(1) - assertThat(qrCodeScanClickCounter).isEqualTo(0) + } + + @Test + fun `pressing ImportButton button should call onImport`() = runComposeTest { + var importClickCounter = 0 + setContentWithTheme { + TbOnboardingMigrationScreen( + onQrCodeScan = { error("Should not be called") }, + onAddAccount = { error("Should not be called") }, + onImport = { importClickCounter++ }, + brandNameProvider = FakeBrandNameProvider, + ) + } + + composeTestRule.onNodeWithTag("ImportButton") + .performScrollTo() + .performClick() + + assertThat(importClickCounter).isEqualTo(1) } }