From 1f245aa3376491f31b6b8d84cc81e12a1fed98c9 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 17 Apr 2023 21:11:24 -0400 Subject: [PATCH] fix everything --- .../qweather/domain/remote/GeocodingApi.kt | 2 +- .../qweather/presentation/QWeatherActivity.kt | 6 +- .../components/weather/WeatherCard.kt | 18 ++-- .../components/weather/WeatherForecast.kt | 3 +- .../screen/LocationPickerScreen.kt | 90 +++++++++++-------- .../screenmodel/DailyWeatherScreenModel.kt | 51 +++++------ .../screenmodel/HourlyWeatherScreenModel.kt | 54 +++++------ .../qweather/presentation/tabs/SettingsTab.kt | 8 ++ .../qweather/presentation/tabs/TodayTab.kt | 16 +--- .../qweather/presentation/tabs/WeekTab.kt | 19 ++-- app/src/main/res/values/strings.xml | 9 +- 11 files changed, 139 insertions(+), 137 deletions(-) diff --git a/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingApi.kt b/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingApi.kt index da15a7c..1d98a79 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingApi.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/GeocodingApi.kt @@ -4,7 +4,7 @@ import retrofit2.http.GET import retrofit2.http.Query interface GeocodingApi { - @GET("v1/search?count=4") + @GET("v1/search?count=10") suspend fun getGeocodingData( @Query("name") location: String, ): GeocodingDto diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/QWeatherActivity.kt b/app/src/main/java/com/henryhiles/qweather/presentation/QWeatherActivity.kt index 1112294..7604304 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/QWeatherActivity.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/QWeatherActivity.kt @@ -37,7 +37,11 @@ class QWeatherActivity : ComponentActivity() { WeatherAppTheme(darkTheme = isDark, monet = prefs.monet) { Surface(modifier = Modifier.fillMaxSize()) { Text(text = location.location) - Navigator(screen = if (isLocationSet) MainScreen() else LocationPickerScreen()) { + Navigator( + screen = if (isLocationSet) MainScreen() else LocationPickerScreen(), + onBackPressed = { + it !is MainScreen + }) { SlideTransition(it) } } 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 23099ce..de3efe3 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 @@ -11,7 +11,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource @@ -22,7 +21,7 @@ import com.henryhiles.qweather.domain.weather.HourlyWeatherData import java.time.format.DateTimeFormatter @Composable -fun WeatherCard(hour: HourlyWeatherData?, modifier: Modifier = Modifier) { +fun WeatherCard(hour: HourlyWeatherData?, location: String, modifier: Modifier = Modifier) { hour?.let { val formattedTime = remember(it) { it.time.format(DateTimeFormatter.ofPattern("HH:mm")) @@ -37,10 +36,17 @@ fun WeatherCard(hour: HourlyWeatherData?, modifier: Modifier = Modifier) { .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - Text( - text = "Today $formattedTime", - modifier = Modifier.align(Alignment.End), color = Color.White - ) + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = location, + ) + Text( + text = "Today $formattedTime", + ) + } Spacer(modifier = Modifier.height(16.dp)) Image( painter = painterResource(id = it.weatherType.iconRes), diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherForecast.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherForecast.kt index d09069b..f96a7bd 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherForecast.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherForecast.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherState @@ -25,7 +24,7 @@ fun WeatherForecast( .fillMaxWidth() .padding(horizontal = 16.dp) ) { - Text(text = "Today", fontSize = 20.sp, color = Color.White) + Text(text = "Today", fontSize = 20.sp) Spacer(modifier = Modifier.height(16.dp)) val rowState = rememberLazyListState(LocalDateTime.now().hour) 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 62ac6be..7d1fe56 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 @@ -9,6 +9,7 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.MyLocation import androidx.compose.material.icons.outlined.Search import androidx.compose.material3.* @@ -24,20 +25,35 @@ import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.koin.getScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import com.henryhiles.qweather.R +import com.henryhiles.qweather.presentation.components.navigation.SmallToolbar import com.henryhiles.qweather.presentation.screenmodel.LocationPickerScreenModel + class LocationPickerScreen : Screen { @Composable override fun Content() { val screenModel: LocationPickerScreenModel = getScreenModel() - var latitude by remember { mutableStateOf(0f) } - var longitude by remember { mutableStateOf(0f) } - var location by remember { mutableStateOf("") } + var latitude by remember { mutableStateOf(screenModel.prefs.latitude) } + var longitude by remember { mutableStateOf(screenModel.prefs.longitude) } + var location by remember { mutableStateOf(screenModel.prefs.location) } var locationSearch by remember { mutableStateOf("") } + var isAboutOpen by remember { mutableStateOf(false) } val navigator = LocalNavigator.current val context = LocalContext.current - Box(modifier = Modifier.fillMaxSize()) { + Scaffold(floatingActionButton = { + FloatingActionButton(onClick = { + screenModel.prefs.location = location + screenModel.prefs.latitude = latitude + screenModel.prefs.longitude = longitude + navigator?.push(MainScreen()) + }) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = stringResource(id = R.string.action_apply) + ) + } + }) { screenModel.state.error?.let { AlertDialog( onDismissRequest = {}, @@ -51,24 +67,31 @@ class LocationPickerScreen : Screen { } }, ) - } ?: AlertDialog( - onDismissRequest = {}, - confirmButton = { - TextButton( - onClick = { - screenModel.prefs.location = location - screenModel.prefs.latitude = latitude - screenModel.prefs.longitude = longitude - navigator?.push(MainScreen()) - }, - enabled = location != "" - ) { - Text(text = stringResource(id = R.string.action_apply)) - } - }, - title = { Text(text = stringResource(id = R.string.location_choose)) }, - text = { - Column { + } ?: kotlin.run { + Column { + SmallToolbar( + title = { Text(text = stringResource(id = R.string.location_choose)) }, + actions = { + IconButton( + onClick = { isAboutOpen = true }) { + Icon( + imageVector = Icons.Outlined.Info, + contentDescription = stringResource(id = R.string.help_screen) + ) + } + }) + Column(modifier = Modifier.padding(16.dp)) { + if (isAboutOpen) AlertDialog( + title = { Text(text = stringResource(id = R.string.location_choose)) }, + text = { Text(text = stringResource(id = R.string.help_location_picker)) }, + onDismissRequest = { isAboutOpen = false }, + confirmButton = { + TextButton( + onClick = { isAboutOpen = false }) { + Text(text = stringResource(id = R.string.action_confirm)) + } + }) + OutlinedTextField( label = { Text(text = stringResource(id = R.string.location)) }, keyboardOptions = KeyboardOptions( @@ -82,9 +105,7 @@ class LocationPickerScreen : Screen { }), maxLines = 1, value = locationSearch, - onValueChange = { - locationSearch = it - }, + onValueChange = { locationSearch = it }, trailingIcon = { if (locationSearch == "") IconButton(onClick = { @@ -106,20 +127,17 @@ class LocationPickerScreen : Screen { contentDescription = stringResource(id = R.string.action_search) ) } - } + }, + modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(16.dp)) - Text(text = "${screenModel.state.locations != null}") - screenModel.state.locations?.let { - Text( - text = "hi" - ) - } if (screenModel.state.isLoading) CircularProgressIndicator( - modifier = Modifier.align( - Alignment.CenterHorizontally - ) + modifier = Modifier + .align( + Alignment.CenterHorizontally + ) + .padding(16.dp) ) else screenModel.state.locations?.let { LazyColumn { items(it) { @@ -161,7 +179,7 @@ class LocationPickerScreen : Screen { } } } - ) + } } } } 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 9e177c3..7bde18d 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 @@ -1,12 +1,10 @@ package com.henryhiles.qweather.presentation.screenmodel -import android.location.Location import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import cafe.adriel.voyager.core.model.ScreenModel import cafe.adriel.voyager.core.model.coroutineScope -import com.henryhiles.qweather.domain.location.LocationTracker import com.henryhiles.qweather.domain.repository.WeatherRepository import com.henryhiles.qweather.domain.util.Resource import com.henryhiles.qweather.domain.weather.DailyWeatherData @@ -21,43 +19,34 @@ data class DailyWeatherState( class DailyWeatherScreenModel( private val repository: WeatherRepository, - private val locationTracker: LocationTracker, + private val location: LocationPreferenceManager ) : ScreenModel { var state by mutableStateOf(DailyWeatherState()) private set - private var currentLocation: Location? = null fun loadWeatherInfo(cache: Boolean = true) { coroutineScope.launch { state = state.copy(isLoading = true, error = null) - currentLocation = locationTracker.getCurrentLocation() - currentLocation?.let { location -> - state = when (val result = - repository.getDailyWeatherData( - lat = location.latitude.toFloat(), - long = location.longitude.toFloat(), - cache = cache - )) { - is Resource.Success -> { - state.copy( - dailyWeatherData = result.data, - isLoading = false, - error = null - ) - } - is Resource.Error -> { - state.copy( - dailyWeatherData = null, - isLoading = false, - error = result.message - ) - } + state = when (val result = + repository.getDailyWeatherData( + lat = location.latitude, + long = location.longitude, + cache = cache + )) { + is Resource.Success -> { + state.copy( + dailyWeatherData = result.data, + isLoading = false, + error = null + ) + } + is Resource.Error -> { + state.copy( + dailyWeatherData = null, + isLoading = false, + error = result.message + ) } - } ?: kotlin.run { - state = state.copy( - isLoading = false, - error = "Couldn't retrieve location. Make sure to grant permission and enable GPS." - ) } } } 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 0e90ee6..e7c4edc 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 @@ -1,13 +1,10 @@ package com.henryhiles.qweather.presentation.screenmodel -import android.content.Context import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import cafe.adriel.voyager.core.model.ScreenModel import cafe.adriel.voyager.core.model.coroutineScope -import com.henryhiles.qweather.R -import com.henryhiles.qweather.domain.location.LocationTracker import com.henryhiles.qweather.domain.repository.WeatherRepository import com.henryhiles.qweather.domain.util.Resource import com.henryhiles.qweather.domain.weather.HourlyWeatherInfo @@ -22,8 +19,7 @@ data class HourlyWeatherState( class HourlyWeatherScreenModel( private val repository: WeatherRepository, - private val locationTracker: LocationTracker, - private val context: Context + val location: LocationPreferenceManager, ) : ScreenModel { var state by mutableStateOf(HourlyWeatherState()) private set @@ -31,35 +27,27 @@ class HourlyWeatherScreenModel( fun loadWeatherInfo(cache: Boolean = true) { coroutineScope.launch { state = state.copy(isLoading = true, error = null, selected = null) - val currentLocation = locationTracker.getCurrentLocation() - currentLocation?.let { location -> - state = when (val result = - repository.getHourlyWeatherData( - lat = location.latitude.toFloat(), - long = location.longitude.toFloat(), - cache = cache - )) { - is Resource.Success -> { - state.copy( - hourlyWeatherInfo = result.data, - isLoading = false, - error = null, - ) - } - - is Resource.Error -> { - state.copy( - hourlyWeatherInfo = null, - isLoading = false, - error = result.message - ) - } + state = when (val result = + repository.getHourlyWeatherData( + lat = location.latitude, + long = location.longitude, + cache = cache + )) { + is Resource.Success -> { + state.copy( + hourlyWeatherInfo = result.data, + isLoading = false, + error = null, + ) + } + + is Resource.Error -> { + state.copy( + hourlyWeatherInfo = null, + isLoading = false, + error = result.message + ) } - } ?: kotlin.run { - state = state.copy( - isLoading = false, - error = context.getString(R.string.error_location) - ) } } } diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/tabs/SettingsTab.kt b/app/src/main/java/com/henryhiles/qweather/presentation/tabs/SettingsTab.kt index c3f2994..f398520 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/tabs/SettingsTab.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/tabs/SettingsTab.kt @@ -3,6 +3,7 @@ package com.henryhiles.qweather.presentation.tabs import androidx.compose.foundation.layout.Column import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Settings +import androidx.compose.material.icons.outlined.GpsFixed import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Palette import androidx.compose.material3.Icon @@ -19,6 +20,7 @@ import com.henryhiles.qweather.domain.util.NavigationTab import com.henryhiles.qweather.presentation.components.settings.SettingsCategory import com.henryhiles.qweather.presentation.screen.AboutScreen import com.henryhiles.qweather.presentation.screen.AppearanceSettingsScreen +import com.henryhiles.qweather.presentation.screen.LocationPickerScreen object SettingsTab : NavigationTab { override val options: TabOptions @@ -45,6 +47,12 @@ object SettingsTab : NavigationTab { subtext = stringResource(R.string.settings_appearance_description), destination = ::AppearanceSettingsScreen ) + SettingsCategory( + icon = Icons.Outlined.GpsFixed, + text = stringResource(R.string.settings_location), + subtext = stringResource(R.string.settings_location_description), + destination = ::LocationPickerScreen + ) } } 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 800848d..67b7f9d 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 @@ -1,6 +1,5 @@ package com.henryhiles.qweather.presentation.tabs -import android.Manifest import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.icons.Icons @@ -17,8 +16,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import cafe.adriel.voyager.koin.getScreenModel import cafe.adriel.voyager.navigator.tab.TabOptions -import com.google.accompanist.permissions.ExperimentalPermissionsApi -import com.google.accompanist.permissions.rememberPermissionState import com.henryhiles.qweather.R import com.henryhiles.qweather.domain.util.NavigationTab import com.henryhiles.qweather.presentation.components.weather.WeatherCard @@ -41,18 +38,12 @@ object TodayTab : NavigationTab { } } - @OptIn(ExperimentalPermissionsApi::class) @Composable override fun Content() { val weatherViewModel = getScreenModel() - val permissionsState = rememberPermissionState( - Manifest.permission.ACCESS_FINE_LOCATION, - ) { - weatherViewModel.loadWeatherInfo() - } - LaunchedEffect(key1 = true) { - permissionsState.launchPermissionRequest() + LaunchedEffect(key1 = false) { + weatherViewModel.loadWeatherInfo() } Box(modifier = Modifier.fillMaxSize()) { @@ -90,7 +81,8 @@ object TodayTab : NavigationTab { WeatherCard( hour = weatherViewModel.state.selected?.let { weatherViewModel.state.hourlyWeatherInfo?.weatherData?.get(it) - } ?: weatherViewModel.state.hourlyWeatherInfo?.currentWeatherData + } ?: weatherViewModel.state.hourlyWeatherInfo?.currentWeatherData, + location = weatherViewModel.location.location ) Spacer(modifier = Modifier.height(16.dp)) 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 ca72062..0b37339 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 @@ -1,7 +1,7 @@ package com.henryhiles.qweather.presentation.tabs -import android.Manifest -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.text.selection.SelectionContainer @@ -9,15 +9,15 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.DateRange import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.res.stringResource import cafe.adriel.voyager.koin.getScreenModel import cafe.adriel.voyager.navigator.tab.TabOptions -import com.google.accompanist.permissions.ExperimentalPermissionsApi -import com.google.accompanist.permissions.rememberPermissionState import com.henryhiles.qweather.R import com.henryhiles.qweather.domain.util.NavigationTab import com.henryhiles.qweather.presentation.components.weather.WeatherDay @@ -39,21 +39,14 @@ object WeekTab : NavigationTab { } } - @OptIn(ExperimentalPermissionsApi::class) @Composable override fun Content() { val weatherViewModel = getScreenModel() - val permissionsState = rememberPermissionState( - Manifest.permission.ACCESS_FINE_LOCATION, - ) { + LaunchedEffect(key1 = false) { weatherViewModel.loadWeatherInfo() } - LaunchedEffect(key1 = true) { - permissionsState.launchPermissionRequest() - } - Box(modifier = Modifier.fillMaxSize()) { when { weatherViewModel.state.isLoading -> { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 264a2a3..4d34db7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,14 +12,19 @@ Reload Try Again - Selected + Selectedx + + How do I use this screen? + Please either tap the auto-pick button or enter a location. Then tap the apply button in the bottom left corner. Theme Dynamic Theme Available on Android 12+ Appearance - Theme, code style + Theme, dynamic colors + Location + Location to fetch data from Location %1$s, %2$s, %3$s