From edeea501eb8a4afdf8d627b790a70d798c7cb8c9 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 6 May 2023 11:02:38 -0400 Subject: [PATCH] Finish up multiple locations support --- .../domain/manager/BasePreferenceManager.kt | 2 +- .../components/location/LocationsDrawer.kt | 93 +++++++++++-------- .../components/weather/WeatherCard.kt | 12 +-- .../screen/LocationPickerScreen.kt | 1 + .../presentation/screen/MainScreen.kt | 29 +++--- .../screenmodel/DailyWeatherScreenModel.kt | 4 +- .../screenmodel/HourlyWeatherScreenModel.kt | 5 +- .../qweather/presentation/tabs/TodayTab.kt | 6 +- .../qweather/presentation/tabs/WeekTab.kt | 2 +- 9 files changed, 82 insertions(+), 72 deletions(-) diff --git a/app/src/main/java/com/henryhiles/qweather/domain/manager/BasePreferenceManager.kt b/app/src/main/java/com/henryhiles/qweather/domain/manager/BasePreferenceManager.kt index 43ea37e..ad4707b 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/manager/BasePreferenceManager.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/manager/BasePreferenceManager.kt @@ -29,7 +29,7 @@ abstract class BasePreferenceManager( enumValueOf(getString(key, defaultValue.name)) protected inline fun getJson(key: String, defaultValue: T) = - Json.decodeFromString(getString(key, null)) ?: defaultValue + Json.decodeFromString(getString(key, Json.encodeToString(defaultValue))) protected fun putString(key: String, value: String?) = prefs.edit { putString(key, value) } private fun putBoolean(key: String, value: Boolean) = prefs.edit { putBoolean(key, value) } diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/location/LocationsDrawer.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/location/LocationsDrawer.kt index a606d2f..cc6dc6b 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/location/LocationsDrawer.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/location/LocationsDrawer.kt @@ -19,53 +19,68 @@ import com.henryhiles.qweather.presentation.screenmodel.LocationPreferenceManage import org.koin.androidx.compose.get @Composable -fun LocationsDrawer(drawerState: DrawerState, children: @Composable () -> Unit) { - val location: LocationPreferenceManager = get() +fun LocationsDrawer( + drawerState: DrawerState, + onClose: () -> Unit, + children: @Composable () -> Unit +) { + val locationPreferenceManager: LocationPreferenceManager = get() val navigator = LocalNavigator.current?.parent - ModalNavigationDrawer(drawerContent = { - ModalDrawerSheet { - Column(modifier = Modifier.padding(16.dp)) { - val locations = location.locations + ModalNavigationDrawer( + drawerContent = { + ModalDrawerSheet { + Column(modifier = Modifier.padding(16.dp)) { + val locations = locationPreferenceManager.locations - Text( - text = stringResource(id = R.string.locations), - style = MaterialTheme.typography.headlineSmall - ) - Spacer(modifier = Modifier.height(16.dp)) - locations.forEachIndexed { index, data -> - NavigationDrawerItem( - label = { Text(text = data.location) }, - selected = index == location.selectedIndex, - onClick = { location.selectedIndex = index }, - badge = { - IconButton(onClick = { location.locations -= data }) { - Icon( - imageVector = Icons.Default.Delete, - contentDescription = stringResource( - id = R.string.action_delete + Text( + text = stringResource(id = R.string.locations), + style = MaterialTheme.typography.headlineSmall + ) + + Spacer(modifier = Modifier.height(16.dp)) + locations.forEachIndexed { index, data -> + val selected = index == locationPreferenceManager.selectedIndex + NavigationDrawerItem( + label = { Text(text = data.location) }, + selected = selected, + onClick = { + onClose() + locationPreferenceManager.selectedIndex = index + }, + badge = { + IconButton(onClick = { + locationPreferenceManager.locations -= data + if (selected) locationPreferenceManager.selectedIndex = 0 + }) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = stringResource( + id = R.string.action_delete + ) ) - ) + } } - } + ) + } + + Spacer(modifier = Modifier.weight(1f)) + NavigationDrawerItem( + label = { Text(text = stringResource(id = R.string.location_add)) }, + icon = { + Icon( + imageVector = Icons.Default.Add, + contentDescription = stringResource(id = R.string.location_add) + ) + }, + selected = true, + onClick = { navigator?.push(LocationPickerScreen()) }, ) } - - Spacer(modifier = Modifier.weight(1f)) - NavigationDrawerItem( - label = { Text(text = stringResource(id = R.string.location_add)) }, - icon = { - Icon( - imageVector = Icons.Default.Add, - contentDescription = stringResource(id = R.string.location_add) - ) - }, - selected = true, - onClick = { navigator?.push(LocationPickerScreen()) }, - ) } - } - }, drawerState = drawerState) { + }, + drawerState = drawerState + ) { children() } } \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherCard.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherCard.kt index fb6daaf..84b7fbf 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherCard.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherCard.kt @@ -1,8 +1,6 @@ package com.henryhiles.qweather.presentation.components.weather -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image -import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons @@ -21,9 +19,8 @@ import androidx.compose.ui.unit.sp import com.henryhiles.qweather.domain.weather.HourlyWeatherData import java.time.format.DateTimeFormatter -@OptIn(ExperimentalFoundationApi::class) @Composable -fun WeatherCard(hour: HourlyWeatherData?, location: String, modifier: Modifier = Modifier) { +fun WeatherCard(hour: HourlyWeatherData?, modifier: Modifier = Modifier) { hour?.let { val formattedTime = remember(it) { it.time.format(DateTimeFormatter.ofPattern("HH:mm")) @@ -42,13 +39,6 @@ fun WeatherCard(hour: HourlyWeatherData?, location: String, modifier: Modifier = horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { - Text( - text = location, - modifier = Modifier - .width(152.dp) - .basicMarquee(delayMillis = 2000, initialDelayMillis = 1000), - maxLines = 1, - ) Text( text = "Today $formattedTime", ) diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/screen/LocationPickerScreen.kt b/app/src/main/java/com/henryhiles/qweather/presentation/screen/LocationPickerScreen.kt index c95776c..5edd8f7 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/screen/LocationPickerScreen.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screen/LocationPickerScreen.kt @@ -44,6 +44,7 @@ class LocationPickerScreen : Screen { FloatingActionButton(onClick = { location?.let { screenModel.prefs.locations += it + screenModel.prefs.selectedIndex = screenModel.prefs.locations.count() - 1 navigator?.push(MainScreen()) } ?: kotlin.run { isAboutOpen = true } }) { diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/screen/MainScreen.kt b/app/src/main/java/com/henryhiles/qweather/presentation/screen/MainScreen.kt index cf30e89..306cbb0 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/screen/MainScreen.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screen/MainScreen.kt @@ -1,21 +1,18 @@ package com.henryhiles.qweather.presentation.screen +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Menu import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.navigator.CurrentScreen -import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.tab.TabNavigator import com.henryhiles.qweather.R import com.henryhiles.qweather.domain.util.NavigationTab @@ -28,26 +25,34 @@ import kotlinx.coroutines.launch import org.koin.androidx.compose.get class MainScreen : Screen { + @OptIn(ExperimentalFoundationApi::class) @Composable override fun Content() { + val locationPreferenceManager: LocationPreferenceManager = get() val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) val coroutineScope = rememberCoroutineScope() TabNavigator(tab = TodayTab) { - LocationsDrawer(drawerState = drawerState) { + LocationsDrawer( + drawerState = drawerState, + onClose = { coroutineScope.launch { drawerState.close() } }) { Scaffold( topBar = { SmallToolbar( - title = { Text(text = stringResource(R.string.app_name)) }, + title = { + with(locationPreferenceManager) { + Text( + text = locations[selectedIndex].location, + maxLines = 1, + modifier = Modifier.basicMarquee() + ) + } + }, actions = { (it.current as? NavigationTab)?.Actions() } ) { - IconButton(onClick = { - coroutineScope.launch { - with(drawerState) { if (isOpen) close() else open() } - } - }) { + IconButton(onClick = { coroutineScope.launch { drawerState.open() } }) { Icon( imageVector = Icons.Default.Menu, contentDescription = stringResource(id = R.string.location_picker_open) diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/DailyWeatherScreenModel.kt b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/DailyWeatherScreenModel.kt index 45c97c5..e8503c1 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/DailyWeatherScreenModel.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/DailyWeatherScreenModel.kt @@ -19,13 +19,13 @@ data class DailyWeatherState( class DailyWeatherScreenModel( private val repository: WeatherRepository, - locationPreferenceManager: LocationPreferenceManager + val locationPreferenceManager: LocationPreferenceManager ) : ScreenModel { var state by mutableStateOf(DailyWeatherState()) private set - val location = locationPreferenceManager.locations[locationPreferenceManager.selectedIndex] fun loadWeatherInfo(cache: Boolean = true) { + val location = locationPreferenceManager.locations[locationPreferenceManager.selectedIndex] coroutineScope.launch { state = state.copy(isLoading = true, error = null) state = when (val result = repository.getDailyWeatherData( diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/HourlyWeatherScreenModel.kt b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/HourlyWeatherScreenModel.kt index 39b3609..e4ff8d1 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/HourlyWeatherScreenModel.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/HourlyWeatherScreenModel.kt @@ -19,14 +19,13 @@ data class HourlyWeatherState( class HourlyWeatherScreenModel( private val repository: WeatherRepository, - locationPreferenceManager: LocationPreferenceManager, + val locationPreferenceManager: LocationPreferenceManager, ) : ScreenModel { var state by mutableStateOf(HourlyWeatherState()) private set - val location = locationPreferenceManager.locations[locationPreferenceManager.selectedIndex] - fun loadWeatherInfo(cache: Boolean = true) { + val location = locationPreferenceManager.locations[locationPreferenceManager.selectedIndex] coroutineScope.launch { state = state.copy(isLoading = true, error = null, selected = null) state = when (val result = diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/tabs/TodayTab.kt b/app/src/main/java/com/henryhiles/qweather/presentation/tabs/TodayTab.kt index 5fd0591..dacf84b 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/tabs/TodayTab.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/tabs/TodayTab.kt @@ -43,7 +43,7 @@ object TodayTab : NavigationTab { override fun Content() { val weatherViewModel = getScreenModel() - LaunchedEffect(key1 = false) { + LaunchedEffect(key1 = weatherViewModel.locationPreferenceManager.selectedIndex) { weatherViewModel.loadWeatherInfo() } @@ -78,13 +78,13 @@ object TodayTab : NavigationTab { Column( modifier = Modifier .fillMaxSize() - .padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) ) { WeatherCard( hour = weatherViewModel.state.selected?.let { weatherViewModel.state.hourlyWeatherInfo?.weatherData?.get(it) } ?: weatherViewModel.state.hourlyWeatherInfo?.currentWeatherData, - location = weatherViewModel.location.location ) WeatherToday(state = weatherViewModel.state) WeatherForecast( diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/tabs/WeekTab.kt b/app/src/main/java/com/henryhiles/qweather/presentation/tabs/WeekTab.kt index 0b37339..9e19933 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/tabs/WeekTab.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/tabs/WeekTab.kt @@ -43,7 +43,7 @@ object WeekTab : NavigationTab { override fun Content() { val weatherViewModel = getScreenModel() - LaunchedEffect(key1 = false) { + LaunchedEffect(key1 = weatherViewModel.locationPreferenceManager.selectedIndex) { weatherViewModel.loadWeatherInfo() }