diff --git a/app/src/main/kotlin/com/infomaniak/auth/data/preferences/PermissionPreferences.kt b/app/src/main/kotlin/com/infomaniak/auth/data/preferences/PermissionPreferences.kt
new file mode 100644
index 00000000..09cf5a65
--- /dev/null
+++ b/app/src/main/kotlin/com/infomaniak/auth/data/preferences/PermissionPreferences.kt
@@ -0,0 +1,34 @@
+/*
+ * Infomaniak Authenticator - Android
+ * Copyright (C) 2026 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+@file:OptIn(ExperimentalSplittiesApi::class)
+
+package com.infomaniak.auth.data.preferences
+
+import kotlinx.coroutines.flow.Flow
+import splitties.experimental.ExperimentalSplittiesApi
+import splitties.preferences.Preferences
+import splitties.preferences.SuspendPrefsAccessor
+
+class PermissionPreferences private constructor(): Preferences(name = "PermissionPreferences") {
+ companion object : SuspendPrefsAccessor(::PermissionPreferences)
+
+ val hasTriggeredNotificationPermissionFlow : Flow
+ var hasTriggeredNotificationPermission by boolPref(key = "HasTriggeredNotificationPermission", defaultValue = false).also {
+ hasTriggeredNotificationPermissionFlow = it.valueFlow()
+ }
+}
diff --git a/app/src/main/kotlin/com/infomaniak/auth/ui/screen/main/MainScreen.kt b/app/src/main/kotlin/com/infomaniak/auth/ui/screen/main/MainScreen.kt
index 1dc76079..c8f51a66 100644
--- a/app/src/main/kotlin/com/infomaniak/auth/ui/screen/main/MainScreen.kt
+++ b/app/src/main/kotlin/com/infomaniak/auth/ui/screen/main/MainScreen.kt
@@ -24,10 +24,8 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
import androidx.navigation3.runtime.NavBackStack
import androidx.navigation3.runtime.NavEntryDecorator
@@ -68,26 +66,21 @@ fun MainScreen(
val notificationPermissionState: PermissionState? = if (SDK_INT >= 33) {
rememberPermissionState(permission = Manifest.permission.POST_NOTIFICATIONS)
} else null
-
- var notificationPermissionScreenShown by rememberSaveable { mutableStateOf(false) }
+ val hasTriggeredNotificationPermission by viewModel.hasTriggeredNotificationPermission.collectAsStateWithLifecycle()
LaunchedEffect(viewModel.appStatus) {
viewModel.appStatus.collect {
val permissionStatus = notificationPermissionState?.status
- val isPermanentlyDenied = (permissionStatus as? PermissionStatus.Denied)?.shouldShowRationale == false
- val isPermissionNotRequired = with(permissionStatus) {
- (this == null || this == PermissionStatus.Granted || !isPermanentlyDenied)
- }
- val skipPermission = notificationPermissionScreenShown && isPermissionNotRequired
+ val shouldShowRationale = (permissionStatus as? PermissionStatus.Denied)?.shouldShowRationale == true
+ val showNotificationPermissionScreen = notificationPermissionState != null &&
+ permissionStatus != PermissionStatus.Granted &&
+ (!hasTriggeredNotificationPermission || shouldShowRationale)
handleAppStatus(
appStatus = it,
currentDestination = currentDestination,
backStack = backStack,
- skipPermission = skipPermission,
- onPermissionAsked = {
- notificationPermissionScreenShown = true
- }
+ showNotificationPermissionScreen = showNotificationPermissionScreen,
)
}
}
@@ -101,8 +94,7 @@ private fun handleAppStatus(
appStatus: AppStatus,
currentDestination: NavKey,
backStack: NavBackStack,
- skipPermission: Boolean,
- onPermissionAsked: () -> Unit,
+ showNotificationPermissionScreen: Boolean,
) {
val targetDestination = when (appStatus) {
is AppStatus.LoginRequired.NotMigrating -> NavDestination.Onboarding.Start
@@ -111,11 +103,10 @@ private fun handleAppStatus(
is AppStatus.LoggingIn -> NavDestination.SecuringAccount
is AppStatus.EverythingReady -> NavDestination.Onboarding.Complete
is AppStatus.SetupComplete -> {
- if (skipPermission) {
- NavDestination.Home
- } else {
- onPermissionAsked()
+ if (showNotificationPermissionScreen) {
NavDestination.Permission.Notification
+ } else {
+ NavDestination.Home
}
}
is AppStatus.AddingAnAccount -> NavDestination.Onboarding.Start
diff --git a/app/src/main/kotlin/com/infomaniak/auth/ui/screen/main/MainViewModel.kt b/app/src/main/kotlin/com/infomaniak/auth/ui/screen/main/MainViewModel.kt
index 501f0496..d490a806 100644
--- a/app/src/main/kotlin/com/infomaniak/auth/ui/screen/main/MainViewModel.kt
+++ b/app/src/main/kotlin/com/infomaniak/auth/ui/screen/main/MainViewModel.kt
@@ -18,10 +18,18 @@
package com.infomaniak.auth.ui.screen.main
import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.infomaniak.auth.data.preferences.PermissionPreferences
import com.infomaniak.auth.lib.AuthenticatorFacade
import com.infomaniak.auth.lib.repository.AppSettingsRepository
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.emitAll
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
@@ -32,4 +40,13 @@ class MainViewModel @Inject constructor(
val appStatus = authenticatorFacade.appStatus
val isAppLocked = appSettingsRepository.getSettings().mapNotNull { it?.isAppLockEnabled }
+ val hasTriggeredNotificationPermission: StateFlow = flow {
+ emitAll(PermissionPreferences().hasTriggeredNotificationPermissionFlow)
+ }.stateIn(viewModelScope, SharingStarted.Lazily, false)
+
+ fun onNotificationPermissionTriggered() {
+ viewModelScope.launch {
+ PermissionPreferences().hasTriggeredNotificationPermission = true
+ }
+ }
}
diff --git a/app/src/main/kotlin/com/infomaniak/auth/ui/screen/permission/NotificationPermissionScreen.kt b/app/src/main/kotlin/com/infomaniak/auth/ui/screen/permission/NotificationPermissionScreen.kt
index 3c8f15a6..10a19fce 100644
--- a/app/src/main/kotlin/com/infomaniak/auth/ui/screen/permission/NotificationPermissionScreen.kt
+++ b/app/src/main/kotlin/com/infomaniak/auth/ui/screen/permission/NotificationPermissionScreen.kt
@@ -18,7 +18,6 @@
package com.infomaniak.auth.ui.screen.permission
import android.Manifest
-import android.annotation.SuppressLint
import android.os.Build.VERSION.SDK_INT
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -35,6 +34,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
+import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import com.google.accompanist.permissions.PermissionStatus
@@ -48,31 +49,52 @@ import com.infomaniak.auth.ui.components.LargeButton
import com.infomaniak.auth.ui.components.TitleAndDescription
import com.infomaniak.auth.ui.images.AppImages
import com.infomaniak.auth.ui.images.illus.bannerNotification.BannerNotification
+import com.infomaniak.auth.ui.screen.main.MainViewModel
import com.infomaniak.auth.ui.theme.AuthenticatorTheme
import com.infomaniak.core.ui.compose.bottomstickybuttonscaffolds.BottomStickyButtonScaffold
import com.infomaniak.core.ui.compose.margin.Margin
import com.infomaniak.core.ui.compose.preview.PreviewSmallWindow
@OptIn(ExperimentalPermissionsApi::class)
-@SuppressLint("ComposeModifierMissing")
@Composable
fun NotificationPermissionScreen(
- navigateToHome: () -> Unit
+ navigateToHome: () -> Unit,
+ viewModel: MainViewModel = hiltViewModel()
) {
- var permissionAsked by remember { mutableStateOf(false) }
+ val hasTriggeredNotificationPermission by viewModel.hasTriggeredNotificationPermission.collectAsStateWithLifecycle()
+
val notificationPermissionState: PermissionState? = if (SDK_INT >= 33) {
rememberPermissionState(permission = Manifest.permission.POST_NOTIFICATIONS)
} else null
+ var permissionAsked by remember { mutableStateOf(false) }
+
LaunchedEffect(notificationPermissionState?.status) {
if (notificationPermissionState == null ||
notificationPermissionState.status == PermissionStatus.Granted ||
- notificationPermissionState.status == PermissionStatus.Denied(false) ||
notificationPermissionState.status is PermissionStatus.Denied && permissionAsked) {
+ if (!hasTriggeredNotificationPermission) {
+ viewModel.onNotificationPermissionTriggered()
+ }
navigateToHome()
}
}
+ NotificationPermissionScreen(
+ navigateToHome = navigateToHome,
+ onPermissionAsked = {
+ notificationPermissionState?.launchPermissionRequest()
+ permissionAsked = true
+ },
+ )
+}
+
+@OptIn(ExperimentalPermissionsApi::class)
+@Composable
+private fun NotificationPermissionScreen(
+ navigateToHome: () -> Unit,
+ onPermissionAsked: () -> Unit,
+) {
BottomStickyButtonScaffold(
topBar = {
InfomaniakAuthenticatorTopAppBar()
@@ -82,10 +104,7 @@ fun NotificationPermissionScreen(
LargeButton(
modifier = Modifier.fillMaxWidth(),
title = stringResource(R.string.onboardingNotificationsAuthorisationButton),
- onClick = {
- notificationPermissionState?.launchPermissionRequest()
- permissionAsked = true
- }
+ onClick = onPermissionAsked
)
LargeButton(
modifier = Modifier.fillMaxWidth(),
@@ -120,6 +139,7 @@ private fun NotificationPermissionScreenPreview() {
AuthenticatorTheme {
NotificationPermissionScreen(
navigateToHome = {},
+ onPermissionAsked = {},
)
}
}