Add MainActivity view model

pull/330/head
Don Turner 2 years ago
parent cf4dd1fbff
commit 5e4100fafc

@ -106,6 +106,7 @@ dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.core.splashscreen)
implementation(libs.androidx.compose.runtime)
implementation(libs.androidx.lifecycle.runtimeCompose)
implementation(libs.androidx.compose.runtime.tracing)
implementation(libs.androidx.compose.material3.windowSizeClass)
implementation(libs.androidx.hilt.navigation.compose)

@ -19,6 +19,7 @@ package com.google.samples.apps.nowinandroid
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
@ -34,13 +35,11 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.metrics.performance.JankStats
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading
import com.google.samples.apps.nowinandroid.MainActivityUiState.Success
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.model.data.UserData
import com.google.samples.apps.nowinandroid.core.result.Result
import com.google.samples.apps.nowinandroid.core.result.asResult
import com.google.samples.apps.nowinandroid.ui.NiaApp
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@ -58,25 +57,20 @@ class MainActivity : ComponentActivity() {
@Inject
lateinit var lazyStats: dagger.Lazy<JankStats>
@Inject
lateinit var userDataRepository: UserDataRepository
val viewModel: MainActivityViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState)
/**
* The current user data, updated here to drive the UI theme
*/
var userDataResult: Result<UserData> by mutableStateOf(Result.Loading)
var uiState: MainActivityUiState by mutableStateOf(Loading)
// Update the user data
// Update the uiState
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
userDataRepository.userDataStream
.asResult()
viewModel.uiState
.onEach {
userDataResult = it
uiState = it
}
.collect()
}
@ -84,9 +78,9 @@ class MainActivity : ComponentActivity() {
// Keep the splash screen on-screen until the user data is loaded
splashScreen.setKeepOnScreenCondition {
when (userDataResult) {
Result.Loading -> true
is Result.Success, is Result.Error -> false
when (uiState) {
Loading -> true
is Success -> false
}
}
@ -96,7 +90,7 @@ class MainActivity : ComponentActivity() {
setContent {
val systemUiController = rememberSystemUiController()
val darkTheme = shouldUseDarkTheme(userDataResult)
val darkTheme = shouldUseDarkTheme(uiState)
// Update the dark content of the system bars to match the theme
DisposableEffect(systemUiController, darkTheme) {
@ -106,7 +100,7 @@ class MainActivity : ComponentActivity() {
NiaTheme(
darkTheme = darkTheme,
androidTheme = shouldUseAndroidTheme(userDataResult)
androidTheme = shouldUseAndroidTheme(uiState)
) {
NiaApp(
windowSizeClass = calculateWindowSizeClass(this),
@ -127,29 +121,29 @@ class MainActivity : ComponentActivity() {
}
/**
* Returns `true` if the Android theme should be used, as a function of the [userDataResult].
* Returns `true` if the Android theme should be used, as a function of the [uiState].
*/
@Composable
fun shouldUseAndroidTheme(
userDataResult: Result<UserData>,
): Boolean = when (userDataResult) {
Result.Loading, is Result.Error -> false
is Result.Success -> when (userDataResult.data.themeBrand) {
uiState: MainActivityUiState,
): Boolean = when (uiState) {
Loading -> false
is Success -> when (uiState.userData.themeBrand) {
ThemeBrand.DEFAULT -> false
ThemeBrand.ANDROID -> true
}
}
/**
* Returns `true` if dark theme should be used, as a function of the [userDataResult] and the
* Returns `true` if dark theme should be used, as a function of the [uiState] and the
* current system context.
*/
@Composable
fun shouldUseDarkTheme(
userDataResult: Result<UserData>,
): Boolean = when (userDataResult) {
Result.Loading, is Result.Error -> isSystemInDarkTheme()
is Result.Success -> when (userDataResult.data.darkThemeConfig) {
uiState: MainActivityUiState,
): Boolean = when (uiState) {
Loading -> isSystemInDarkTheme()
is Success -> when (uiState.userData.darkThemeConfig) {
DarkThemeConfig.FOLLOW_SYSTEM -> isSystemInDarkTheme()
DarkThemeConfig.LIGHT -> false
DarkThemeConfig.DARK -> true

@ -0,0 +1,48 @@
/*
* Copyright 2022 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.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading
import com.google.samples.apps.nowinandroid.MainActivityUiState.Success
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.model.data.UserData
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@HiltViewModel
class MainActivityViewModel @Inject constructor(
userDataRepository: UserDataRepository
) : ViewModel() {
val uiState: StateFlow<MainActivityUiState> = userDataRepository.userDataStream.map {
Success(it)
}.stateIn(
scope = viewModelScope,
initialValue = Loading,
started = SharingStarted.WhileSubscribed(5_000)
)
}
sealed interface MainActivityUiState {
object Loading : MainActivityUiState
data class Success(val userData: UserData) : MainActivityUiState
}
Loading…
Cancel
Save