diff --git a/app/src/main/java/com/henryhiles/qweather/di/ManagerModule.kt b/app/src/main/java/com/henryhiles/qweather/di/ManagerModule.kt index 9225691..1a92b49 100644 --- a/app/src/main/java/com/henryhiles/qweather/di/ManagerModule.kt +++ b/app/src/main/java/com/henryhiles/qweather/di/ManagerModule.kt @@ -1,6 +1,7 @@ package com.henryhiles.qweather.di import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferenceManager +import com.henryhiles.qweather.presentation.screenmodel.UnitPreferenceManager import com.henryhiles.qweather.presentation.screenmodel.LocationPreferenceManager import org.koin.core.module.dsl.singleOf @@ -8,5 +9,6 @@ import org.koin.dsl.module val managerModule = module { singleOf(::AppearancePreferenceManager) + singleOf(::UnitPreferenceManager) singleOf(::LocationPreferenceManager) } \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/di/ScreenModelModule.kt b/app/src/main/java/com/henryhiles/qweather/di/ScreenModelModule.kt index 5f4f491..c4c8878 100644 --- a/app/src/main/java/com/henryhiles/qweather/di/ScreenModelModule.kt +++ b/app/src/main/java/com/henryhiles/qweather/di/ScreenModelModule.kt @@ -1,6 +1,7 @@ package com.henryhiles.qweather.di import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferencesScreenModel +import com.henryhiles.qweather.presentation.screenmodel.UnitPreferencesScreenModel import com.henryhiles.qweather.presentation.screenmodel.DailyWeatherScreenModel import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherScreenModel import com.henryhiles.qweather.presentation.screenmodel.LocationPickerScreenModel @@ -9,6 +10,7 @@ import org.koin.dsl.module val screenModelModule = module { factoryOf(::AppearancePreferencesScreenModel) + factoryOf(::UnitPreferencesScreenModel) factoryOf(::LocationPickerScreenModel) factoryOf(::HourlyWeatherScreenModel) factoryOf(::DailyWeatherScreenModel) diff --git a/app/src/main/java/com/henryhiles/qweather/domain/mappers/WeatherMappers.kt b/app/src/main/java/com/henryhiles/qweather/domain/mappers/WeatherMappers.kt index 4278746..f178122 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/mappers/WeatherMappers.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/mappers/WeatherMappers.kt @@ -1,7 +1,9 @@ package com.henryhiles.qweather.domain.mappers import com.henryhiles.qweather.domain.remote.DailyWeatherDataDto +import com.henryhiles.qweather.domain.remote.DailyWeatherUnitsDto import com.henryhiles.qweather.domain.remote.HourlyWeatherDataDto +import com.henryhiles.qweather.domain.remote.HourlyWeatherUnitsDto import com.henryhiles.qweather.domain.remote.WeatherDto import com.henryhiles.qweather.domain.weather.DailyWeatherData import com.henryhiles.qweather.domain.weather.HourlyWeatherData @@ -12,7 +14,7 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter import kotlin.math.roundToInt -fun HourlyWeatherDataDto.toHourlyWeatherData(): List { +fun HourlyWeatherDataDto.toHourlyWeatherData(units: HourlyWeatherUnitsDto): List { return time.subList(0, 24).mapIndexed { index, time -> HourlyWeatherData( time = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME), @@ -21,11 +23,12 @@ fun HourlyWeatherDataDto.toHourlyWeatherData(): List { windSpeed = windSpeed[index].roundToInt(), precipitationProbability = precipitationProbability.getOrNull(index), weatherType = WeatherType.fromWMO(weatherCode[index]), + units = units, ) } } -fun DailyWeatherDataDto.toDailyWeatherData(): List { +fun DailyWeatherDataDto.toDailyWeatherData(units: DailyWeatherUnitsDto): List { return date.mapIndexed { index, date -> DailyWeatherData( date = LocalDate.parse(date, DateTimeFormatter.ISO_DATE), @@ -38,12 +41,17 @@ fun DailyWeatherDataDto.toDailyWeatherData(): List { windSpeedMax = windSpeedMax[index].roundToInt(), sunrise = LocalDateTime.parse(sunrise[index]), sunset = LocalDateTime.parse(sunset[index]), + precipitationSum = precipitationSum[index], + units = units.copy( + precipitationSum = units.precipitationSum.replace("inch", "\""), + windSpeedMax = units.windSpeedMax.replace("mp/h", "mph"), + ) ) } } fun WeatherDto.toHourlyWeatherInfo(): HourlyWeatherInfo { - val weatherDataMap = hourlyWeatherData.toHourlyWeatherData() + val weatherDataMap = hourlyWeatherData.toHourlyWeatherData(units = hourlyUnits) val now = LocalDateTime.now() val currentWeatherData = weatherDataMap.find { it.time.hour == now.hour diff --git a/app/src/main/java/com/henryhiles/qweather/domain/remote/DailyWeatherDataDto.kt b/app/src/main/java/com/henryhiles/qweather/domain/remote/DailyWeatherDto.kt similarity index 61% rename from app/src/main/java/com/henryhiles/qweather/domain/remote/DailyWeatherDataDto.kt rename to app/src/main/java/com/henryhiles/qweather/domain/remote/DailyWeatherDto.kt index 7ae59d0..8ae23e8 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/remote/DailyWeatherDataDto.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/DailyWeatherDto.kt @@ -25,4 +25,21 @@ data class DailyWeatherDataDto( val apparentTemperatureMax: List, @SerialName("apparent_temperature_min") val apparentTemperatureMin: List +) +@Serializable +data class DailyWeatherUnitsDto( + @SerialName("precipitation_probability_max") + val precipitationProbabilityMax: String, + @SerialName("precipitation_sum") + val precipitationSum: String, + @SerialName("windspeed_10m_max") + val windSpeedMax: String, + @SerialName("temperature_2m_max") + val temperatureMax: String, + @SerialName("temperature_2m_min") + val temperatureMin: String, + @SerialName("apparent_temperature_max") + val apparentTemperatureMax: String, + @SerialName("apparent_temperature_min") + val apparentTemperatureMin: String ) \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/remote/HourlyWeatherDataDto.kt b/app/src/main/java/com/henryhiles/qweather/domain/remote/HourlyWeatherDto.kt similarity index 62% rename from app/src/main/java/com/henryhiles/qweather/domain/remote/HourlyWeatherDataDto.kt rename to app/src/main/java/com/henryhiles/qweather/domain/remote/HourlyWeatherDto.kt index 541e19e..ad755ce 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/remote/HourlyWeatherDataDto.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/HourlyWeatherDto.kt @@ -16,4 +16,16 @@ data class HourlyWeatherDataDto( val precipitationProbability: List, @SerialName("windspeed_10m") val windSpeed: List, +) + +@Serializable +data class HourlyWeatherUnitsDto( + @SerialName("temperature_2m") + val temperature: String, + @SerialName("apparent_temperature") + val apparentTemperature: String, + @SerialName("precipitation_probability") + val precipitationProbability: String, + @SerialName("windspeed_10m") + val windSpeed: String, ) \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherApi.kt b/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherApi.kt index fd608d8..6ae7db2 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherApi.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherApi.kt @@ -1,5 +1,7 @@ package com.henryhiles.qweather.domain.remote +import com.henryhiles.qweather.presentation.screenmodel.PrecipitationUnit +import com.henryhiles.qweather.presentation.screenmodel.TempUnit import retrofit2.http.GET import retrofit2.http.Headers import retrofit2.http.Query @@ -17,6 +19,9 @@ interface WeatherApi { suspend fun getWeatherData( @Query("latitude") lat: Float, @Query("longitude") long: Float, + @Query("temperature_unit") tempUnit: String, + @Query("wind_speed_unit") windUnit: String, + @Query("precipitation_unit") precipitationUnit: String, ): WeatherDto @Headers("Cache-Control: no-cache") @@ -24,5 +29,8 @@ interface WeatherApi { suspend fun getWeatherDataWithoutCache( @Query("latitude") lat: Float, @Query("longitude") long: Float, + @Query("temperature_unit") tempUnit: String, + @Query("wind_speed_unit") windUnit: String, + @Query("precipitation_unit") precipitationUnit: String, ): WeatherDto } \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherDto.kt b/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherDto.kt index 2ec4aac..ad991b2 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherDto.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/remote/WeatherDto.kt @@ -8,6 +8,12 @@ data class WeatherDto( @SerialName("hourly") val hourlyWeatherData: HourlyWeatherDataDto, + @SerialName("hourly_units") + val hourlyUnits: HourlyWeatherUnitsDto, + @SerialName("daily") - val dailyWeatherData: DailyWeatherDataDto + val dailyWeatherData: DailyWeatherDataDto, + + @SerialName("daily_units") + val dailyUnits: DailyWeatherUnitsDto, ) \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/repository/WeatherRepository.kt b/app/src/main/java/com/henryhiles/qweather/domain/repository/WeatherRepository.kt index 0239b06..cdf94df 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/repository/WeatherRepository.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/repository/WeatherRepository.kt @@ -1,28 +1,48 @@ package com.henryhiles.qweather.domain.repository +import androidx.compose.ui.text.toLowerCase import com.henryhiles.qweather.domain.mappers.toDailyWeatherData import com.henryhiles.qweather.domain.mappers.toHourlyWeatherInfo import com.henryhiles.qweather.domain.remote.WeatherApi +import com.henryhiles.qweather.domain.remote.WeatherDto import com.henryhiles.qweather.domain.util.Resource import com.henryhiles.qweather.domain.weather.DailyWeatherData import com.henryhiles.qweather.domain.weather.HourlyWeatherInfo +import com.henryhiles.qweather.presentation.screenmodel.UnitPreferenceManager class WeatherRepository(private val api: WeatherApi) { - suspend fun getHourlyWeatherData( + private suspend fun getWeatherData( lat: Float, long: Float, + units: UnitPreferenceManager, + cache: Boolean = true, + ): WeatherDto { + return if (cache) api.getWeatherData( + lat, + long, + units.tempUnit.name.lowercase(), + units.windUnit.name.lowercase(), + units.precipitationUnit.name.lowercase(), + ) else api.getWeatherDataWithoutCache( + lat, + long, + units.tempUnit.name, + units.windUnit.name, + units.precipitationUnit.name, + ) + } + + suspend fun getDailyWeatherData( + lat: Float, + long: Float, + units: UnitPreferenceManager, cache: Boolean = true - ): Resource { + ): Resource> { return try { Resource.Success( - data = ( - if (cache) api.getWeatherData( - lat = lat, - long = long - ) else api.getWeatherDataWithoutCache( - lat = lat, - long = long - )).toHourlyWeatherInfo() + with(getWeatherData(lat, long, units, cache)) { + dailyWeatherData.toDailyWeatherData(dailyUnits) + } ) } catch (e: Exception) { e.printStackTrace() @@ -30,20 +50,15 @@ class WeatherRepository(private val api: WeatherApi) { } } - suspend fun getDailyWeatherData( + suspend fun getHourlyWeatherData( lat: Float, long: Float, + units: UnitPreferenceManager, cache: Boolean = true - ): Resource> { + ): Resource { return try { Resource.Success( - (if (cache) api.getWeatherData( - lat = lat, - long = long - ) else api.getWeatherDataWithoutCache( - lat = lat, - long = long - )).dailyWeatherData.toDailyWeatherData() + getWeatherData(lat, long, units, cache).toHourlyWeatherInfo() ) } catch (e: Exception) { e.printStackTrace() diff --git a/app/src/main/java/com/henryhiles/qweather/domain/weather/DailyWeatherData.kt b/app/src/main/java/com/henryhiles/qweather/domain/weather/DailyWeather.kt similarity index 72% rename from app/src/main/java/com/henryhiles/qweather/domain/weather/DailyWeatherData.kt rename to app/src/main/java/com/henryhiles/qweather/domain/weather/DailyWeather.kt index dd42e6c..17bbf0d 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/weather/DailyWeatherData.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/weather/DailyWeather.kt @@ -1,10 +1,12 @@ package com.henryhiles.qweather.domain.weather +import com.henryhiles.qweather.domain.remote.DailyWeatherUnitsDto import java.time.LocalDate import java.time.LocalDateTime data class DailyWeatherData( val date: LocalDate, + val precipitationSum: Float, val weatherType: WeatherType, val sunrise: LocalDateTime, val sunset: LocalDateTime, @@ -13,5 +15,6 @@ data class DailyWeatherData( val apparentTemperatureMax: Int, val apparentTemperatureMin: Int, val precipitationProbabilityMax: Int?, - val windSpeedMax: Int + val windSpeedMax: Int, + val units: DailyWeatherUnitsDto, ) \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/domain/weather/HourlyWeatherData.kt b/app/src/main/java/com/henryhiles/qweather/domain/weather/HourlyWeather.kt similarity index 73% rename from app/src/main/java/com/henryhiles/qweather/domain/weather/HourlyWeatherData.kt rename to app/src/main/java/com/henryhiles/qweather/domain/weather/HourlyWeather.kt index 6dd9617..71e61a4 100644 --- a/app/src/main/java/com/henryhiles/qweather/domain/weather/HourlyWeatherData.kt +++ b/app/src/main/java/com/henryhiles/qweather/domain/weather/HourlyWeather.kt @@ -1,5 +1,6 @@ package com.henryhiles.qweather.domain.weather +import com.henryhiles.qweather.domain.remote.HourlyWeatherUnitsDto import java.time.LocalDateTime data class HourlyWeatherData( @@ -9,4 +10,5 @@ data class HourlyWeatherData( val weatherType: WeatherType, val precipitationProbability: Int?, val windSpeed: Int, + val units: HourlyWeatherUnitsDto, ) \ No newline at end of file diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/settings/SettingsItemChoice.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/settings/SettingsItemChoice.kt index a87041d..c6baa83 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/settings/SettingsItemChoice.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/settings/SettingsItemChoice.kt @@ -9,6 +9,7 @@ import androidx.compose.ui.Modifier @Composable inline fun > SettingsItemChoice( label: String, + secondaryLabel: String? = null, title: String = label, disabled: Boolean = false, pref: E, @@ -23,6 +24,7 @@ inline fun > SettingsItemChoice( SettingItem( modifier = Modifier.clickable { opened = true }, text = { Text(text = label) }, + secondaryText = { if (secondaryLabel != null) Text(text = secondaryLabel) } ) { SettingsChoiceDialog( visible = opened, 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 b166963..3bc1f66 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 @@ -15,15 +15,22 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.henryhiles.qweather.R import com.henryhiles.qweather.domain.util.getIcon import com.henryhiles.qweather.domain.weather.DailyWeatherData import com.henryhiles.qweather.domain.weather.HourlyWeatherData import java.time.format.DateTimeFormatter @Composable -fun WeatherCard(hour: HourlyWeatherData, dailyData: DailyWeatherData, modifier: Modifier = Modifier) { +fun WeatherCard( + hour: HourlyWeatherData, + dailyData: DailyWeatherData, + modifier: Modifier = Modifier +) { val formattedTime = remember(hour) { hour.time.format(DateTimeFormatter.ofPattern("HH:mm")) } @@ -48,14 +55,23 @@ fun WeatherCard(hour: HourlyWeatherData, dailyData: DailyWeatherData, modifier: Spacer(modifier = Modifier.height(16.dp)) Image( painter = painterResource(id = getIcon(hour, dailyData)), - contentDescription = "Image of ${hour.weatherType.weatherDesc}", + contentDescription = hour.weatherType.weatherDesc, modifier = Modifier.height(140.dp), contentScale = ContentScale.FillHeight ) Spacer(modifier = Modifier.height(16.dp)) - Text(text = "${hour.temperature}°C", fontSize = 50.sp) + Text(text = "${hour.temperature}${hour.units.temperature}", fontSize = 50.sp) Spacer(modifier = Modifier.height(16.dp)) - Text(text = "${hour.weatherType.weatherDesc} - Feels like ${hour.apparentTemperature}°C", fontSize = 20.sp) + Text( + text = stringResource( + id = R.string.weather_description, + hour.weatherType.weatherDesc, + hour.apparentTemperature, + hour.units.apparentTemperature + ), + fontSize = 20.sp, + textAlign = TextAlign.Center, + ) Spacer(modifier = Modifier.height(32.dp)) Row( modifier = Modifier.fillMaxWidth(), @@ -63,15 +79,15 @@ fun WeatherCard(hour: HourlyWeatherData, dailyData: DailyWeatherData, modifier: ) { WeatherDataDisplay( value = hour.precipitationProbability, - unit = "%", + unit = hour.units.precipitationProbability, icon = Icons.Outlined.WaterDrop, - description = "Chance of precipitation" + description = stringResource(id = R.string.precipitation_probability) ) WeatherDataDisplay( value = hour.windSpeed, - unit = "km/h", + unit = hour.units.windSpeed, icon = Icons.Outlined.WindPower, - description = "Wind Speed", + description = stringResource(id = R.string.wind_speed), ) } } diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherDataDisplay.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherDataDisplay.kt index a0dc036..94ded43 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherDataDisplay.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherDataDisplay.kt @@ -16,7 +16,7 @@ import com.henryhiles.qweather.R @Composable fun WeatherDataDisplay( - value: Int?, + value: Any?, unit: String, icon: ImageVector, description: String, diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherDay.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherDay.kt index 2860c9b..2413a3e 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherDay.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherDay.kt @@ -16,8 +16,10 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import com.henryhiles.qweather.R import com.henryhiles.qweather.domain.weather.DailyWeatherData import java.time.format.DateTimeFormatter @@ -40,7 +42,7 @@ fun WeatherDay(dailyWeatherData: DailyWeatherData) { ) { Image( painter = painterResource(id = dailyWeatherData.weatherType.iconRes), - contentDescription = "Image of ${dailyWeatherData.weatherType.weatherDesc}", + contentDescription = dailyWeatherData.weatherType.weatherDesc, modifier = Modifier.width(48.dp) ) @@ -50,12 +52,18 @@ fun WeatherDay(dailyWeatherData: DailyWeatherData) { text = formattedDate, fontWeight = FontWeight.Bold, ) - Text(text = "Feels like ${dailyWeatherData.apparentTemperatureMax}°C") + Text( + text = stringResource( + id = R.string.feels_like, + dailyWeatherData.apparentTemperatureMax, + dailyWeatherData.units.apparentTemperatureMax + ) + ) } Spacer(modifier = Modifier.weight(1f)) Text( - text = "${dailyWeatherData.temperatureMax}°C", + text = "${dailyWeatherData.temperatureMax}${dailyWeatherData.units.temperatureMax}", style = MaterialTheme.typography.titleLarge, ) } @@ -67,23 +75,21 @@ fun WeatherDay(dailyWeatherData: DailyWeatherData) { ) { WeatherDataDisplay( value = dailyWeatherData.precipitationProbabilityMax, - unit = "%", + unit = dailyWeatherData.units.precipitationProbabilityMax, icon = Icons.Outlined.WaterDrop, - description = "Chance of rain" + description = stringResource(id = R.string.precipitation_probability) ) - Spacer(modifier = Modifier.width(16.dp)) WeatherDataDisplay( - value = dailyWeatherData.windSpeedMax, - unit = "mm", + value = dailyWeatherData.precipitationSum, + unit = dailyWeatherData.units.precipitationSum, icon = Icons.Outlined.Water, - description = "Precipitation Amount" + description = stringResource(id = R.string.precipitation_amount) ) - Spacer(modifier = Modifier.width(16.dp)) WeatherDataDisplay( value = dailyWeatherData.windSpeedMax, - unit = "km/h", + unit = dailyWeatherData.units.windSpeedMax, icon = Icons.Outlined.WindPower, - description = "Wind Speed" + description = stringResource(id = R.string.wind_speed) ) } } diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherHour.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherHour.kt index 372b147..8591f66 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherHour.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherHour.kt @@ -44,10 +44,10 @@ fun WeatherHour( painter = painterResource( id = getIcon(it, dailyData) ), - contentDescription = "Image of ${it.weatherType.weatherDesc}", + contentDescription = it.weatherType.weatherDesc, modifier = Modifier.width(40.dp) ) - Text(text = "${it.temperature}°C") + Text(text = "${it.temperature}${it.units.temperature}") } } } diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherToday.kt b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherToday.kt index f313e40..14b6614 100644 --- a/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherToday.kt +++ b/app/src/main/java/com/henryhiles/qweather/presentation/components/weather/WeatherToday.kt @@ -46,19 +46,19 @@ fun WeatherToday(data: DailyWeatherData) { ) { WeatherDataDisplay( value = data.temperatureMax, - unit = "°C", + unit = data.units.temperatureMax, icon = Icons.Default.ArrowUpward, - description = stringResource(R.string.weather_high, data.temperatureMax) + description = stringResource(R.string.weather_high, data.temperatureMax, data.units.temperatureMax) ) WeatherDataDisplay( value = data.temperatureMin, - unit = "°C", + unit = data.units.temperatureMin, icon = Icons.Default.ArrowDownward, - description = stringResource(id = R.string.weather_low, data.temperatureMin) + description = stringResource(id = R.string.weather_low, data.temperatureMin, data.units.temperatureMin) ) WeatherDataDisplay( value = data.precipitationProbabilityMax, - unit = "%", + unit = data.units.precipitationProbabilityMax, icon = Icons.Outlined.WaterDrop, description = data.precipitationProbabilityMax?.let { stringResource( diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/screen/UnitsScreen.kt b/app/src/main/java/com/henryhiles/qweather/presentation/screen/UnitsScreen.kt new file mode 100644 index 0000000..b856be4 --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screen/UnitsScreen.kt @@ -0,0 +1,60 @@ +package com.henryhiles.qweather.presentation.screen + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.koin.getScreenModel +import com.henryhiles.qweather.R +import com.henryhiles.qweather.presentation.components.navigation.SmallToolbar +import com.henryhiles.qweather.presentation.components.settings.SettingsItemChoice +import com.henryhiles.qweather.presentation.screenmodel.UnitPreferencesScreenModel + +class UnitsScreen : Screen { + @Composable + override fun Content(){ + val screenModel: UnitPreferencesScreenModel = getScreenModel() + val context = LocalContext.current + + Scaffold(topBar = { + SmallToolbar( + title = { Text(text = stringResource(R.string.settings_units)) }, + backButton = true + ) + }) { padding -> + Column( + modifier = Modifier + .padding(padding) + .verticalScroll(rememberScrollState()) + ) { + SettingsItemChoice( + label = stringResource(R.string.unit_temp), + secondaryLabel = stringResource(R.string.unit_temp_desc), + pref = screenModel.prefs.tempUnit, + labelFactory = { context.getString(it.label) } + ) { screenModel.prefs.tempUnit = it } + + SettingsItemChoice( + label = stringResource(R.string.unit_wind), + secondaryLabel = stringResource(R.string.unit_wind_desc), + pref = screenModel.prefs.windUnit, + labelFactory = { context.getString(it.label) } + ) { screenModel.prefs.windUnit = it } + + SettingsItemChoice( + label = stringResource(R.string.unit_precipitation), + secondaryLabel = stringResource(R.string.unit_precipitation_desc), + pref = screenModel.prefs.precipitationUnit, + labelFactory = { context.getString(it.label) } + ) { screenModel.prefs.precipitationUnit = it } + } + } + } +} \ No newline at end of file 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 65d2e8a..49927e3 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,7 +19,8 @@ data class DailyWeatherState( class DailyWeatherScreenModel( private val repository: WeatherRepository, - val locationPreferenceManager: LocationPreferenceManager + private val unitsPreferenceManager: UnitPreferenceManager, + val locationPreferenceManager: LocationPreferenceManager, ) : ScreenModel { var state by mutableStateOf(DailyWeatherState()) private set @@ -31,7 +32,8 @@ class DailyWeatherScreenModel( state = when (val result = repository.getDailyWeatherData( lat = location.latitude, long = location.longitude, - cache = cache + cache = cache, + units = unitsPreferenceManager, )) { is Resource.Success -> { state.copy( 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 8b6cf10..5573171 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,6 +19,7 @@ data class HourlyWeatherState( class HourlyWeatherScreenModel( private val repository: WeatherRepository, + private val unitsPreferenceManager: UnitPreferenceManager, val locationPreferenceManager: LocationPreferenceManager, ) : ScreenModel { var state by mutableStateOf(HourlyWeatherState()) @@ -32,6 +33,7 @@ class HourlyWeatherScreenModel( repository.getHourlyWeatherData( lat = location.latitude, long = location.longitude, + units = unitsPreferenceManager, cache = cache )) { is Resource.Success -> { diff --git a/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/UnitPreferencesScreenModel.kt b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/UnitPreferencesScreenModel.kt new file mode 100644 index 0000000..f696017 --- /dev/null +++ b/app/src/main/java/com/henryhiles/qweather/presentation/screenmodel/UnitPreferencesScreenModel.kt @@ -0,0 +1,36 @@ +package com.henryhiles.qweather.presentation.screenmodel + +import android.content.Context +import androidx.annotation.StringRes +import cafe.adriel.voyager.core.model.ScreenModel +import com.henryhiles.qweather.R +import com.henryhiles.qweather.domain.manager.BasePreferenceManager + +class UnitPreferenceManager(context: Context) : + BasePreferenceManager(context.getSharedPreferences("prefs", Context.MODE_PRIVATE)) { + + var tempUnit by enumPreference("temp_unit", TempUnit.CELSIUS) + var windUnit by enumPreference("wind_unit", WindUnit.KMH) + var precipitationUnit by enumPreference("precipitation_unit", PrecipitationUnit.MM) +} + +enum class TempUnit(@StringRes val label: Int) { + CELSIUS(R.string.celsius), + FAHRENHEIT(R.string.fahrenheit), +} + +enum class WindUnit(@StringRes val label: Int) { + KMH(R.string.kmh), + MS(R.string.ms), + MPH(R.string.mph), + KN(R.string.kn), +} + +enum class PrecipitationUnit(@StringRes val label: Int) { + MM(R.string.mm), + INCH(R.string.inch) +} + +class UnitPreferencesScreenModel( + val prefs: UnitPreferenceManager +) : ScreenModel \ No newline at end of file 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 bf6bf5d..b76cd47 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.AcUnit import androidx.compose.material.icons.outlined.GpsFixed import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Palette @@ -20,6 +21,7 @@ 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 +import com.henryhiles.qweather.presentation.screen.UnitsScreen object SettingsTab : NavigationTab { override val options: TabOptions @@ -52,6 +54,12 @@ object SettingsTab : NavigationTab { subtext = stringResource(R.string.settings_location_description), destination = ::LocationPickerScreen ) + SettingsCategory( + icon = Icons.Outlined.AcUnit, + text = stringResource(R.string.settings_units), + subtext = stringResource(R.string.settings_units_description), + destination = ::UnitsScreen + ) } } 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 c5a245c..83ef2d3 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 @@ -46,15 +46,15 @@ object WeekTab : NavigationTab { @Composable override fun Content() { - val dailyWeatherViewModel = getScreenModel() + val weatherViewModel = getScreenModel() - LaunchedEffect(key1 = dailyWeatherViewModel.locationPreferenceManager.selectedIndex) { - dailyWeatherViewModel.loadWeatherInfo() + LaunchedEffect(key1 = weatherViewModel.locationPreferenceManager.selectedIndex) { + weatherViewModel.loadWeatherInfo() } Box(modifier = Modifier.fillMaxSize()) { when { - dailyWeatherViewModel.state.isLoading -> { + weatherViewModel.state.isLoading -> { CircularProgressIndicator( modifier = Modifier.align( Alignment.Center @@ -62,15 +62,19 @@ object WeekTab : NavigationTab { ) } - dailyWeatherViewModel.state.error != null -> { + weatherViewModel.state.error != null -> { AlertDialog( onDismissRequest = {}, - confirmButton = {}, - title = { Text(text = stringResource(R.string.error)) }, + confirmButton = { + TextButton(onClick = { weatherViewModel.loadWeatherInfo() }) { + Text(text = stringResource(id = R.string.action_try_again)) + } + }, + title = { Text(text = stringResource(id = R.string.error)) }, text = { SelectionContainer { Text( - text = dailyWeatherViewModel.state.error!!, + text = weatherViewModel.state.error!!, ) } }, @@ -79,7 +83,7 @@ object WeekTab : NavigationTab { else -> { LazyColumn(contentPadding = PaddingValues(16.dp)) { - dailyWeatherViewModel.state.dailyWeatherData?.let { data -> + weatherViewModel.state.dailyWeatherData?.let { data -> item { WeatherToday(data = data[0]) } items(data) { Spacer(modifier = Modifier.height(16.dp)) @@ -94,10 +98,10 @@ object WeekTab : NavigationTab { @Composable override fun Actions() { - val dailyWeatherViewModel = getScreenModel() + val weatherViewModel = getScreenModel() IconButton(onClick = { - dailyWeatherViewModel.loadWeatherInfo(cache = false) + weatherViewModel.loadWeatherInfo(cache = false) }) { Icon( imageVector = Icons.Filled.Refresh, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 877dee5..dcf8e16 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,6 +23,8 @@ Theme, dynamic colors Location Location to fetch data from + Units + Units to fetch data in, e.g. imperial/metric Location Locations @@ -34,9 +36,33 @@ Light Dark - High: %1$d°C - Low: %1$d°C + + Temperature Unit + Celsius (C°) + Fahrenheit (F°) + Celsius, Fahrenheit + + Wind Speed Unit + Kilometers per Hour (km/h) + Meters per second (m/s) + Miles per hour (mph) + Knots (kn) + Km/h, m/s, Mph, Knots + + Precipitation Unit + Millimeters (mm) + Inches (in) + Millimeters, Inches + + High: %1$d%2$s + Low: %1$d%2$s + Feels like %1$d%2$s Precipitation: %1$d﹪ + %1$s - Feels like %2$d%3$s + + Precipitation Probability + Precipitation Amount + Wind Speed Today in %1$s