diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/MainActivity.kt index 177e184ca..41a7cf229 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/MainActivity.kt @@ -21,7 +21,16 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.infomaniak.multiplatform_swisstransfer.common.models.Theme import com.infomaniak.swisstransfer.ui.screen.main.MainScreen +import com.infomaniak.swisstransfer.ui.screen.main.settings.SettingsViewModel import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme import dagger.hilt.android.AndroidEntryPoint @@ -32,9 +41,19 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { - SwissTransferTheme { + SwissTransferTheme(isDarkTheme = isDarkTheme()) { MainScreen() } } } } + +@Composable +fun isDarkTheme(): Boolean { + val settingsViewModel = hiltViewModel() + val appSettings by settingsViewModel.appSettingsFlow.collectAsStateWithLifecycle(null) + + return appSettings?.let { + if (it.theme == Theme.SYSTEM) isSystemInDarkTheme() else it.theme == Theme.DARK + } ?: isSystemInDarkTheme() +} diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreen.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreen.kt index 483fb0e5f..abf3f931e 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreen.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreen.kt @@ -30,7 +30,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource -import com.infomaniak.multiplatform_swisstransfer.common.interfaces.appSettings.AppSettings import com.infomaniak.multiplatform_swisstransfer.common.models.DownloadLimit import com.infomaniak.multiplatform_swisstransfer.common.models.EmailLanguage import com.infomaniak.multiplatform_swisstransfer.common.models.Theme @@ -52,7 +51,10 @@ import com.infomaniak.swisstransfer.ui.utils.PreviewMobile @Composable fun SettingsScreen( - appSettings: AppSettings, + theme: GetSetCallbacks, + validityPeriod: GetSetCallbacks, + downloadLimit: GetSetCallbacks, + emailLanguage: GetSetCallbacks, onItemClick: (SettingsOptionScreens) -> Unit, getSelectedSetting: () -> SettingsOptionScreens?, ) { @@ -76,7 +78,7 @@ fun SettingsScreen( titleRes = R.string.settingsOptionTheme, isSelected = { selectedSetting == THEME }, icon = AppIcons.PaintbrushPalette, - description = appSettings.theme.getString(), + description = theme.get().getString(), endIcon = CHEVRON, onClick = { onItemClick(THEME) }, ) @@ -95,7 +97,7 @@ fun SettingsScreen( titleRes = R.string.settingsOptionValidityPeriod, isSelected = { selectedSetting == VALIDITY_PERIOD }, icon = AppIcons.ArrowDownFile, - description = appSettings.validityPeriod.getString(), + description = validityPeriod.get().getString(), endIcon = CHEVRON, onClick = { onItemClick(VALIDITY_PERIOD) }, ) @@ -103,7 +105,7 @@ fun SettingsScreen( titleRes = R.string.settingsOptionDownloadLimit, isSelected = { selectedSetting == DOWNLOAD_LIMIT }, icon = AppIcons.Clock, - description = appSettings.downloadLimit.getString(), + description = downloadLimit.get().getString(), endIcon = CHEVRON, onClick = { onItemClick(DOWNLOAD_LIMIT) }, ) @@ -111,7 +113,7 @@ fun SettingsScreen( titleRes = R.string.settingsOptionEmailLanguage, isSelected = { selectedSetting == EMAIL_LANGUAGE }, icon = AppIcons.SpeechBubble, - description = appSettings.emailLanguage.getString(), + description = emailLanguage.get().getString(), endIcon = CHEVRON, onClick = { onItemClick(EMAIL_LANGUAGE) }, ) @@ -184,19 +186,19 @@ enum class SettingsOptionScreens { DISCOVER_INFOMANIAK, SHARE_IDEAS, GIVE_FEEDBACK, } -private class DummyAppSettings( - override var theme: Theme = Theme.SYSTEM, - override var downloadLimit: DownloadLimit = DownloadLimit.TWOHUNDREDFIFTY, - override var emailLanguage: EmailLanguage = EmailLanguage.FRENCH, - override var validityPeriod: ValidityPeriod = ValidityPeriod.THIRTY, -) : AppSettings - @PreviewMobile @Composable private fun SettingsScreenPreview() { SwissTransferTheme { Surface(color = MaterialTheme.colorScheme.background) { - SettingsScreen(appSettings = DummyAppSettings(), onItemClick = {}, getSelectedSetting = { null }) + SettingsScreen( + theme = GetSetCallbacks(get = { Theme.SYSTEM }, set = {}), + validityPeriod = GetSetCallbacks(get = { ValidityPeriod.THIRTY }, set = {}), + downloadLimit = GetSetCallbacks(get = { DownloadLimit.TWOHUNDREDFIFTY }, set = {}), + emailLanguage = GetSetCallbacks(get = { EmailLanguage.ENGLISH }, set = {}), + onItemClick = {}, + getSelectedSetting = { null }, + ) } } } diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreenWrapper.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreenWrapper.kt index 432ef5234..992d3489d 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreenWrapper.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/SettingsScreenWrapper.kt @@ -27,49 +27,84 @@ import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.infomaniak.multiplatform_swisstransfer.common.interfaces.appSettings.AppSettings +import com.infomaniak.multiplatform_swisstransfer.common.models.DownloadLimit +import com.infomaniak.multiplatform_swisstransfer.common.models.EmailLanguage +import com.infomaniak.multiplatform_swisstransfer.common.models.Theme +import com.infomaniak.multiplatform_swisstransfer.common.models.ValidityPeriod import com.infomaniak.swisstransfer.R import com.infomaniak.swisstransfer.ui.components.TwoPaneScaffold import com.infomaniak.swisstransfer.ui.screen.main.settings.SettingsOptionScreens.* import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme import com.infomaniak.swisstransfer.ui.utils.* -@OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun SettingsScreenWrapper( windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(), settingsViewModel: SettingsViewModel = hiltViewModel(), ) { val appSettings by settingsViewModel.appSettingsFlow.collectAsStateWithLifecycle(null) + appSettings?.let { safeAppSettings -> - TwoPaneScaffold( - windowAdaptiveInfo = windowAdaptiveInfo, - listPane = { ListPane(this, safeAppSettings) }, - detailPane = { DetailPane(safeAppSettings, settingsViewModel, navigator = this) }, - ) + val theme = GetSetCallbacks(get = { safeAppSettings.theme }, set = { settingsViewModel.setTheme(it) }) + val validityPeriod = + GetSetCallbacks(get = { safeAppSettings.validityPeriod }, set = { settingsViewModel.setValidityPeriod(it) }) + val downloadLimit = + GetSetCallbacks(get = { safeAppSettings.downloadLimit }, set = { settingsViewModel.setDownloadLimit(it) }) + val emailLanguage = + GetSetCallbacks(get = { safeAppSettings.emailLanguage }, set = { settingsViewModel.setEmailLanguage(it) }) + + SettingsScreenWrapper(windowAdaptiveInfo, theme, validityPeriod, downloadLimit, emailLanguage) } } @OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable -private fun ListPane(navigator: ThreePaneScaffoldNavigator, appSettings: AppSettings) { +fun SettingsScreenWrapper( + windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(), + theme: GetSetCallbacks, + validityPeriod: GetSetCallbacks, + downloadLimit: GetSetCallbacks, + emailLanguage: GetSetCallbacks, +) { + TwoPaneScaffold( + windowAdaptiveInfo = windowAdaptiveInfo, + listPane = { ListPane(navigator = this, theme, validityPeriod, downloadLimit, emailLanguage) }, + detailPane = { DetailPane(navigator = this, theme, validityPeriod, downloadLimit, emailLanguage) }, + ) +} + +@Immutable +class GetSetCallbacks( + val get: () -> T, + val set: (T) -> Unit, +) + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +@Composable +private fun ListPane( + navigator: ThreePaneScaffoldNavigator, + theme: GetSetCallbacks, + validityPeriod: GetSetCallbacks, + downloadLimit: GetSetCallbacks, + emailLanguage: GetSetCallbacks, +) { val context = LocalContext.current val aboutURL = stringResource(R.string.urlAbout) val userReportURL = stringResource(R.string.urlUserReportAndroid) SettingsScreen( - appSettings, + theme, + validityPeriod, + downloadLimit, + emailLanguage, onItemClick = { item -> when (item) { NOTIFICATIONS -> context.openAppNotificationSettings() @@ -89,9 +124,11 @@ private fun ListPane(navigator: ThreePaneScaffoldNavigator, + theme: GetSetCallbacks, + validityPeriod: GetSetCallbacks, + downloadLimit: GetSetCallbacks, + emailLanguage: GetSetCallbacks, ) { var lastSelectedScreen by rememberSaveable { mutableStateOf(null) } @@ -103,24 +140,24 @@ private fun DetailPane( when (destination) { THEME -> SettingsThemeScreen( - theme = appSettings.theme, + theme = theme.get(), navigateBack = navigateBack, - onThemeUpdate = settingsViewModel::setTheme, + onThemeUpdate = { theme.set(it) }, ) VALIDITY_PERIOD -> SettingsValidityPeriodScreen( - validityPeriod = appSettings.validityPeriod, + validityPeriod = validityPeriod.get(), navigateBack = navigateBack, - onValidityPeriodChange = settingsViewModel::setValidityPeriod, + onValidityPeriodChange = { validityPeriod.set(it) }, ) DOWNLOAD_LIMIT -> SettingsDownloadsLimitScreen( - downloadLimit = appSettings.downloadLimit, + downloadLimit = downloadLimit.get(), navigateBack = navigateBack, - onDownloadLimitChange = settingsViewModel::setDownloadLimit, + onDownloadLimitChange = { downloadLimit.set(it) }, ) EMAIL_LANGUAGE -> SettingsEmailLanguageScreen( - emailLanguage = appSettings.emailLanguage, + emailLanguage = emailLanguage.get(), navigateBack = navigateBack, - onEmailLanguageChange = settingsViewModel::setEmailLanguage, + onEmailLanguageChange = { emailLanguage.set(it) }, ) NOTIFICATIONS, DISCOVER_INFOMANIAK, @@ -144,7 +181,12 @@ private fun NoSelectionEmptyState() { private fun SettingsScreenWrapperPreview() { SwissTransferTheme { Surface(color = MaterialTheme.colorScheme.background) { - SettingsScreenWrapper() + SettingsScreenWrapper( + theme = GetSetCallbacks(get = { Theme.SYSTEM }, set = {}), + validityPeriod = GetSetCallbacks(get = { ValidityPeriod.THIRTY }, set = {}), + downloadLimit = GetSetCallbacks(get = { DownloadLimit.TWOHUNDREDFIFTY }, set = {}), + emailLanguage = GetSetCallbacks(get = { EmailLanguage.ENGLISH }, set = {}), + ) } } } diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt index 5a60c8adb..c688ec874 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/theme/Theme.kt @@ -23,42 +23,29 @@ import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.* import androidx.compose.ui.graphics.Color -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.infomaniak.multiplatform_swisstransfer.common.models.Theme -import com.infomaniak.swisstransfer.ui.screen.main.settings.SettingsViewModel val LocalCustomTypography = staticCompositionLocalOf { Typography } val LocalCustomColorScheme: ProvidableCompositionLocal = staticCompositionLocalOf { CustomColorScheme() } @Composable fun SwissTransferTheme( - settingsViewModel: SettingsViewModel = hiltViewModel(), - darkTheme: Boolean = isDarkTheme(settingsViewModel), + isDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit, ) { - val customColors = if (darkTheme) CustomDarkColorScheme else CustomLightColorScheme + val customColors = if (isDarkTheme) CustomDarkColorScheme else CustomLightColorScheme CompositionLocalProvider( LocalCustomTypography provides Typography, LocalTextStyle provides Typography.bodyRegular, LocalCustomColorScheme provides customColors, ) { MaterialTheme( - colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme, + colorScheme = if (isDarkTheme) DarkColorScheme else LightColorScheme, shapes = Shapes, content = content, ) } } -@Composable -fun isDarkTheme(settingsViewModel: SettingsViewModel): Boolean { - val appSettings by settingsViewModel.appSettingsFlow.collectAsStateWithLifecycle(null) - return appSettings?.let { - if (it.theme == Theme.SYSTEM) isSystemInDarkTheme() else it.theme == Theme.DARK - } ?: isSystemInDarkTheme() -} - object SwissTransferTheme { val typography: CustomTypography @Composable