diff --git a/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt index 87d396ba5..e48fbe9f3 100644 --- a/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt +++ b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt @@ -21,8 +21,6 @@ import androidx.activity.ComponentActivity import androidx.activity.SystemBarStyle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue @@ -42,8 +40,6 @@ import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourc import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme -import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig -import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone import com.google.samples.apps.nowinandroid.ui.NiaApp import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState @@ -159,47 +155,6 @@ class MainActivity : ComponentActivity() { } } -/** - * Returns `true` if the Android theme should be used, as a function of the [uiState]. - */ -@Composable -private fun shouldUseAndroidTheme( - uiState: MainScreenUiState, -): Boolean = when (uiState) { - Loading -> false - is Success -> when (uiState.userData.themeBrand) { - ThemeBrand.DEFAULT -> false - ThemeBrand.ANDROID -> true - } -} - -/** - * Returns `true` if the dynamic color is disabled, as a function of the [uiState]. - */ -@Composable -private fun shouldDisableDynamicTheming( - uiState: MainScreenUiState, -): Boolean = when (uiState) { - Loading -> false - is Success -> !uiState.userData.useDynamicColor -} - -/** - * Returns `true` if dark theme should be used, as a function of the [uiState] and the - * current system context. - */ -@Composable -private fun shouldUseDarkTheme( - uiState: MainScreenUiState, -): Boolean = when (uiState) { - Loading -> isSystemInDarkTheme() - is Success -> when (uiState.userData.darkThemeConfig) { - DarkThemeConfig.FOLLOW_SYSTEM -> isSystemInDarkTheme() - DarkThemeConfig.LIGHT -> false - DarkThemeConfig.DARK -> true - } -} - /** * The default light scrim, as defined by androidx and the platform: * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt;l=35-38;drc=27e7d52e8604a080133e8b842db10c89b4482598 diff --git a/app/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/App.kt b/app/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/App.kt new file mode 100644 index 000000000..926dd35d2 --- /dev/null +++ b/app/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/App.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable +import com.google.samples.apps.nowinandroid.MainScreenUiState.Loading +import com.google.samples.apps.nowinandroid.MainScreenUiState.Success +import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig +import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand + +/** + * Returns `true` if the Android theme should be used, as a function of the [uiState]. + */ +@Composable +fun shouldUseAndroidTheme( + uiState: MainScreenUiState, +): Boolean = when (uiState) { + Loading -> false + is Success -> when (uiState.userData.themeBrand) { + ThemeBrand.DEFAULT -> false + ThemeBrand.ANDROID -> true + } +} + +/** + * Returns `true` if the dynamic color is disabled, as a function of the [uiState]. + */ +@Composable +fun shouldDisableDynamicTheming( + uiState: MainScreenUiState, +): Boolean = when (uiState) { + Loading -> false + is Success -> !uiState.userData.useDynamicColor +} + +/** + * Returns `true` if dark theme should be used, as a function of the [uiState] and the + * current system context. + */ +@Composable +fun shouldUseDarkTheme( + uiState: MainScreenUiState, +): Boolean = when (uiState) { + Loading -> isSystemInDarkTheme() + is Success -> when (uiState.userData.darkThemeConfig) { + DarkThemeConfig.FOLLOW_SYSTEM -> isSystemInDarkTheme() + DarkThemeConfig.LIGHT -> false + DarkThemeConfig.DARK -> true + } +} diff --git a/app/src/jvmMain/kotlin/Main.kt b/app/src/jvmMain/kotlin/Main.kt new file mode 100644 index 000000000..bf16e0c18 --- /dev/null +++ b/app/src/jvmMain/kotlin/Main.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import com.google.samples.apps.nowinandroid.MainScreenUiState +import com.google.samples.apps.nowinandroid.MainScreenViewModel +import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper +import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper +import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository +import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor +import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone +import com.google.samples.apps.nowinandroid.core.ui.collectAsStateWithLifecycle +import com.google.samples.apps.nowinandroid.di.appModules +import com.google.samples.apps.nowinandroid.shouldDisableDynamicTheming +import com.google.samples.apps.nowinandroid.shouldUseAndroidTheme +import com.google.samples.apps.nowinandroid.shouldUseDarkTheme +import com.google.samples.apps.nowinandroid.ui.NiaApp +import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState +import nowinandroid.shared.generated.resources.Res +import nowinandroid.shared.generated.resources.app_name +import org.jetbrains.compose.resources.stringResource +import org.koin.compose.KoinApplication +import org.koin.compose.koinInject + +fun main() = application { + Window( + onCloseRequest = ::exitApplication, + title = stringResource(Res.string.app_name), + ) { + App() + } +} + +@Composable +fun App() { + KoinApplication( + application = { + modules(appModules) + }, + ) { + val networkMonitor: NetworkMonitor = koinInject() + val timeZoneMonitor: TimeZoneMonitor = koinInject() + val analyticsHelper: AnalyticsHelper = koinInject() + val userNewsResourceRepository: UserNewsResourceRepository = koinInject() + val viewModel: MainScreenViewModel = koinInject() + val uiState: MainScreenUiState by viewModel.uiState.collectAsStateWithLifecycle() + val darkTheme = shouldUseDarkTheme(uiState) + + val appState = rememberNiaAppState( + networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, + timeZoneMonitor = timeZoneMonitor, + ) + + val currentTimeZone by appState.currentTimeZone.collectAsStateWithLifecycle() + + CompositionLocalProvider( + LocalAnalyticsHelper provides analyticsHelper, + LocalTimeZone provides currentTimeZone, + ) { + NiaTheme( + darkTheme = darkTheme, + androidTheme = shouldUseAndroidTheme(uiState), + disableDynamicTheming = shouldDisableDynamicTheming(uiState), + ) { + NiaApp(appState) + } + } + } +}