From d58637eea20d31bf4a0753d68d5c154567c87e37 Mon Sep 17 00:00:00 2001 From: cketti Date: Mon, 6 Nov 2023 19:09:29 +0100 Subject: [PATCH 1/3] Split onboarding into multiple modules --- app-feature-preview/build.gradle.kts | 2 +- .../feature/preview/navigation/FeatureNavHost.kt | 6 +++--- feature/launcher/build.gradle.kts | 2 +- .../feature/launcher/FeatureLauncherActivity.kt | 2 +- .../launcher/navigation/FeatureLauncherNavHost.kt | 4 ++-- feature/onboarding/main/build.gradle.kts | 13 +++++++++++++ .../main}/navigation/OnboardingNavigation.kt | 4 ++-- feature/onboarding/{ => welcome}/build.gradle.kts | 4 ++-- .../onboarding/welcome}/ui/OnboardingContent.kt | 4 ++-- .../onboarding/welcome}/ui/OnboardingScreen.kt | 2 +- .../main/res/drawable/onboarding_welcome_logo.xml | 0 .../src/main/res/values-ar}/strings.xml | 0 .../src/main/res/values-az}/strings.xml | 0 .../src/main/res/values-be}/strings.xml | 0 .../src/main/res/values-bg}/strings.xml | 0 .../src/main/res/values-br}/strings.xml | 0 .../src/main/res/values-ca/strings.xml | 0 .../src/main/res/values-cs}/strings.xml | 0 .../src/main/res/values-cy}/strings.xml | 0 .../src/main/res/values-da/strings.xml | 0 .../src/main/res/values-de/strings.xml | 0 .../src/main/res/values-el}/strings.xml | 0 .../src/main/res/values-en-rGB}/strings.xml | 0 .../src/main/res/values-eo}/strings.xml | 0 .../src/main/res/values-es}/strings.xml | 0 .../src/main/res/values-et}/strings.xml | 0 .../src/main/res/values-eu}/strings.xml | 0 .../src/main/res/values-fa}/strings.xml | 0 .../src/main/res/values-fi}/strings.xml | 0 .../src/main/res/values-fr}/strings.xml | 0 .../src/main/res/values-fy}/strings.xml | 0 .../src/main/res/values-gd}/strings.xml | 0 .../src/main/res/values-gl}/strings.xml | 0 .../src/main/res/values-hi/strings.xml | 0 .../src/main/res/values-hr}/strings.xml | 0 .../src/main/res/values-hu/strings.xml | 0 .../src/main/res/values-hy}/strings.xml | 0 .../src/main/res/values-in}/strings.xml | 0 .../src/main/res/values-is/strings.xml | 0 .../src/main/res/values-it}/strings.xml | 0 .../src/main/res/values-iw}/strings.xml | 0 .../src/main/res/values-ja/strings.xml | 0 .../src/main/res/values-ka}/strings.xml | 0 .../src/main/res/values-ko}/strings.xml | 0 .../src/main/res/values-lt}/strings.xml | 0 .../src/main/res/values-lv}/strings.xml | 0 .../src/main/res/values-ml}/strings.xml | 0 .../src/main/res/values-nb-rNO/strings.xml | 0 .../src/main/res/values-nl}/strings.xml | 0 .../src/main/res/values-nn}/strings.xml | 0 .../src/main/res/values-pl/strings.xml | 0 .../src/main/res/values-pt-rBR}/strings.xml | 0 .../src/main/res/values-pt-rPT}/strings.xml | 0 .../src/main/res/values-ro/strings.xml | 0 .../src/main/res/values-ru}/strings.xml | 0 .../src/main/res/values-sk}/strings.xml | 0 .../src/main/res/values-sl}/strings.xml | 0 .../src/main/res/values-sq/strings.xml | 0 .../src/main/res/values-sr}/strings.xml | 0 .../src/main/res/values-sv}/strings.xml | 0 .../src/main/res/values-ta}/strings.xml | 0 .../src/main/res/values-tr/strings.xml | 0 .../src/main/res/values-uk}/strings.xml | 0 .../src/main/res/values-vi}/strings.xml | 0 .../src/main/res/values-zh-rCN/strings.xml | 0 .../src/main/res/values-zh-rTW}/strings.xml | 0 .../{ => welcome}/src/main/res/values/strings.xml | 0 settings.gradle.kts | 6 +++++- 68 files changed, 33 insertions(+), 16 deletions(-) create mode 100644 feature/onboarding/main/build.gradle.kts rename feature/onboarding/{src/main/kotlin/app/k9mail/feature/onboarding => main/src/main/kotlin/app/k9mail/feature/onboarding/main}/navigation/OnboardingNavigation.kt (84%) rename feature/onboarding/{ => welcome}/build.gradle.kts (60%) rename feature/onboarding/{src/main/kotlin/app/k9mail/feature/onboarding => welcome/src/main/kotlin/app/k9mail/feature/onboarding/welcome}/ui/OnboardingContent.kt (98%) rename feature/onboarding/{src/main/kotlin/app/k9mail/feature/onboarding => welcome/src/main/kotlin/app/k9mail/feature/onboarding/welcome}/ui/OnboardingScreen.kt (90%) rename feature/onboarding/{ => welcome}/src/main/res/drawable/onboarding_welcome_logo.xml (100%) rename feature/onboarding/{src/main/res/values-cs => welcome/src/main/res/values-ar}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-bg => welcome/src/main/res/values-az}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-es => welcome/src/main/res/values-be}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-et => welcome/src/main/res/values-bg}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-eu => welcome/src/main/res/values-br}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-ca/strings.xml (100%) rename feature/onboarding/{src/main/res/values-fi => welcome/src/main/res/values-cs}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-fr => welcome/src/main/res/values-cy}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-da/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-de/strings.xml (100%) rename feature/onboarding/{src/main/res/values-fy => welcome/src/main/res/values-el}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-it => welcome/src/main/res/values-en-rGB}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-nl => welcome/src/main/res/values-eo}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-pt-rBR => welcome/src/main/res/values-es}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-sv => welcome/src/main/res/values-et}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-zh-rTW => welcome/src/main/res/values-eu}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-ar => welcome/src/main/res/values-fa}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-az => welcome/src/main/res/values-fi}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-be => welcome/src/main/res/values-fr}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-br => welcome/src/main/res/values-fy}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-cy => welcome/src/main/res/values-gd}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-el => welcome/src/main/res/values-gl}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-hi/strings.xml (100%) rename feature/onboarding/{src/main/res/values-en-rGB => welcome/src/main/res/values-hr}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-hu/strings.xml (100%) rename feature/onboarding/{src/main/res/values-eo => welcome/src/main/res/values-hy}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-fa => welcome/src/main/res/values-in}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-is/strings.xml (100%) rename feature/onboarding/{src/main/res/values-gd => welcome/src/main/res/values-it}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-gl => welcome/src/main/res/values-iw}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-ja/strings.xml (100%) rename feature/onboarding/{src/main/res/values-hr => welcome/src/main/res/values-ka}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-hy => welcome/src/main/res/values-ko}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-in => welcome/src/main/res/values-lt}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-iw => welcome/src/main/res/values-lv}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-ka => welcome/src/main/res/values-ml}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-nb-rNO/strings.xml (100%) rename feature/onboarding/{src/main/res/values-ko => welcome/src/main/res/values-nl}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-lt => welcome/src/main/res/values-nn}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-pl/strings.xml (100%) rename feature/onboarding/{src/main/res/values-lv => welcome/src/main/res/values-pt-rBR}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-ml => welcome/src/main/res/values-pt-rPT}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-ro/strings.xml (100%) rename feature/onboarding/{src/main/res/values-nn => welcome/src/main/res/values-ru}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-pt-rPT => welcome/src/main/res/values-sk}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-ru => welcome/src/main/res/values-sl}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-sq/strings.xml (100%) rename feature/onboarding/{src/main/res/values-sk => welcome/src/main/res/values-sr}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-sl => welcome/src/main/res/values-sv}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-sr => welcome/src/main/res/values-ta}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-tr/strings.xml (100%) rename feature/onboarding/{src/main/res/values-ta => welcome/src/main/res/values-uk}/strings.xml (100%) rename feature/onboarding/{src/main/res/values-uk => welcome/src/main/res/values-vi}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values-zh-rCN/strings.xml (100%) rename feature/onboarding/{src/main/res/values-vi => welcome/src/main/res/values-zh-rTW}/strings.xml (100%) rename feature/onboarding/{ => welcome}/src/main/res/values/strings.xml (100%) diff --git a/app-feature-preview/build.gradle.kts b/app-feature-preview/build.gradle.kts index 2d1beda7e..6566d944c 100644 --- a/app-feature-preview/build.gradle.kts +++ b/app-feature-preview/build.gradle.kts @@ -60,7 +60,7 @@ dependencies { implementation(projects.core.common) implementation(projects.mail.common) - implementation(projects.feature.onboarding) + implementation(projects.feature.onboarding.main) implementation(projects.feature.account.setup) implementation(projects.feature.account.edit) diff --git a/app-feature-preview/src/main/java/app/k9mail/feature/preview/navigation/FeatureNavHost.kt b/app-feature-preview/src/main/java/app/k9mail/feature/preview/navigation/FeatureNavHost.kt index 5f1a99e19..94b31fc51 100644 --- a/app-feature-preview/src/main/java/app/k9mail/feature/preview/navigation/FeatureNavHost.kt +++ b/app-feature-preview/src/main/java/app/k9mail/feature/preview/navigation/FeatureNavHost.kt @@ -8,9 +8,9 @@ import app.k9mail.feature.account.edit.navigation.accountEditRoute import app.k9mail.feature.account.edit.navigation.navigateToAccountEditIncomingServerSettings import app.k9mail.feature.account.setup.navigation.accountSetupRoute import app.k9mail.feature.account.setup.navigation.navigateToAccountSetup -import app.k9mail.feature.onboarding.navigation.NAVIGATION_ROUTE_ONBOARDING -import app.k9mail.feature.onboarding.navigation.navigateToOnboarding -import app.k9mail.feature.onboarding.navigation.onboardingRoute +import app.k9mail.feature.onboarding.main.navigation.NAVIGATION_ROUTE_ONBOARDING +import app.k9mail.feature.onboarding.main.navigation.navigateToOnboarding +import app.k9mail.feature.onboarding.main.navigation.onboardingRoute @Composable fun FeatureNavHost( diff --git a/feature/launcher/build.gradle.kts b/feature/launcher/build.gradle.kts index c7d08c7ad..72ce81fb3 100644 --- a/feature/launcher/build.gradle.kts +++ b/feature/launcher/build.gradle.kts @@ -19,7 +19,7 @@ android { dependencies { implementation(projects.core.ui.compose.designsystem) implementation(projects.app.ui.base) - implementation(projects.feature.onboarding) + implementation(projects.feature.onboarding.main) implementation(projects.feature.account.setup) implementation(projects.feature.account.edit) diff --git a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/FeatureLauncherActivity.kt b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/FeatureLauncherActivity.kt index af2050bb9..0b9dd7106 100644 --- a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/FeatureLauncherActivity.kt +++ b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/FeatureLauncherActivity.kt @@ -11,7 +11,7 @@ import app.k9mail.feature.account.edit.navigation.NAVIGATION_ROUTE_ACCOUNT_EDIT_ import app.k9mail.feature.account.edit.navigation.withAccountUuid import app.k9mail.feature.account.setup.navigation.NAVIGATION_ROUTE_ACCOUNT_SETUP import app.k9mail.feature.launcher.ui.FeatureLauncherApp -import app.k9mail.feature.onboarding.navigation.NAVIGATION_ROUTE_ONBOARDING +import app.k9mail.feature.onboarding.main.navigation.NAVIGATION_ROUTE_ONBOARDING import com.fsck.k9.ui.base.K9Activity class FeatureLauncherActivity : K9Activity() { diff --git a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/navigation/FeatureLauncherNavHost.kt b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/navigation/FeatureLauncherNavHost.kt index 5de11098b..b9a7d69ce 100644 --- a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/navigation/FeatureLauncherNavHost.kt +++ b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/navigation/FeatureLauncherNavHost.kt @@ -10,8 +10,8 @@ import app.k9mail.feature.account.setup.navigation.accountSetupRoute import app.k9mail.feature.account.setup.navigation.navigateToAccountSetup import app.k9mail.feature.launcher.FeatureLauncherExternalContract.AccountSetupFinishedLauncher import app.k9mail.feature.launcher.FeatureLauncherExternalContract.ImportSettingsLauncher -import app.k9mail.feature.onboarding.navigation.NAVIGATION_ROUTE_ONBOARDING -import app.k9mail.feature.onboarding.navigation.onboardingRoute +import app.k9mail.feature.onboarding.main.navigation.NAVIGATION_ROUTE_ONBOARDING +import app.k9mail.feature.onboarding.main.navigation.onboardingRoute import org.koin.compose.koinInject @Composable diff --git a/feature/onboarding/main/build.gradle.kts b/feature/onboarding/main/build.gradle.kts new file mode 100644 index 000000000..716a05212 --- /dev/null +++ b/feature/onboarding/main/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id(ThunderbirdPlugins.Library.androidCompose) +} + +android { + namespace = "app.k9mail.feature.onboarding.main" + resourcePrefix = "onboarding_main_" +} + +dependencies { + implementation(projects.core.ui.compose.designsystem) + implementation(projects.feature.onboarding.welcome) +} diff --git a/feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/navigation/OnboardingNavigation.kt b/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavigation.kt similarity index 84% rename from feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/navigation/OnboardingNavigation.kt rename to feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavigation.kt index a9e2263af..aa0082c5e 100644 --- a/feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/navigation/OnboardingNavigation.kt +++ b/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavigation.kt @@ -1,10 +1,10 @@ -package app.k9mail.feature.onboarding.navigation +package app.k9mail.feature.onboarding.main.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import app.k9mail.core.ui.compose.common.navigation.deepLinkComposable -import app.k9mail.feature.onboarding.ui.OnboardingScreen +import app.k9mail.feature.onboarding.welcome.ui.OnboardingScreen const val NAVIGATION_ROUTE_ONBOARDING = "onboarding" diff --git a/feature/onboarding/build.gradle.kts b/feature/onboarding/welcome/build.gradle.kts similarity index 60% rename from feature/onboarding/build.gradle.kts rename to feature/onboarding/welcome/build.gradle.kts index fd113b522..e620724e1 100644 --- a/feature/onboarding/build.gradle.kts +++ b/feature/onboarding/welcome/build.gradle.kts @@ -3,8 +3,8 @@ plugins { } android { - namespace = "app.k9mail.feature.onboarding" - resourcePrefix = "onboarding_" + namespace = "app.k9mail.feature.onboarding.welcome" + resourcePrefix = "onboarding_welcome_" } dependencies { diff --git a/feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/ui/OnboardingContent.kt b/feature/onboarding/welcome/src/main/kotlin/app/k9mail/feature/onboarding/welcome/ui/OnboardingContent.kt similarity index 98% rename from feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/ui/OnboardingContent.kt rename to feature/onboarding/welcome/src/main/kotlin/app/k9mail/feature/onboarding/welcome/ui/OnboardingContent.kt index d008a2bad..1da819eec 100644 --- a/feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/ui/OnboardingContent.kt +++ b/feature/onboarding/welcome/src/main/kotlin/app/k9mail/feature/onboarding/welcome/ui/OnboardingContent.kt @@ -1,4 +1,4 @@ -package app.k9mail.feature.onboarding.ui +package app.k9mail.feature.onboarding.welcome.ui import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement @@ -23,7 +23,7 @@ import app.k9mail.core.ui.compose.designsystem.template.ResponsiveContent import app.k9mail.core.ui.compose.theme.K9Theme import app.k9mail.core.ui.compose.theme.MainTheme import app.k9mail.core.ui.compose.theme.ThunderbirdTheme -import app.k9mail.feature.onboarding.R +import app.k9mail.feature.onboarding.welcome.R @Composable internal fun OnboardingContent( diff --git a/feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/ui/OnboardingScreen.kt b/feature/onboarding/welcome/src/main/kotlin/app/k9mail/feature/onboarding/welcome/ui/OnboardingScreen.kt similarity index 90% rename from feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/ui/OnboardingScreen.kt rename to feature/onboarding/welcome/src/main/kotlin/app/k9mail/feature/onboarding/welcome/ui/OnboardingScreen.kt index e1972a069..a274d9506 100644 --- a/feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/ui/OnboardingScreen.kt +++ b/feature/onboarding/welcome/src/main/kotlin/app/k9mail/feature/onboarding/welcome/ui/OnboardingScreen.kt @@ -1,4 +1,4 @@ -package app.k9mail.feature.onboarding.ui +package app.k9mail.feature.onboarding.welcome.ui import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview diff --git a/feature/onboarding/src/main/res/drawable/onboarding_welcome_logo.xml b/feature/onboarding/welcome/src/main/res/drawable/onboarding_welcome_logo.xml similarity index 100% rename from feature/onboarding/src/main/res/drawable/onboarding_welcome_logo.xml rename to feature/onboarding/welcome/src/main/res/drawable/onboarding_welcome_logo.xml diff --git a/feature/onboarding/src/main/res/values-cs/strings.xml b/feature/onboarding/welcome/src/main/res/values-ar/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-cs/strings.xml rename to feature/onboarding/welcome/src/main/res/values-ar/strings.xml diff --git a/feature/onboarding/src/main/res/values-bg/strings.xml b/feature/onboarding/welcome/src/main/res/values-az/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-bg/strings.xml rename to feature/onboarding/welcome/src/main/res/values-az/strings.xml diff --git a/feature/onboarding/src/main/res/values-es/strings.xml b/feature/onboarding/welcome/src/main/res/values-be/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-es/strings.xml rename to feature/onboarding/welcome/src/main/res/values-be/strings.xml diff --git a/feature/onboarding/src/main/res/values-et/strings.xml b/feature/onboarding/welcome/src/main/res/values-bg/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-et/strings.xml rename to feature/onboarding/welcome/src/main/res/values-bg/strings.xml diff --git a/feature/onboarding/src/main/res/values-eu/strings.xml b/feature/onboarding/welcome/src/main/res/values-br/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-eu/strings.xml rename to feature/onboarding/welcome/src/main/res/values-br/strings.xml diff --git a/feature/onboarding/src/main/res/values-ca/strings.xml b/feature/onboarding/welcome/src/main/res/values-ca/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-ca/strings.xml rename to feature/onboarding/welcome/src/main/res/values-ca/strings.xml diff --git a/feature/onboarding/src/main/res/values-fi/strings.xml b/feature/onboarding/welcome/src/main/res/values-cs/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-fi/strings.xml rename to feature/onboarding/welcome/src/main/res/values-cs/strings.xml diff --git a/feature/onboarding/src/main/res/values-fr/strings.xml b/feature/onboarding/welcome/src/main/res/values-cy/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-fr/strings.xml rename to feature/onboarding/welcome/src/main/res/values-cy/strings.xml diff --git a/feature/onboarding/src/main/res/values-da/strings.xml b/feature/onboarding/welcome/src/main/res/values-da/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-da/strings.xml rename to feature/onboarding/welcome/src/main/res/values-da/strings.xml diff --git a/feature/onboarding/src/main/res/values-de/strings.xml b/feature/onboarding/welcome/src/main/res/values-de/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-de/strings.xml rename to feature/onboarding/welcome/src/main/res/values-de/strings.xml diff --git a/feature/onboarding/src/main/res/values-fy/strings.xml b/feature/onboarding/welcome/src/main/res/values-el/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-fy/strings.xml rename to feature/onboarding/welcome/src/main/res/values-el/strings.xml diff --git a/feature/onboarding/src/main/res/values-it/strings.xml b/feature/onboarding/welcome/src/main/res/values-en-rGB/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-it/strings.xml rename to feature/onboarding/welcome/src/main/res/values-en-rGB/strings.xml diff --git a/feature/onboarding/src/main/res/values-nl/strings.xml b/feature/onboarding/welcome/src/main/res/values-eo/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-nl/strings.xml rename to feature/onboarding/welcome/src/main/res/values-eo/strings.xml diff --git a/feature/onboarding/src/main/res/values-pt-rBR/strings.xml b/feature/onboarding/welcome/src/main/res/values-es/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-pt-rBR/strings.xml rename to feature/onboarding/welcome/src/main/res/values-es/strings.xml diff --git a/feature/onboarding/src/main/res/values-sv/strings.xml b/feature/onboarding/welcome/src/main/res/values-et/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-sv/strings.xml rename to feature/onboarding/welcome/src/main/res/values-et/strings.xml diff --git a/feature/onboarding/src/main/res/values-zh-rTW/strings.xml b/feature/onboarding/welcome/src/main/res/values-eu/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-zh-rTW/strings.xml rename to feature/onboarding/welcome/src/main/res/values-eu/strings.xml diff --git a/feature/onboarding/src/main/res/values-ar/strings.xml b/feature/onboarding/welcome/src/main/res/values-fa/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-ar/strings.xml rename to feature/onboarding/welcome/src/main/res/values-fa/strings.xml diff --git a/feature/onboarding/src/main/res/values-az/strings.xml b/feature/onboarding/welcome/src/main/res/values-fi/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-az/strings.xml rename to feature/onboarding/welcome/src/main/res/values-fi/strings.xml diff --git a/feature/onboarding/src/main/res/values-be/strings.xml b/feature/onboarding/welcome/src/main/res/values-fr/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-be/strings.xml rename to feature/onboarding/welcome/src/main/res/values-fr/strings.xml diff --git a/feature/onboarding/src/main/res/values-br/strings.xml b/feature/onboarding/welcome/src/main/res/values-fy/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-br/strings.xml rename to feature/onboarding/welcome/src/main/res/values-fy/strings.xml diff --git a/feature/onboarding/src/main/res/values-cy/strings.xml b/feature/onboarding/welcome/src/main/res/values-gd/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-cy/strings.xml rename to feature/onboarding/welcome/src/main/res/values-gd/strings.xml diff --git a/feature/onboarding/src/main/res/values-el/strings.xml b/feature/onboarding/welcome/src/main/res/values-gl/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-el/strings.xml rename to feature/onboarding/welcome/src/main/res/values-gl/strings.xml diff --git a/feature/onboarding/src/main/res/values-hi/strings.xml b/feature/onboarding/welcome/src/main/res/values-hi/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-hi/strings.xml rename to feature/onboarding/welcome/src/main/res/values-hi/strings.xml diff --git a/feature/onboarding/src/main/res/values-en-rGB/strings.xml b/feature/onboarding/welcome/src/main/res/values-hr/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-en-rGB/strings.xml rename to feature/onboarding/welcome/src/main/res/values-hr/strings.xml diff --git a/feature/onboarding/src/main/res/values-hu/strings.xml b/feature/onboarding/welcome/src/main/res/values-hu/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-hu/strings.xml rename to feature/onboarding/welcome/src/main/res/values-hu/strings.xml diff --git a/feature/onboarding/src/main/res/values-eo/strings.xml b/feature/onboarding/welcome/src/main/res/values-hy/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-eo/strings.xml rename to feature/onboarding/welcome/src/main/res/values-hy/strings.xml diff --git a/feature/onboarding/src/main/res/values-fa/strings.xml b/feature/onboarding/welcome/src/main/res/values-in/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-fa/strings.xml rename to feature/onboarding/welcome/src/main/res/values-in/strings.xml diff --git a/feature/onboarding/src/main/res/values-is/strings.xml b/feature/onboarding/welcome/src/main/res/values-is/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-is/strings.xml rename to feature/onboarding/welcome/src/main/res/values-is/strings.xml diff --git a/feature/onboarding/src/main/res/values-gd/strings.xml b/feature/onboarding/welcome/src/main/res/values-it/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-gd/strings.xml rename to feature/onboarding/welcome/src/main/res/values-it/strings.xml diff --git a/feature/onboarding/src/main/res/values-gl/strings.xml b/feature/onboarding/welcome/src/main/res/values-iw/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-gl/strings.xml rename to feature/onboarding/welcome/src/main/res/values-iw/strings.xml diff --git a/feature/onboarding/src/main/res/values-ja/strings.xml b/feature/onboarding/welcome/src/main/res/values-ja/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-ja/strings.xml rename to feature/onboarding/welcome/src/main/res/values-ja/strings.xml diff --git a/feature/onboarding/src/main/res/values-hr/strings.xml b/feature/onboarding/welcome/src/main/res/values-ka/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-hr/strings.xml rename to feature/onboarding/welcome/src/main/res/values-ka/strings.xml diff --git a/feature/onboarding/src/main/res/values-hy/strings.xml b/feature/onboarding/welcome/src/main/res/values-ko/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-hy/strings.xml rename to feature/onboarding/welcome/src/main/res/values-ko/strings.xml diff --git a/feature/onboarding/src/main/res/values-in/strings.xml b/feature/onboarding/welcome/src/main/res/values-lt/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-in/strings.xml rename to feature/onboarding/welcome/src/main/res/values-lt/strings.xml diff --git a/feature/onboarding/src/main/res/values-iw/strings.xml b/feature/onboarding/welcome/src/main/res/values-lv/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-iw/strings.xml rename to feature/onboarding/welcome/src/main/res/values-lv/strings.xml diff --git a/feature/onboarding/src/main/res/values-ka/strings.xml b/feature/onboarding/welcome/src/main/res/values-ml/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-ka/strings.xml rename to feature/onboarding/welcome/src/main/res/values-ml/strings.xml diff --git a/feature/onboarding/src/main/res/values-nb-rNO/strings.xml b/feature/onboarding/welcome/src/main/res/values-nb-rNO/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-nb-rNO/strings.xml rename to feature/onboarding/welcome/src/main/res/values-nb-rNO/strings.xml diff --git a/feature/onboarding/src/main/res/values-ko/strings.xml b/feature/onboarding/welcome/src/main/res/values-nl/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-ko/strings.xml rename to feature/onboarding/welcome/src/main/res/values-nl/strings.xml diff --git a/feature/onboarding/src/main/res/values-lt/strings.xml b/feature/onboarding/welcome/src/main/res/values-nn/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-lt/strings.xml rename to feature/onboarding/welcome/src/main/res/values-nn/strings.xml diff --git a/feature/onboarding/src/main/res/values-pl/strings.xml b/feature/onboarding/welcome/src/main/res/values-pl/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-pl/strings.xml rename to feature/onboarding/welcome/src/main/res/values-pl/strings.xml diff --git a/feature/onboarding/src/main/res/values-lv/strings.xml b/feature/onboarding/welcome/src/main/res/values-pt-rBR/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-lv/strings.xml rename to feature/onboarding/welcome/src/main/res/values-pt-rBR/strings.xml diff --git a/feature/onboarding/src/main/res/values-ml/strings.xml b/feature/onboarding/welcome/src/main/res/values-pt-rPT/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-ml/strings.xml rename to feature/onboarding/welcome/src/main/res/values-pt-rPT/strings.xml diff --git a/feature/onboarding/src/main/res/values-ro/strings.xml b/feature/onboarding/welcome/src/main/res/values-ro/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-ro/strings.xml rename to feature/onboarding/welcome/src/main/res/values-ro/strings.xml diff --git a/feature/onboarding/src/main/res/values-nn/strings.xml b/feature/onboarding/welcome/src/main/res/values-ru/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-nn/strings.xml rename to feature/onboarding/welcome/src/main/res/values-ru/strings.xml diff --git a/feature/onboarding/src/main/res/values-pt-rPT/strings.xml b/feature/onboarding/welcome/src/main/res/values-sk/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-pt-rPT/strings.xml rename to feature/onboarding/welcome/src/main/res/values-sk/strings.xml diff --git a/feature/onboarding/src/main/res/values-ru/strings.xml b/feature/onboarding/welcome/src/main/res/values-sl/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-ru/strings.xml rename to feature/onboarding/welcome/src/main/res/values-sl/strings.xml diff --git a/feature/onboarding/src/main/res/values-sq/strings.xml b/feature/onboarding/welcome/src/main/res/values-sq/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-sq/strings.xml rename to feature/onboarding/welcome/src/main/res/values-sq/strings.xml diff --git a/feature/onboarding/src/main/res/values-sk/strings.xml b/feature/onboarding/welcome/src/main/res/values-sr/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-sk/strings.xml rename to feature/onboarding/welcome/src/main/res/values-sr/strings.xml diff --git a/feature/onboarding/src/main/res/values-sl/strings.xml b/feature/onboarding/welcome/src/main/res/values-sv/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-sl/strings.xml rename to feature/onboarding/welcome/src/main/res/values-sv/strings.xml diff --git a/feature/onboarding/src/main/res/values-sr/strings.xml b/feature/onboarding/welcome/src/main/res/values-ta/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-sr/strings.xml rename to feature/onboarding/welcome/src/main/res/values-ta/strings.xml diff --git a/feature/onboarding/src/main/res/values-tr/strings.xml b/feature/onboarding/welcome/src/main/res/values-tr/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-tr/strings.xml rename to feature/onboarding/welcome/src/main/res/values-tr/strings.xml diff --git a/feature/onboarding/src/main/res/values-ta/strings.xml b/feature/onboarding/welcome/src/main/res/values-uk/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-ta/strings.xml rename to feature/onboarding/welcome/src/main/res/values-uk/strings.xml diff --git a/feature/onboarding/src/main/res/values-uk/strings.xml b/feature/onboarding/welcome/src/main/res/values-vi/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-uk/strings.xml rename to feature/onboarding/welcome/src/main/res/values-vi/strings.xml diff --git a/feature/onboarding/src/main/res/values-zh-rCN/strings.xml b/feature/onboarding/welcome/src/main/res/values-zh-rCN/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-zh-rCN/strings.xml rename to feature/onboarding/welcome/src/main/res/values-zh-rCN/strings.xml diff --git a/feature/onboarding/src/main/res/values-vi/strings.xml b/feature/onboarding/welcome/src/main/res/values-zh-rTW/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values-vi/strings.xml rename to feature/onboarding/welcome/src/main/res/values-zh-rTW/strings.xml diff --git a/feature/onboarding/src/main/res/values/strings.xml b/feature/onboarding/welcome/src/main/res/values/strings.xml similarity index 100% rename from feature/onboarding/src/main/res/values/strings.xml rename to feature/onboarding/welcome/src/main/res/values/strings.xml diff --git a/settings.gradle.kts b/settings.gradle.kts index eb170d99c..91cd737b2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -39,7 +39,11 @@ include( include( ":feature:launcher", - ":feature:onboarding", +) + +include( + ":feature:onboarding:main", + ":feature:onboarding:welcome", ) include( From f3b06b9eb04ea344126e3a69b644f9407628fc90 Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 26 Oct 2023 14:45:42 +0200 Subject: [PATCH 2/3] Add onboarding permissions screen --- core/android/permissions/build.gradle.kts | 13 ++ .../permissions/AndroidPermissionChecker.kt | 38 ++++ .../CorePermissionsAndroidModule.kt | 8 + .../core/android/permissions/Permission.kt | 9 + .../android/permissions/PermissionChecker.kt | 8 + .../android/permissions/PermissionState.kt | 10 + .../AndroidPermissionCheckerTest.kt | 66 ++++++ .../image/ImageWithOverlayCoordinate.kt | 18 ++ .../common/visibility/VisibilityModifiers.kt | 9 + .../atom/DelayedCircularProgressIndicator.kt | 43 ++++ .../app/k9mail/core/ui/compose/theme/Icons.kt | 9 + .../theme/IconsWithBottomRightOverlay.kt | 27 +++ .../app/k9mail/core/ui/compose/theme/Sizes.kt | 1 + feature/onboarding/main/build.gradle.kts | 9 + .../onboarding/permissions/build.gradle.kts | 14 ++ .../permissions/PermissionsModule.kt | 21 ++ .../domain/PermissionsDomainContract.kt | 14 ++ .../domain/usecase/CheckPermission.kt | 18 ++ .../permissions/ui/PermissionBox.kt | 174 +++++++++++++++ .../permissions/ui/PermissionsContent.kt | 198 ++++++++++++++++++ .../permissions/ui/PermissionsContract.kt | 40 ++++ .../permissions/ui/PermissionsScreen.kt | 72 +++++++ .../permissions/ui/PermissionsViewModel.kt | 108 ++++++++++ .../src/main/res/values/strings.xml | 17 ++ settings.gradle.kts | 2 + 25 files changed, 946 insertions(+) create mode 100644 core/android/permissions/build.gradle.kts create mode 100644 core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/AndroidPermissionChecker.kt create mode 100644 core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/CorePermissionsAndroidModule.kt create mode 100644 core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/Permission.kt create mode 100644 core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/PermissionChecker.kt create mode 100644 core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/PermissionState.kt create mode 100644 core/android/permissions/src/test/kotlin/app/k9mail/core/android/permissions/AndroidPermissionCheckerTest.kt create mode 100644 core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/image/ImageWithOverlayCoordinate.kt create mode 100644 core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/visibility/VisibilityModifiers.kt create mode 100644 core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/DelayedCircularProgressIndicator.kt create mode 100644 core/ui/compose/theme/src/main/java/app/k9mail/core/ui/compose/theme/IconsWithBottomRightOverlay.kt create mode 100644 feature/onboarding/permissions/build.gradle.kts create mode 100644 feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/PermissionsModule.kt create mode 100644 feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/domain/PermissionsDomainContract.kt create mode 100644 feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/domain/usecase/CheckPermission.kt create mode 100644 feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionBox.kt create mode 100644 feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsContent.kt create mode 100644 feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsContract.kt create mode 100644 feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsScreen.kt create mode 100644 feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsViewModel.kt create mode 100644 feature/onboarding/permissions/src/main/res/values/strings.xml diff --git a/core/android/permissions/build.gradle.kts b/core/android/permissions/build.gradle.kts new file mode 100644 index 000000000..41bb4a224 --- /dev/null +++ b/core/android/permissions/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id(ThunderbirdPlugins.Library.android) +} + +android { + namespace = "app.k9mail.core.android.permissions" +} + +dependencies { + testImplementation(libs.androidx.test.core) + testImplementation(libs.robolectric) + testImplementation(libs.assertk) +} diff --git a/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/AndroidPermissionChecker.kt b/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/AndroidPermissionChecker.kt new file mode 100644 index 000000000..371bf2db3 --- /dev/null +++ b/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/AndroidPermissionChecker.kt @@ -0,0 +1,38 @@ +package app.k9mail.core.android.permissions + +import android.Manifest +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import androidx.core.content.ContextCompat + +/** + * Checks if a [Permission] has been granted to the app. + */ +class AndroidPermissionChecker( + private val context: Context, +) : PermissionChecker { + + override fun checkPermission(permission: Permission): PermissionState { + return when (permission) { + Permission.Contacts -> { + checkSelfPermission(Manifest.permission.READ_CONTACTS) + } + Permission.Notifications -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) + } else { + PermissionState.GrantedImplicitly + } + } + } + } + + private fun checkSelfPermission(permission: String): PermissionState { + return if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) { + PermissionState.Granted + } else { + PermissionState.Denied + } + } +} diff --git a/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/CorePermissionsAndroidModule.kt b/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/CorePermissionsAndroidModule.kt new file mode 100644 index 000000000..dc1885345 --- /dev/null +++ b/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/CorePermissionsAndroidModule.kt @@ -0,0 +1,8 @@ +package app.k9mail.core.android.permissions + +import org.koin.core.module.Module +import org.koin.dsl.module + +val corePermissionsAndroidModule: Module = module { + factory { AndroidPermissionChecker(context = get()) } +} diff --git a/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/Permission.kt b/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/Permission.kt new file mode 100644 index 000000000..4ffaa43c3 --- /dev/null +++ b/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/Permission.kt @@ -0,0 +1,9 @@ +package app.k9mail.core.android.permissions + +/** + * System permissions we ask for during onboarding. + */ +enum class Permission { + Contacts, + Notifications, +} diff --git a/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/PermissionChecker.kt b/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/PermissionChecker.kt new file mode 100644 index 000000000..6713b34a5 --- /dev/null +++ b/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/PermissionChecker.kt @@ -0,0 +1,8 @@ +package app.k9mail.core.android.permissions + +/** + * Checks if a [Permission] has been granted to the app. + */ +interface PermissionChecker { + fun checkPermission(permission: Permission): PermissionState +} diff --git a/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/PermissionState.kt b/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/PermissionState.kt new file mode 100644 index 000000000..fc123ae9f --- /dev/null +++ b/core/android/permissions/src/main/kotlin/app/k9mail/core/android/permissions/PermissionState.kt @@ -0,0 +1,10 @@ +package app.k9mail.core.android.permissions + +enum class PermissionState { + /** + * The permission is not a runtime permission in the Android version we're running on. + */ + GrantedImplicitly, + Granted, + Denied, +} diff --git a/core/android/permissions/src/test/kotlin/app/k9mail/core/android/permissions/AndroidPermissionCheckerTest.kt b/core/android/permissions/src/test/kotlin/app/k9mail/core/android/permissions/AndroidPermissionCheckerTest.kt new file mode 100644 index 000000000..af2ec34c3 --- /dev/null +++ b/core/android/permissions/src/test/kotlin/app/k9mail/core/android/permissions/AndroidPermissionCheckerTest.kt @@ -0,0 +1,66 @@ +package app.k9mail.core.android.permissions + +import android.Manifest +import android.app.Application +import android.os.Build +import androidx.test.core.app.ApplicationProvider +import assertk.assertThat +import assertk.assertions.isEqualTo +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.Shadows +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(minSdk = Build.VERSION_CODES.TIRAMISU) +class AndroidPermissionCheckerTest { + private val application: Application = ApplicationProvider.getApplicationContext() + private val shadowApplication = Shadows.shadowOf(application) + + private val permissionChecker = AndroidPermissionChecker(application) + + @Test + fun `granted READ_CONTACTS permission`() { + shadowApplication.grantPermissions(Manifest.permission.READ_CONTACTS) + + val result = permissionChecker.checkPermission(Permission.Contacts) + + assertThat(result).isEqualTo(PermissionState.Granted) + } + + @Test + fun `denied READ_CONTACTS permission`() { + shadowApplication.denyPermissions(Manifest.permission.READ_CONTACTS) + + val result = permissionChecker.checkPermission(Permission.Contacts) + + assertThat(result).isEqualTo(PermissionState.Denied) + } + + @Test + fun `granted POST_NOTIFICATIONS permission`() { + shadowApplication.grantPermissions(Manifest.permission.POST_NOTIFICATIONS) + + val result = permissionChecker.checkPermission(Permission.Notifications) + + assertThat(result).isEqualTo(PermissionState.Granted) + } + + @Test + fun `denied POST_NOTIFICATIONS permission`() { + shadowApplication.denyPermissions(Manifest.permission.POST_NOTIFICATIONS) + + val result = permissionChecker.checkPermission(Permission.Notifications) + + assertThat(result).isEqualTo(PermissionState.Denied) + } + + @Test + @Config(minSdk = Build.VERSION_CODES.S_V2, maxSdk = Build.VERSION_CODES.S_V2) + fun `POST_NOTIFICATIONS permission not available`() { + val result = permissionChecker.checkPermission(Permission.Notifications) + + assertThat(result).isEqualTo(PermissionState.GrantedImplicitly) + } +} diff --git a/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/image/ImageWithOverlayCoordinate.kt b/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/image/ImageWithOverlayCoordinate.kt new file mode 100644 index 000000000..e64bf2b39 --- /dev/null +++ b/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/image/ImageWithOverlayCoordinate.kt @@ -0,0 +1,18 @@ +package app.k9mail.core.ui.compose.common.image + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.Dp + +/** + * An image with a coordinate to draw a (smaller) overlay icon on top of it. + * + * Example: An icon representing an Android permission with an overlay icon to indicate whether the permission has been + * granted. + */ +@Immutable +data class ImageWithOverlayCoordinate( + val image: ImageVector, + val overlayOffsetX: Dp, + val overlayOffsetY: Dp, +) diff --git a/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/visibility/VisibilityModifiers.kt b/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/visibility/VisibilityModifiers.kt new file mode 100644 index 000000000..2425ecc4f --- /dev/null +++ b/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/visibility/VisibilityModifiers.kt @@ -0,0 +1,9 @@ +package app.k9mail.core.ui.compose.common.visibility + +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha + +/** + * Sets a composable to be fully transparent when it should be hidden (but still present for layout purposes). + */ +fun Modifier.hide(hide: Boolean) = alpha(if (hide) 0f else 1f) diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/DelayedCircularProgressIndicator.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/DelayedCircularProgressIndicator.kt new file mode 100644 index 000000000..2a84d1922 --- /dev/null +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/DelayedCircularProgressIndicator.kt @@ -0,0 +1,43 @@ +package app.k9mail.core.ui.compose.designsystem.atom + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +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.graphics.Color +import app.k9mail.core.ui.compose.common.visibility.hide +import app.k9mail.core.ui.compose.theme.MainTheme +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +private const val LOADING_INDICATOR_DELAY = 500L + +/** + * Only show a [CircularProgressIndicator] after [LOADING_INDICATOR_DELAY] ms. + * + * Use this to avoid flashing a loading indicator for loads that are usually very fast. + */ +@Composable +fun DelayedCircularProgressIndicator( + modifier: Modifier = Modifier, + color: Color = MainTheme.colors.secondary, +) { + var progressIndicatorVisible by remember { mutableStateOf(false) } + + LaunchedEffect(key1 = Unit) { + launch { + delay(LOADING_INDICATOR_DELAY) + progressIndicatorVisible = true + } + } + + CircularProgressIndicator( + modifier = Modifier + .hide(!progressIndicatorVisible) + .then(modifier), + color = color, + ) +} diff --git a/core/ui/compose/theme/src/main/java/app/k9mail/core/ui/compose/theme/Icons.kt b/core/ui/compose/theme/src/main/java/app/k9mail/core/ui/compose/theme/Icons.kt index 5ff0bf046..c20a75a8d 100644 --- a/core/ui/compose/theme/src/main/java/app/k9mail/core/ui/compose/theme/Icons.kt +++ b/core/ui/compose/theme/src/main/java/app/k9mail/core/ui/compose/theme/Icons.kt @@ -1,6 +1,9 @@ package app.k9mail.core.ui.compose.theme import androidx.compose.material.icons.filled.AccountCircle +import androidx.compose.material.icons.filled.Cancel +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.Error import androidx.compose.material.icons.filled.MoveToInbox import androidx.compose.material.icons.filled.Notifications @@ -48,6 +51,12 @@ object Icons { val user: ImageVector get() = MaterialIcons.Filled.AccountCircle + + val check: ImageVector + get() = MaterialIcons.Filled.CheckCircle + + val cancel: ImageVector + get() = MaterialIcons.Filled.Cancel } object Outlined { diff --git a/core/ui/compose/theme/src/main/java/app/k9mail/core/ui/compose/theme/IconsWithBottomRightOverlay.kt b/core/ui/compose/theme/src/main/java/app/k9mail/core/ui/compose/theme/IconsWithBottomRightOverlay.kt new file mode 100644 index 000000000..220d926c2 --- /dev/null +++ b/core/ui/compose/theme/src/main/java/app/k9mail/core/ui/compose/theme/IconsWithBottomRightOverlay.kt @@ -0,0 +1,27 @@ +package app.k9mail.core.ui.compose.theme + +import androidx.compose.material.icons.filled.Notifications +import androidx.compose.material.icons.filled.Person +import androidx.compose.ui.unit.dp +import app.k9mail.core.ui.compose.common.image.ImageWithOverlayCoordinate +import androidx.compose.material.icons.Icons as MaterialIcons + +// We're using "by lazy" so not all icons are loaded into memory as soon as the object is accessed. But once a property +// is accessed we want to retain the `ImageWithOverlayCoordinate` instance. +object IconsWithBottomRightOverlay { + val person: ImageWithOverlayCoordinate by lazy { + ImageWithOverlayCoordinate( + image = MaterialIcons.Filled.Person, + overlayOffsetX = 24.dp, + overlayOffsetY = 20.dp, + ) + } + + val notification: ImageWithOverlayCoordinate by lazy { + ImageWithOverlayCoordinate( + image = MaterialIcons.Filled.Notifications, + overlayOffsetX = 23.dp, + overlayOffsetY = 19.dp, + ) + } +} diff --git a/core/ui/compose/theme/src/main/java/app/k9mail/core/ui/compose/theme/Sizes.kt b/core/ui/compose/theme/src/main/java/app/k9mail/core/ui/compose/theme/Sizes.kt index 148270cf7..fc5b60c70 100644 --- a/core/ui/compose/theme/src/main/java/app/k9mail/core/ui/compose/theme/Sizes.kt +++ b/core/ui/compose/theme/src/main/java/app/k9mail/core/ui/compose/theme/Sizes.kt @@ -16,6 +16,7 @@ data class Sizes( val huger: Dp = 384.dp, val icon: Dp = 24.dp, + val largeIcon: Dp = 32.dp, val topBarHeight: Dp = 56.dp, val bottomBarHeight: Dp = 56.dp, diff --git a/feature/onboarding/main/build.gradle.kts b/feature/onboarding/main/build.gradle.kts index 716a05212..1b82fa832 100644 --- a/feature/onboarding/main/build.gradle.kts +++ b/feature/onboarding/main/build.gradle.kts @@ -5,6 +5,15 @@ plugins { android { namespace = "app.k9mail.feature.onboarding.main" resourcePrefix = "onboarding_main_" + + buildTypes { + debug { + manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project" + } + release { + manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project" + } + } } dependencies { diff --git a/feature/onboarding/permissions/build.gradle.kts b/feature/onboarding/permissions/build.gradle.kts new file mode 100644 index 000000000..240d12f9f --- /dev/null +++ b/feature/onboarding/permissions/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id(ThunderbirdPlugins.Library.androidCompose) +} + +android { + namespace = "app.k9mail.feature.onboarding.permissions" + resourcePrefix = "onboarding_permissions_" +} + +dependencies { + implementation(projects.core.ui.compose.designsystem) + implementation(projects.core.android.permissions) + implementation(projects.feature.account.common) +} diff --git a/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/PermissionsModule.kt b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/PermissionsModule.kt new file mode 100644 index 000000000..7a4b1b7e7 --- /dev/null +++ b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/PermissionsModule.kt @@ -0,0 +1,21 @@ +package app.k9mail.feature.onboarding.permissions + +import app.k9mail.core.android.permissions.corePermissionsAndroidModule +import app.k9mail.feature.onboarding.permissions.domain.PermissionsDomainContract.UseCase +import app.k9mail.feature.onboarding.permissions.domain.usecase.CheckPermission +import app.k9mail.feature.onboarding.permissions.ui.PermissionsViewModel +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.core.module.Module +import org.koin.dsl.module + +val featureOnboardingPermissionsModule: Module = module { + includes(corePermissionsAndroidModule) + + factory { CheckPermission(permissionChecker = get()) } + + viewModel { + PermissionsViewModel( + checkPermission = get(), + ) + } +} diff --git a/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/domain/PermissionsDomainContract.kt b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/domain/PermissionsDomainContract.kt new file mode 100644 index 000000000..ef1fb950b --- /dev/null +++ b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/domain/PermissionsDomainContract.kt @@ -0,0 +1,14 @@ +package app.k9mail.feature.onboarding.permissions.domain + +import app.k9mail.core.android.permissions.Permission +import app.k9mail.core.android.permissions.PermissionState + +interface PermissionsDomainContract { + + interface UseCase { + + fun interface CheckPermission { + operator fun invoke(permission: Permission): PermissionState + } + } +} diff --git a/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/domain/usecase/CheckPermission.kt b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/domain/usecase/CheckPermission.kt new file mode 100644 index 000000000..5f53d40fd --- /dev/null +++ b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/domain/usecase/CheckPermission.kt @@ -0,0 +1,18 @@ +package app.k9mail.feature.onboarding.permissions.domain.usecase + +import app.k9mail.core.android.permissions.Permission +import app.k9mail.core.android.permissions.PermissionChecker +import app.k9mail.core.android.permissions.PermissionState +import app.k9mail.feature.onboarding.permissions.domain.PermissionsDomainContract.UseCase + +/** + * Checks if a [Permission] has been granted to the app. + */ +class CheckPermission( + private val permissionChecker: PermissionChecker, +) : UseCase.CheckPermission { + + override fun invoke(permission: Permission): PermissionState { + return permissionChecker.checkPermission(permission) + } +} diff --git a/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionBox.kt b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionBox.kt new file mode 100644 index 000000000..e16efd406 --- /dev/null +++ b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionBox.kt @@ -0,0 +1,174 @@ +package app.k9mail.feature.onboarding.permissions.ui + +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import app.k9mail.core.ui.compose.common.image.ImageWithOverlayCoordinate +import app.k9mail.core.ui.compose.designsystem.atom.Icon +import app.k9mail.core.ui.compose.designsystem.atom.button.Button +import app.k9mail.core.ui.compose.designsystem.atom.text.TextBody2 +import app.k9mail.core.ui.compose.designsystem.atom.text.TextHeadline6 +import app.k9mail.core.ui.compose.theme.Icons +import app.k9mail.core.ui.compose.theme.IconsWithBottomRightOverlay +import app.k9mail.core.ui.compose.theme.K9Theme +import app.k9mail.core.ui.compose.theme.MainTheme +import app.k9mail.feature.onboarding.permissions.R +import app.k9mail.feature.onboarding.permissions.ui.PermissionsContract.UiPermissionState + +private val MAX_WIDTH = 500.dp + +@Composable +internal fun PermissionBox( + icon: ImageWithOverlayCoordinate, + permissionState: UiPermissionState, + title: String, + description: String, + onAllowClick: () -> Unit, +) { + Column( + modifier = Modifier + .width(MAX_WIDTH) + .padding(horizontal = MainTheme.spacings.double), + ) { + Row { + Box( + modifier = Modifier.padding( + end = MainTheme.spacings.double, + top = MainTheme.spacings.default, + bottom = MainTheme.spacings.default, + ), + ) { + IconWithPermissionStateOverlay(icon, permissionState) + } + Column { + TextHeadline6(text = title) + TextBody2(text = description) + } + } + + val buttonAlpha by animateFloatAsState( + targetValue = if (permissionState == UiPermissionState.Granted) 0f else 1f, + label = "AllowButtonAlpha", + ) + + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.End, + ) { + Spacer(modifier = Modifier.height(MainTheme.spacings.default)) + + Button( + text = stringResource(R.string.onboarding_permissions_allow_button), + onClick = onAllowClick, + modifier = Modifier.alpha(buttonAlpha), + ) + } + } +} + +@Composable +private fun IconWithPermissionStateOverlay( + icon: ImageWithOverlayCoordinate, + permissionState: UiPermissionState, +) { + Box { + val iconSize = MainTheme.sizes.largeIcon + val overlayIconSize = iconSize / 2 + val overlayIconOffset = overlayIconSize / 2 + val scalingFactor = iconSize / icon.image.defaultHeight + val overlayOffsetX = (icon.overlayOffsetX * scalingFactor) - overlayIconOffset + val overlayOffsetY = (icon.overlayOffsetY * scalingFactor) - overlayIconOffset + + Icon( + imageVector = icon.image, + modifier = Modifier.size(iconSize), + ) + + when (permissionState) { + UiPermissionState.Unknown -> Unit + UiPermissionState.Granted -> { + Icon( + imageVector = Icons.Filled.check, + tint = MainTheme.colors.success, + modifier = Modifier + .size(overlayIconSize) + .offset( + x = overlayOffsetX, + y = overlayOffsetY, + ), + ) + } + + UiPermissionState.Denied -> { + Icon( + imageVector = Icons.Filled.cancel, + tint = MainTheme.colors.warning, + modifier = Modifier + .size(overlayIconSize) + .offset( + x = overlayOffsetX, + y = overlayOffsetY, + ), + ) + } + } + } +} + +@Preview(showBackground = true) +@Composable +internal fun PermissionBoxUnknownStatePreview() { + K9Theme { + PermissionBox( + icon = IconsWithBottomRightOverlay.person, + permissionState = UiPermissionState.Unknown, + title = "Contacts", + description = "Allow access to be able to display contact names and photos.", + onAllowClick = {}, + ) + } +} + +@Preview(showBackground = true) +@Composable +internal fun PermissionBoxGrantedStatePreview() { + K9Theme { + PermissionBox( + icon = IconsWithBottomRightOverlay.person, + permissionState = UiPermissionState.Granted, + title = "Contacts", + description = "Allow access to be able to display contact names and photos.", + onAllowClick = {}, + ) + } +} + +@Preview(showBackground = true) +@Composable +internal fun PermissionBoxDeniedStatePreview() { + K9Theme { + PermissionBox( + icon = IconsWithBottomRightOverlay.person, + permissionState = UiPermissionState.Denied, + title = "Contacts", + description = "Allow access to be able to display contact names and photos.", + onAllowClick = {}, + ) + } +} diff --git a/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsContent.kt b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsContent.kt new file mode 100644 index 000000000..127331b1a --- /dev/null +++ b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsContent.kt @@ -0,0 +1,198 @@ +package app.k9mail.feature.onboarding.permissions.ui + +import androidx.compose.animation.Crossfade +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.k9mail.core.ui.compose.common.PreviewDevices +import app.k9mail.core.ui.compose.common.visibility.hide +import app.k9mail.core.ui.compose.designsystem.atom.DelayedCircularProgressIndicator +import app.k9mail.core.ui.compose.designsystem.atom.Surface +import app.k9mail.core.ui.compose.designsystem.atom.button.Button +import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonText +import app.k9mail.core.ui.compose.designsystem.atom.text.TextHeadline5 +import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer +import app.k9mail.core.ui.compose.designsystem.template.Scaffold +import app.k9mail.core.ui.compose.theme.IconsWithBottomRightOverlay +import app.k9mail.core.ui.compose.theme.K9Theme +import app.k9mail.core.ui.compose.theme.MainTheme +import app.k9mail.feature.account.common.ui.AppTitleTopHeader +import app.k9mail.feature.onboarding.permissions.R +import app.k9mail.feature.onboarding.permissions.ui.PermissionsContract.Event +import app.k9mail.feature.onboarding.permissions.ui.PermissionsContract.State +import app.k9mail.feature.onboarding.permissions.ui.PermissionsContract.UiPermissionState +import app.k9mail.feature.account.common.R as CommonR + +@Composable +internal fun PermissionsContent( + state: State, + onEvent: (Event) -> Unit, +) { + val scrollState = rememberScrollState() + + Scaffold( + bottomBar = { + BottomBar(state, onEvent, scrollState) + }, + ) { innerPadding -> + ResponsiveWidthContainer( + modifier = Modifier + .fillMaxWidth() + .padding(innerPadding), + ) { + Column( + verticalArrangement = Arrangement.SpaceBetween, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxHeight() + .verticalScroll(state = scrollState), + ) { + HeaderArea() + + ContentArea(state, onEvent) + + // This provides some bottom padding but is also necessary to make the vertical arrangement have the + // desired effect of putting ContentArea() in the middle. + Spacer(modifier = Modifier.height(MainTheme.spacings.double)) + } + } + } +} + +@Composable +private fun HeaderArea() { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + AppTitleTopHeader() + + TextHeadline5( + text = stringResource(R.string.onboarding_permissions_screen_title), + modifier = Modifier.padding(horizontal = MainTheme.spacings.double), + ) + + Spacer(modifier = Modifier.height(MainTheme.spacings.double)) + } +} + +@Composable +private fun ContentArea(state: State, onEvent: (Event) -> Unit) { + Column( + verticalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxHeight() + .padding(MainTheme.spacings.double), + ) { + if (state.isLoading) { + DelayedCircularProgressIndicator() + } else { + PermissionBoxes(state, onEvent) + } + } +} + +@Composable +private fun PermissionBoxes( + state: State, + onEvent: (Event) -> Unit, +) { + PermissionBox( + icon = IconsWithBottomRightOverlay.person, + permissionState = state.contactsPermissionState, + title = stringResource(R.string.onboarding_permissions_contacts_title), + description = stringResource(R.string.onboarding_permissions_contacts_description), + onAllowClick = { onEvent(Event.AllowContactsPermissionClicked) }, + ) + + if (state.isNotificationsPermissionVisible) { + Spacer(modifier = Modifier.height(MainTheme.spacings.quadruple)) + + PermissionBox( + icon = IconsWithBottomRightOverlay.notification, + permissionState = state.notificationsPermissionState, + title = stringResource(R.string.onboarding_permissions_notifications_title), + description = stringResource(R.string.onboarding_permissions_notifications_description), + onAllowClick = { onEvent(Event.AllowNotificationsPermissionClicked) }, + ) + } +} + +@Composable +private fun BottomBar( + state: State, + onEvent: (Event) -> Unit, + scrollState: ScrollState, +) { + // Elevate the bottom bar when some scrollable content is "underneath" it + val elevation by animateDpAsState( + targetValue = if (scrollState.canScrollForward) 8.dp else 0.dp, + label = "BottomBarElevation", + ) + + Surface(elevation = elevation) { + ResponsiveWidthContainer( + modifier = Modifier.fillMaxWidth(), + ) { + Row( + modifier = Modifier + .padding( + start = MainTheme.spacings.double, + end = MainTheme.spacings.double, + top = MainTheme.spacings.half, + bottom = MainTheme.spacings.half, + ) + .fillMaxWidth(), + horizontalArrangement = Arrangement.End, + ) { + Crossfade( + targetState = state.isNextButtonVisible, + label = "NextButton", + ) { isNextButtonVisible -> + Button( + text = stringResource(CommonR.string.account_common_button_next), + onClick = { onEvent(Event.NextClicked) }, + modifier = Modifier.hide(!isNextButtonVisible), + ) + + ButtonText( + text = stringResource(R.string.onboarding_permissions_skip_button), + onClick = { onEvent(Event.NextClicked) }, + modifier = Modifier.hide(isNextButtonVisible), + ) + } + } + } + } +} + +@PreviewDevices +@Composable +internal fun PermissionContentPreview() { + K9Theme { + PermissionsContent( + state = State( + isLoading = false, + contactsPermissionState = UiPermissionState.Granted, + notificationsPermissionState = UiPermissionState.Denied, + isNotificationsPermissionVisible = true, + isNextButtonVisible = false, + ), + onEvent = {}, + ) + } +} diff --git a/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsContract.kt b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsContract.kt new file mode 100644 index 000000000..51b84771c --- /dev/null +++ b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsContract.kt @@ -0,0 +1,40 @@ +package app.k9mail.feature.onboarding.permissions.ui + +import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel + +interface PermissionsContract { + interface ViewModel : UnidirectionalViewModel + + data class State( + val isLoading: Boolean = true, + val contactsPermissionState: UiPermissionState = UiPermissionState.Unknown, + val notificationsPermissionState: UiPermissionState = UiPermissionState.Unknown, + val isNotificationsPermissionVisible: Boolean = false, + val isNextButtonVisible: Boolean = false, + ) + + sealed interface Event { + data object LoadPermissionState : Event + + data object AllowContactsPermissionClicked : Event + data object AllowNotificationsPermissionClicked : Event + + data class ContactsPermissionResult(val success: Boolean) : Event + data class NotificationsPermissionResult(val success: Boolean) : Event + + data object NextClicked : Event + } + + sealed interface Effect { + data object RequestContactsPermission : Effect + data object RequestNotificationsPermission : Effect + + data object NavigateNext : Effect + } + + enum class UiPermissionState { + Unknown, + Granted, + Denied, + } +} diff --git a/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsScreen.kt b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsScreen.kt new file mode 100644 index 000000000..4b8423987 --- /dev/null +++ b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsScreen.kt @@ -0,0 +1,72 @@ +package app.k9mail.feature.onboarding.permissions.ui + +import android.Manifest +import android.os.Build +import androidx.activity.compose.ManagedActivityResultLauncher +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts.RequestPermission +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import app.k9mail.core.android.permissions.PermissionState +import app.k9mail.core.ui.compose.common.PreviewDevices +import app.k9mail.core.ui.compose.common.mvi.observe +import app.k9mail.core.ui.compose.theme.K9Theme +import app.k9mail.feature.onboarding.permissions.ui.PermissionsContract.Effect +import app.k9mail.feature.onboarding.permissions.ui.PermissionsContract.Event +import kotlinx.coroutines.Dispatchers +import org.koin.androidx.compose.koinViewModel + +@Composable +fun PermissionsScreen( + viewModel: PermissionsContract.ViewModel = koinViewModel(), + onNext: () -> Unit, +) { + val contactsPermissionLauncher = rememberLauncherForActivityResult(RequestPermission()) { success -> + viewModel.event(Event.ContactsPermissionResult(success)) + } + + val notificationsPermissionLauncher = rememberLauncherForActivityResult(RequestPermission()) { success -> + viewModel.event(Event.NotificationsPermissionResult(success)) + } + + val (state, dispatch) = viewModel.observe { effect -> + when (effect) { + Effect.RequestContactsPermission -> contactsPermissionLauncher.requestContactsPermission() + Effect.RequestNotificationsPermission -> notificationsPermissionLauncher.requestNotificationsPermission() + Effect.NavigateNext -> onNext() + } + } + + LaunchedEffect(key1 = Unit) { + dispatch(Event.LoadPermissionState) + } + + PermissionsContent( + state = state.value, + onEvent = dispatch, + ) +} + +private fun ManagedActivityResultLauncher.requestContactsPermission() { + launch(Manifest.permission.READ_CONTACTS) +} + +private fun ManagedActivityResultLauncher.requestNotificationsPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + launch(Manifest.permission.POST_NOTIFICATIONS) + } +} + +@PreviewDevices +@Composable +internal fun PermissionScreenPreview() { + K9Theme { + PermissionsScreen( + viewModel = PermissionsViewModel( + checkPermission = { PermissionState.Denied }, + backgroundDispatcher = Dispatchers.Main.immediate, + ), + onNext = {}, + ) + } +} diff --git a/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsViewModel.kt b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsViewModel.kt new file mode 100644 index 000000000..7803a127a --- /dev/null +++ b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionsViewModel.kt @@ -0,0 +1,108 @@ +package app.k9mail.feature.onboarding.permissions.ui + +import androidx.lifecycle.viewModelScope +import app.k9mail.core.android.permissions.Permission +import app.k9mail.core.android.permissions.PermissionState +import app.k9mail.core.ui.compose.common.mvi.BaseViewModel +import app.k9mail.feature.onboarding.permissions.domain.PermissionsDomainContract.UseCase +import app.k9mail.feature.onboarding.permissions.ui.PermissionsContract.Effect +import app.k9mail.feature.onboarding.permissions.ui.PermissionsContract.Event +import app.k9mail.feature.onboarding.permissions.ui.PermissionsContract.State +import app.k9mail.feature.onboarding.permissions.ui.PermissionsContract.UiPermissionState +import app.k9mail.feature.onboarding.permissions.ui.PermissionsContract.ViewModel +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class PermissionsViewModel( + private val checkPermission: UseCase.CheckPermission, + private val backgroundDispatcher: CoroutineDispatcher = Dispatchers.IO, +) : BaseViewModel(initialState = State(isLoading = true)), ViewModel { + + override fun event(event: Event) { + when (event) { + Event.LoadPermissionState -> handleOneTimeEvent(event, ::loadPermissionState) + Event.AllowContactsPermissionClicked -> handleAllowContactsPermissionClicked() + Event.AllowNotificationsPermissionClicked -> handleAllowNotificationsPermissionClicked() + is Event.ContactsPermissionResult -> handleContactsPermissionResult(event.success) + is Event.NotificationsPermissionResult -> handleNotificationsPermissionResult(event.success) + Event.NextClicked -> handleNextClicked() + } + } + + private fun loadPermissionState() { + viewModelScope.launch { + val (contactsPermissionState, notificationsPermissionState) = withContext(backgroundDispatcher) { + arrayOf( + checkPermission(Permission.Contacts), + checkPermission(Permission.Notifications), + ) + } + + val contactsUiPermissionState = when (contactsPermissionState) { + PermissionState.GrantedImplicitly -> error("Unexpected case") + PermissionState.Granted -> UiPermissionState.Granted + PermissionState.Denied -> UiPermissionState.Unknown + } + val notificationsUiPermissionState = when (notificationsPermissionState) { + PermissionState.GrantedImplicitly -> UiPermissionState.Unknown + PermissionState.Granted -> UiPermissionState.Granted + PermissionState.Denied -> UiPermissionState.Unknown + } + val isNotificationsPermissionVisible = notificationsPermissionState != PermissionState.GrantedImplicitly + + updateState { state -> + state.copy( + isLoading = false, + contactsPermissionState = contactsUiPermissionState, + notificationsPermissionState = notificationsUiPermissionState, + isNotificationsPermissionVisible = isNotificationsPermissionVisible, + ) + } + updateNextButtonState() + } + } + + private fun handleAllowContactsPermissionClicked() { + emitEffect(Effect.RequestContactsPermission) + } + + private fun handleAllowNotificationsPermissionClicked() { + emitEffect(Effect.RequestNotificationsPermission) + } + + private fun handleContactsPermissionResult(success: Boolean) { + updateState { state -> + state.copy( + contactsPermissionState = if (success) UiPermissionState.Granted else UiPermissionState.Denied, + ) + } + updateNextButtonState() + } + + private fun handleNotificationsPermissionResult(success: Boolean) { + updateState { state -> + state.copy( + notificationsPermissionState = if (success) UiPermissionState.Granted else UiPermissionState.Denied, + ) + } + updateNextButtonState() + } + + private fun updateNextButtonState() { + updateState { state -> + val isContactsPermissionGranted = state.contactsPermissionState == UiPermissionState.Granted + val isNotificationsPermissionGrantedOrHidden = !state.isNotificationsPermissionVisible || + state.notificationsPermissionState == UiPermissionState.Granted + + state.copy( + isNextButtonVisible = isContactsPermissionGranted && isNotificationsPermissionGrantedOrHidden, + ) + } + } + + private fun handleNextClicked() { + emitEffect(Effect.NavigateNext) + } +} diff --git a/feature/onboarding/permissions/src/main/res/values/strings.xml b/feature/onboarding/permissions/src/main/res/values/strings.xml new file mode 100644 index 000000000..096560516 --- /dev/null +++ b/feature/onboarding/permissions/src/main/res/values/strings.xml @@ -0,0 +1,17 @@ + + + + Improve the experience + + Contacts + + Allow access to be able to provide contact suggestions and to display contact names and photos. + + Notifications + + Turn on notifications so you don\'t miss any emails. + + Allow + + Skip + diff --git a/settings.gradle.kts b/settings.gradle.kts index 91cd737b2..1969a84ab 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -44,6 +44,7 @@ include( include( ":feature:onboarding:main", ":feature:onboarding:welcome", + ":feature:onboarding:permissions", ) include( @@ -68,6 +69,7 @@ include( ":core:featureflags", ":core:testing", ":core:android:common", + ":core:android:permissions", ":core:android:testing", ":core:ui:compose:common", ":core:ui:compose:designsystem", From fe33dd2cfa5fcdc4f4b36e01d4b531d7e2af360d Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 26 Oct 2023 22:38:49 +0200 Subject: [PATCH 3/3] Add permissions screen to onboarding flow --- .../k9mail/feature/preview/FeatureModule.kt | 5 ++ .../preview/navigation/FeatureNavHost.kt | 6 +- .../navigation/AccountSetupNavigation.kt | 14 +++++ .../launcher/di/FeatureLauncherModule.kt | 2 + .../navigation/FeatureLauncherNavHost.kt | 4 +- feature/onboarding/main/build.gradle.kts | 2 + .../onboarding/main/OnboardingModule.kt | 11 ++++ .../main/navigation/OnboardingNavHost.kt | 63 +++++++++++++++++++ .../main/navigation/OnboardingNavigation.kt | 11 ++-- 9 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/OnboardingModule.kt create mode 100644 feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavHost.kt diff --git a/app-feature-preview/src/main/java/app/k9mail/feature/preview/FeatureModule.kt b/app-feature-preview/src/main/java/app/k9mail/feature/preview/FeatureModule.kt index aa80e7390..a5b1186ff 100644 --- a/app-feature-preview/src/main/java/app/k9mail/feature/preview/FeatureModule.kt +++ b/app-feature-preview/src/main/java/app/k9mail/feature/preview/FeatureModule.kt @@ -6,6 +6,7 @@ import app.k9mail.feature.account.edit.AccountEditExternalContract import app.k9mail.feature.account.edit.featureAccountEditModule import app.k9mail.feature.account.setup.AccountSetupExternalContract import app.k9mail.feature.account.setup.featureAccountSetupModule +import app.k9mail.feature.onboarding.main.featureOnboardingModule import app.k9mail.feature.preview.account.AccountOwnerNameProvider import app.k9mail.feature.preview.account.InMemoryAccountStore import app.k9mail.feature.preview.auth.AndroidKeyStoreDirectoryProvider @@ -18,6 +19,7 @@ import com.fsck.k9.mail.ssl.LocalKeyStore import com.fsck.k9.mail.ssl.TrustManagerFactory import com.fsck.k9.mail.ssl.TrustedSocketFactory import org.koin.core.module.Module +import org.koin.core.qualifier.named import org.koin.dsl.binds import org.koin.dsl.module @@ -41,9 +43,12 @@ val featureModule: Module = module { single { TrustManagerFactory.createInstance(get()) } single { DefaultTrustedSocketFactory(get(), get()) } single { RealOAuth2TokenProviderFactory(context = get()) } + single(named("ClientIdAppName")) { "App Name" } + single(named("ClientIdAppVersion")) { "App Version" } includes( accountModule, + featureOnboardingModule, featureAccountSetupModule, featureAccountEditModule, ) diff --git a/app-feature-preview/src/main/java/app/k9mail/feature/preview/navigation/FeatureNavHost.kt b/app-feature-preview/src/main/java/app/k9mail/feature/preview/navigation/FeatureNavHost.kt index 94b31fc51..20e0e7018 100644 --- a/app-feature-preview/src/main/java/app/k9mail/feature/preview/navigation/FeatureNavHost.kt +++ b/app-feature-preview/src/main/java/app/k9mail/feature/preview/navigation/FeatureNavHost.kt @@ -7,7 +7,6 @@ import androidx.navigation.compose.NavHost import app.k9mail.feature.account.edit.navigation.accountEditRoute import app.k9mail.feature.account.edit.navigation.navigateToAccountEditIncomingServerSettings import app.k9mail.feature.account.setup.navigation.accountSetupRoute -import app.k9mail.feature.account.setup.navigation.navigateToAccountSetup import app.k9mail.feature.onboarding.main.navigation.NAVIGATION_ROUTE_ONBOARDING import app.k9mail.feature.onboarding.main.navigation.navigateToOnboarding import app.k9mail.feature.onboarding.main.navigation.onboardingRoute @@ -24,8 +23,11 @@ fun FeatureNavHost( modifier = modifier, ) { onboardingRoute( - onStart = { navController.navigateToAccountSetup() }, onImport = { /* TODO */ }, + onBack = navController::popBackStack, + onFinish = { accountUuid -> + navController.navigateToAccountEditIncomingServerSettings(accountUuid) + }, ) accountSetupRoute( onBack = navController::popBackStack, diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavigation.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavigation.kt index 5d4eb2ce8..740835675 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavigation.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavigation.kt @@ -3,6 +3,7 @@ package app.k9mail.feature.account.setup.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions +import androidx.navigation.compose.composable import app.k9mail.core.ui.compose.common.navigation.deepLinkComposable import app.k9mail.feature.account.setup.ui.AccountSetupScreen @@ -23,3 +24,16 @@ fun NavGraphBuilder.accountSetupRoute( ) } } + +fun NavGraphBuilder.nestedAccountSetupRoute( + route: String, + onBack: () -> Unit, + onFinish: (String) -> Unit, +) { + composable(route) { + AccountSetupScreen( + onBack = onBack, + onFinish = onFinish, + ) + } +} diff --git a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/di/FeatureLauncherModule.kt b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/di/FeatureLauncherModule.kt index 1e5a5e473..ddab81965 100644 --- a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/di/FeatureLauncherModule.kt +++ b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/di/FeatureLauncherModule.kt @@ -2,10 +2,12 @@ package app.k9mail.feature.launcher.di import app.k9mail.feature.account.edit.featureAccountEditModule import app.k9mail.feature.account.setup.featureAccountSetupModule +import app.k9mail.feature.onboarding.main.featureOnboardingModule import org.koin.dsl.module val featureLauncherModule = module { includes( + featureOnboardingModule, featureAccountSetupModule, featureAccountEditModule, ) diff --git a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/navigation/FeatureLauncherNavHost.kt b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/navigation/FeatureLauncherNavHost.kt index b9a7d69ce..0ca3213db 100644 --- a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/navigation/FeatureLauncherNavHost.kt +++ b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/navigation/FeatureLauncherNavHost.kt @@ -7,7 +7,6 @@ import androidx.navigation.compose.NavHost import app.k9mail.core.ui.compose.common.activity.LocalActivity import app.k9mail.feature.account.edit.navigation.accountEditRoute import app.k9mail.feature.account.setup.navigation.accountSetupRoute -import app.k9mail.feature.account.setup.navigation.navigateToAccountSetup import app.k9mail.feature.launcher.FeatureLauncherExternalContract.AccountSetupFinishedLauncher import app.k9mail.feature.launcher.FeatureLauncherExternalContract.ImportSettingsLauncher import app.k9mail.feature.onboarding.main.navigation.NAVIGATION_ROUTE_ONBOARDING @@ -30,8 +29,9 @@ fun FeatureLauncherNavHost( modifier = modifier, ) { onboardingRoute( - onStart = { navController.navigateToAccountSetup() }, onImport = { importSettingsLauncher.launch() }, + onBack = onBack, + onFinish = { accountUuid -> accountSetupFinishedLauncher.launch(accountUuid) }, ) accountSetupRoute( onBack = onBack, diff --git a/feature/onboarding/main/build.gradle.kts b/feature/onboarding/main/build.gradle.kts index 1b82fa832..9b8070137 100644 --- a/feature/onboarding/main/build.gradle.kts +++ b/feature/onboarding/main/build.gradle.kts @@ -19,4 +19,6 @@ android { dependencies { implementation(projects.core.ui.compose.designsystem) implementation(projects.feature.onboarding.welcome) + implementation(projects.feature.account.setup) + implementation(projects.feature.onboarding.permissions) } diff --git a/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/OnboardingModule.kt b/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/OnboardingModule.kt new file mode 100644 index 000000000..fc02cc333 --- /dev/null +++ b/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/OnboardingModule.kt @@ -0,0 +1,11 @@ +package app.k9mail.feature.onboarding.main + +import app.k9mail.feature.onboarding.permissions.featureOnboardingPermissionsModule +import org.koin.core.module.Module +import org.koin.dsl.module + +val featureOnboardingModule: Module = module { + includes( + featureOnboardingPermissionsModule, + ) +} 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 new file mode 100644 index 000000000..3d49d1da3 --- /dev/null +++ b/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavHost.kt @@ -0,0 +1,63 @@ +package app.k9mail.feature.onboarding.main.navigation + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.navigation.NavController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import app.k9mail.feature.account.setup.navigation.nestedAccountSetupRoute +import app.k9mail.feature.onboarding.permissions.ui.PermissionsScreen +import app.k9mail.feature.onboarding.welcome.ui.OnboardingScreen + +private const val NESTED_NAVIGATION_ROUTE_WELCOME = "welcome" +private const val NESTED_NAVIGATION_ROUTE_ACCOUNT_SETUP = "account_setup" +private const val NESTED_NAVIGATION_ROUTE_PERMISSIONS = "permissions" + +private fun NavController.navigateToAccountSetup() { + navigate(NESTED_NAVIGATION_ROUTE_ACCOUNT_SETUP) +} + +private fun NavController.navigateToPermissions() { + navigate(NESTED_NAVIGATION_ROUTE_PERMISSIONS) +} + +@Composable +fun OnboardingNavHost( + onImport: () -> Unit, + onBack: () -> Unit, + onFinish: (String) -> Unit, +) { + val navController = rememberNavController() + var accountUuid by rememberSaveable { mutableStateOf(null) } + + NavHost( + navController = navController, + startDestination = NESTED_NAVIGATION_ROUTE_WELCOME, + ) { + composable(route = NESTED_NAVIGATION_ROUTE_WELCOME) { + OnboardingScreen( + onStartClick = { navController.navigateToAccountSetup() }, + onImportClick = onImport, + ) + } + + nestedAccountSetupRoute( + route = NESTED_NAVIGATION_ROUTE_ACCOUNT_SETUP, + onBack = onBack, + onFinish = { createdAccountUuid -> + accountUuid = createdAccountUuid + navController.navigateToPermissions() + }, + ) + + composable(route = NESTED_NAVIGATION_ROUTE_PERMISSIONS) { + PermissionsScreen( + onNext = { onFinish(requireNotNull(accountUuid)) }, + ) + } + } +} diff --git a/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavigation.kt b/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavigation.kt index aa0082c5e..16cd0b3e7 100644 --- a/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavigation.kt +++ b/feature/onboarding/main/src/main/kotlin/app/k9mail/feature/onboarding/main/navigation/OnboardingNavigation.kt @@ -4,7 +4,6 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import app.k9mail.core.ui.compose.common.navigation.deepLinkComposable -import app.k9mail.feature.onboarding.welcome.ui.OnboardingScreen const val NAVIGATION_ROUTE_ONBOARDING = "onboarding" @@ -15,13 +14,15 @@ fun NavController.navigateToOnboarding( } fun NavGraphBuilder.onboardingRoute( - onStart: () -> Unit, onImport: () -> Unit, + onBack: () -> Unit, + onFinish: (String) -> Unit, ) { deepLinkComposable(route = NAVIGATION_ROUTE_ONBOARDING) { - OnboardingScreen( - onStartClick = onStart, - onImportClick = onImport, + OnboardingNavHost( + onImport = onImport, + onBack = onBack, + onFinish = onFinish, ) } }