Different unit support, fixes #2
This commit is contained in:
parent
985382fa1b
commit
ae71058669
23 changed files with 304 additions and 67 deletions
|
@ -1,6 +1,7 @@
|
||||||
package com.henryhiles.qweather.di
|
package com.henryhiles.qweather.di
|
||||||
|
|
||||||
import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferenceManager
|
import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferenceManager
|
||||||
|
import com.henryhiles.qweather.presentation.screenmodel.UnitPreferenceManager
|
||||||
import com.henryhiles.qweather.presentation.screenmodel.LocationPreferenceManager
|
import com.henryhiles.qweather.presentation.screenmodel.LocationPreferenceManager
|
||||||
|
|
||||||
import org.koin.core.module.dsl.singleOf
|
import org.koin.core.module.dsl.singleOf
|
||||||
|
@ -8,5 +9,6 @@ import org.koin.dsl.module
|
||||||
|
|
||||||
val managerModule = module {
|
val managerModule = module {
|
||||||
singleOf(::AppearancePreferenceManager)
|
singleOf(::AppearancePreferenceManager)
|
||||||
|
singleOf(::UnitPreferenceManager)
|
||||||
singleOf(::LocationPreferenceManager)
|
singleOf(::LocationPreferenceManager)
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package com.henryhiles.qweather.di
|
package com.henryhiles.qweather.di
|
||||||
|
|
||||||
import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferencesScreenModel
|
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.DailyWeatherScreenModel
|
||||||
import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherScreenModel
|
import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherScreenModel
|
||||||
import com.henryhiles.qweather.presentation.screenmodel.LocationPickerScreenModel
|
import com.henryhiles.qweather.presentation.screenmodel.LocationPickerScreenModel
|
||||||
|
@ -9,6 +10,7 @@ import org.koin.dsl.module
|
||||||
|
|
||||||
val screenModelModule = module {
|
val screenModelModule = module {
|
||||||
factoryOf(::AppearancePreferencesScreenModel)
|
factoryOf(::AppearancePreferencesScreenModel)
|
||||||
|
factoryOf(::UnitPreferencesScreenModel)
|
||||||
factoryOf(::LocationPickerScreenModel)
|
factoryOf(::LocationPickerScreenModel)
|
||||||
factoryOf(::HourlyWeatherScreenModel)
|
factoryOf(::HourlyWeatherScreenModel)
|
||||||
factoryOf(::DailyWeatherScreenModel)
|
factoryOf(::DailyWeatherScreenModel)
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package com.henryhiles.qweather.domain.mappers
|
package com.henryhiles.qweather.domain.mappers
|
||||||
|
|
||||||
import com.henryhiles.qweather.domain.remote.DailyWeatherDataDto
|
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.HourlyWeatherDataDto
|
||||||
|
import com.henryhiles.qweather.domain.remote.HourlyWeatherUnitsDto
|
||||||
import com.henryhiles.qweather.domain.remote.WeatherDto
|
import com.henryhiles.qweather.domain.remote.WeatherDto
|
||||||
import com.henryhiles.qweather.domain.weather.DailyWeatherData
|
import com.henryhiles.qweather.domain.weather.DailyWeatherData
|
||||||
import com.henryhiles.qweather.domain.weather.HourlyWeatherData
|
import com.henryhiles.qweather.domain.weather.HourlyWeatherData
|
||||||
|
@ -12,7 +14,7 @@ import java.time.LocalDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
fun HourlyWeatherDataDto.toHourlyWeatherData(): List<HourlyWeatherData> {
|
fun HourlyWeatherDataDto.toHourlyWeatherData(units: HourlyWeatherUnitsDto): List<HourlyWeatherData> {
|
||||||
return time.subList(0, 24).mapIndexed { index, time ->
|
return time.subList(0, 24).mapIndexed { index, time ->
|
||||||
HourlyWeatherData(
|
HourlyWeatherData(
|
||||||
time = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME),
|
time = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME),
|
||||||
|
@ -21,11 +23,12 @@ fun HourlyWeatherDataDto.toHourlyWeatherData(): List<HourlyWeatherData> {
|
||||||
windSpeed = windSpeed[index].roundToInt(),
|
windSpeed = windSpeed[index].roundToInt(),
|
||||||
precipitationProbability = precipitationProbability.getOrNull(index),
|
precipitationProbability = precipitationProbability.getOrNull(index),
|
||||||
weatherType = WeatherType.fromWMO(weatherCode[index]),
|
weatherType = WeatherType.fromWMO(weatherCode[index]),
|
||||||
|
units = units,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DailyWeatherDataDto.toDailyWeatherData(): List<DailyWeatherData> {
|
fun DailyWeatherDataDto.toDailyWeatherData(units: DailyWeatherUnitsDto): List<DailyWeatherData> {
|
||||||
return date.mapIndexed { index, date ->
|
return date.mapIndexed { index, date ->
|
||||||
DailyWeatherData(
|
DailyWeatherData(
|
||||||
date = LocalDate.parse(date, DateTimeFormatter.ISO_DATE),
|
date = LocalDate.parse(date, DateTimeFormatter.ISO_DATE),
|
||||||
|
@ -38,12 +41,17 @@ fun DailyWeatherDataDto.toDailyWeatherData(): List<DailyWeatherData> {
|
||||||
windSpeedMax = windSpeedMax[index].roundToInt(),
|
windSpeedMax = windSpeedMax[index].roundToInt(),
|
||||||
sunrise = LocalDateTime.parse(sunrise[index]),
|
sunrise = LocalDateTime.parse(sunrise[index]),
|
||||||
sunset = LocalDateTime.parse(sunset[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 {
|
fun WeatherDto.toHourlyWeatherInfo(): HourlyWeatherInfo {
|
||||||
val weatherDataMap = hourlyWeatherData.toHourlyWeatherData()
|
val weatherDataMap = hourlyWeatherData.toHourlyWeatherData(units = hourlyUnits)
|
||||||
val now = LocalDateTime.now()
|
val now = LocalDateTime.now()
|
||||||
val currentWeatherData = weatherDataMap.find {
|
val currentWeatherData = weatherDataMap.find {
|
||||||
it.time.hour == now.hour
|
it.time.hour == now.hour
|
||||||
|
|
|
@ -26,3 +26,20 @@ data class DailyWeatherDataDto(
|
||||||
@SerialName("apparent_temperature_min")
|
@SerialName("apparent_temperature_min")
|
||||||
val apparentTemperatureMin: List<Float>
|
val apparentTemperatureMin: List<Float>
|
||||||
)
|
)
|
||||||
|
@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
|
||||||
|
)
|
|
@ -17,3 +17,15 @@ data class HourlyWeatherDataDto(
|
||||||
@SerialName("windspeed_10m")
|
@SerialName("windspeed_10m")
|
||||||
val windSpeed: List<Float>,
|
val windSpeed: List<Float>,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@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,
|
||||||
|
)
|
|
@ -1,5 +1,7 @@
|
||||||
package com.henryhiles.qweather.domain.remote
|
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.GET
|
||||||
import retrofit2.http.Headers
|
import retrofit2.http.Headers
|
||||||
import retrofit2.http.Query
|
import retrofit2.http.Query
|
||||||
|
@ -17,6 +19,9 @@ interface WeatherApi {
|
||||||
suspend fun getWeatherData(
|
suspend fun getWeatherData(
|
||||||
@Query("latitude") lat: Float,
|
@Query("latitude") lat: Float,
|
||||||
@Query("longitude") long: Float,
|
@Query("longitude") long: Float,
|
||||||
|
@Query("temperature_unit") tempUnit: String,
|
||||||
|
@Query("wind_speed_unit") windUnit: String,
|
||||||
|
@Query("precipitation_unit") precipitationUnit: String,
|
||||||
): WeatherDto
|
): WeatherDto
|
||||||
|
|
||||||
@Headers("Cache-Control: no-cache")
|
@Headers("Cache-Control: no-cache")
|
||||||
|
@ -24,5 +29,8 @@ interface WeatherApi {
|
||||||
suspend fun getWeatherDataWithoutCache(
|
suspend fun getWeatherDataWithoutCache(
|
||||||
@Query("latitude") lat: Float,
|
@Query("latitude") lat: Float,
|
||||||
@Query("longitude") long: Float,
|
@Query("longitude") long: Float,
|
||||||
|
@Query("temperature_unit") tempUnit: String,
|
||||||
|
@Query("wind_speed_unit") windUnit: String,
|
||||||
|
@Query("precipitation_unit") precipitationUnit: String,
|
||||||
): WeatherDto
|
): WeatherDto
|
||||||
}
|
}
|
|
@ -8,6 +8,12 @@ data class WeatherDto(
|
||||||
@SerialName("hourly")
|
@SerialName("hourly")
|
||||||
val hourlyWeatherData: HourlyWeatherDataDto,
|
val hourlyWeatherData: HourlyWeatherDataDto,
|
||||||
|
|
||||||
|
@SerialName("hourly_units")
|
||||||
|
val hourlyUnits: HourlyWeatherUnitsDto,
|
||||||
|
|
||||||
@SerialName("daily")
|
@SerialName("daily")
|
||||||
val dailyWeatherData: DailyWeatherDataDto
|
val dailyWeatherData: DailyWeatherDataDto,
|
||||||
|
|
||||||
|
@SerialName("daily_units")
|
||||||
|
val dailyUnits: DailyWeatherUnitsDto,
|
||||||
)
|
)
|
|
@ -1,28 +1,48 @@
|
||||||
package com.henryhiles.qweather.domain.repository
|
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.toDailyWeatherData
|
||||||
import com.henryhiles.qweather.domain.mappers.toHourlyWeatherInfo
|
import com.henryhiles.qweather.domain.mappers.toHourlyWeatherInfo
|
||||||
import com.henryhiles.qweather.domain.remote.WeatherApi
|
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.util.Resource
|
||||||
import com.henryhiles.qweather.domain.weather.DailyWeatherData
|
import com.henryhiles.qweather.domain.weather.DailyWeatherData
|
||||||
import com.henryhiles.qweather.domain.weather.HourlyWeatherInfo
|
import com.henryhiles.qweather.domain.weather.HourlyWeatherInfo
|
||||||
|
import com.henryhiles.qweather.presentation.screenmodel.UnitPreferenceManager
|
||||||
|
|
||||||
class WeatherRepository(private val api: WeatherApi) {
|
class WeatherRepository(private val api: WeatherApi) {
|
||||||
suspend fun getHourlyWeatherData(
|
private suspend fun getWeatherData(
|
||||||
lat: Float,
|
lat: Float,
|
||||||
long: 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
|
cache: Boolean = true
|
||||||
): Resource<HourlyWeatherInfo> {
|
): Resource<List<DailyWeatherData>> {
|
||||||
return try {
|
return try {
|
||||||
Resource.Success(
|
Resource.Success(
|
||||||
data = (
|
with(getWeatherData(lat, long, units, cache)) {
|
||||||
if (cache) api.getWeatherData(
|
dailyWeatherData.toDailyWeatherData(dailyUnits)
|
||||||
lat = lat,
|
}
|
||||||
long = long
|
|
||||||
) else api.getWeatherDataWithoutCache(
|
|
||||||
lat = lat,
|
|
||||||
long = long
|
|
||||||
)).toHourlyWeatherInfo()
|
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
@ -30,20 +50,15 @@ class WeatherRepository(private val api: WeatherApi) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getDailyWeatherData(
|
suspend fun getHourlyWeatherData(
|
||||||
lat: Float,
|
lat: Float,
|
||||||
long: Float,
|
long: Float,
|
||||||
|
units: UnitPreferenceManager,
|
||||||
cache: Boolean = true
|
cache: Boolean = true
|
||||||
): Resource<List<DailyWeatherData>> {
|
): Resource<HourlyWeatherInfo> {
|
||||||
return try {
|
return try {
|
||||||
Resource.Success(
|
Resource.Success(
|
||||||
(if (cache) api.getWeatherData(
|
getWeatherData(lat, long, units, cache).toHourlyWeatherInfo()
|
||||||
lat = lat,
|
|
||||||
long = long
|
|
||||||
) else api.getWeatherDataWithoutCache(
|
|
||||||
lat = lat,
|
|
||||||
long = long
|
|
||||||
)).dailyWeatherData.toDailyWeatherData()
|
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package com.henryhiles.qweather.domain.weather
|
package com.henryhiles.qweather.domain.weather
|
||||||
|
|
||||||
|
import com.henryhiles.qweather.domain.remote.DailyWeatherUnitsDto
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
data class DailyWeatherData(
|
data class DailyWeatherData(
|
||||||
val date: LocalDate,
|
val date: LocalDate,
|
||||||
|
val precipitationSum: Float,
|
||||||
val weatherType: WeatherType,
|
val weatherType: WeatherType,
|
||||||
val sunrise: LocalDateTime,
|
val sunrise: LocalDateTime,
|
||||||
val sunset: LocalDateTime,
|
val sunset: LocalDateTime,
|
||||||
|
@ -13,5 +15,6 @@ data class DailyWeatherData(
|
||||||
val apparentTemperatureMax: Int,
|
val apparentTemperatureMax: Int,
|
||||||
val apparentTemperatureMin: Int,
|
val apparentTemperatureMin: Int,
|
||||||
val precipitationProbabilityMax: Int?,
|
val precipitationProbabilityMax: Int?,
|
||||||
val windSpeedMax: Int
|
val windSpeedMax: Int,
|
||||||
|
val units: DailyWeatherUnitsDto,
|
||||||
)
|
)
|
|
@ -1,5 +1,6 @@
|
||||||
package com.henryhiles.qweather.domain.weather
|
package com.henryhiles.qweather.domain.weather
|
||||||
|
|
||||||
|
import com.henryhiles.qweather.domain.remote.HourlyWeatherUnitsDto
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
data class HourlyWeatherData(
|
data class HourlyWeatherData(
|
||||||
|
@ -9,4 +10,5 @@ data class HourlyWeatherData(
|
||||||
val weatherType: WeatherType,
|
val weatherType: WeatherType,
|
||||||
val precipitationProbability: Int?,
|
val precipitationProbability: Int?,
|
||||||
val windSpeed: Int,
|
val windSpeed: Int,
|
||||||
|
val units: HourlyWeatherUnitsDto,
|
||||||
)
|
)
|
|
@ -9,6 +9,7 @@ import androidx.compose.ui.Modifier
|
||||||
@Composable
|
@Composable
|
||||||
inline fun <reified E : Enum<E>> SettingsItemChoice(
|
inline fun <reified E : Enum<E>> SettingsItemChoice(
|
||||||
label: String,
|
label: String,
|
||||||
|
secondaryLabel: String? = null,
|
||||||
title: String = label,
|
title: String = label,
|
||||||
disabled: Boolean = false,
|
disabled: Boolean = false,
|
||||||
pref: E,
|
pref: E,
|
||||||
|
@ -23,6 +24,7 @@ inline fun <reified E : Enum<E>> SettingsItemChoice(
|
||||||
SettingItem(
|
SettingItem(
|
||||||
modifier = Modifier.clickable { opened = true },
|
modifier = Modifier.clickable { opened = true },
|
||||||
text = { Text(text = label) },
|
text = { Text(text = label) },
|
||||||
|
secondaryText = { if (secondaryLabel != null) Text(text = secondaryLabel) }
|
||||||
) {
|
) {
|
||||||
SettingsChoiceDialog(
|
SettingsChoiceDialog(
|
||||||
visible = opened,
|
visible = opened,
|
||||||
|
|
|
@ -15,15 +15,22 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.painterResource
|
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.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.henryhiles.qweather.R
|
||||||
import com.henryhiles.qweather.domain.util.getIcon
|
import com.henryhiles.qweather.domain.util.getIcon
|
||||||
import com.henryhiles.qweather.domain.weather.DailyWeatherData
|
import com.henryhiles.qweather.domain.weather.DailyWeatherData
|
||||||
import com.henryhiles.qweather.domain.weather.HourlyWeatherData
|
import com.henryhiles.qweather.domain.weather.HourlyWeatherData
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun WeatherCard(hour: HourlyWeatherData, dailyData: DailyWeatherData, modifier: Modifier = Modifier) {
|
fun WeatherCard(
|
||||||
|
hour: HourlyWeatherData,
|
||||||
|
dailyData: DailyWeatherData,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
val formattedTime = remember(hour) {
|
val formattedTime = remember(hour) {
|
||||||
hour.time.format(DateTimeFormatter.ofPattern("HH:mm"))
|
hour.time.format(DateTimeFormatter.ofPattern("HH:mm"))
|
||||||
}
|
}
|
||||||
|
@ -48,14 +55,23 @@ fun WeatherCard(hour: HourlyWeatherData, dailyData: DailyWeatherData, modifier:
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(id = getIcon(hour, dailyData)),
|
painter = painterResource(id = getIcon(hour, dailyData)),
|
||||||
contentDescription = "Image of ${hour.weatherType.weatherDesc}",
|
contentDescription = hour.weatherType.weatherDesc,
|
||||||
modifier = Modifier.height(140.dp),
|
modifier = Modifier.height(140.dp),
|
||||||
contentScale = ContentScale.FillHeight
|
contentScale = ContentScale.FillHeight
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
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))
|
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))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
@ -63,15 +79,15 @@ fun WeatherCard(hour: HourlyWeatherData, dailyData: DailyWeatherData, modifier:
|
||||||
) {
|
) {
|
||||||
WeatherDataDisplay(
|
WeatherDataDisplay(
|
||||||
value = hour.precipitationProbability,
|
value = hour.precipitationProbability,
|
||||||
unit = "%",
|
unit = hour.units.precipitationProbability,
|
||||||
icon = Icons.Outlined.WaterDrop,
|
icon = Icons.Outlined.WaterDrop,
|
||||||
description = "Chance of precipitation"
|
description = stringResource(id = R.string.precipitation_probability)
|
||||||
)
|
)
|
||||||
WeatherDataDisplay(
|
WeatherDataDisplay(
|
||||||
value = hour.windSpeed,
|
value = hour.windSpeed,
|
||||||
unit = "km/h",
|
unit = hour.units.windSpeed,
|
||||||
icon = Icons.Outlined.WindPower,
|
icon = Icons.Outlined.WindPower,
|
||||||
description = "Wind Speed",
|
description = stringResource(id = R.string.wind_speed),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import com.henryhiles.qweather.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun WeatherDataDisplay(
|
fun WeatherDataDisplay(
|
||||||
value: Int?,
|
value: Any?,
|
||||||
unit: String,
|
unit: String,
|
||||||
icon: ImageVector,
|
icon: ImageVector,
|
||||||
description: String,
|
description: String,
|
||||||
|
|
|
@ -16,8 +16,10 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.henryhiles.qweather.R
|
||||||
import com.henryhiles.qweather.domain.weather.DailyWeatherData
|
import com.henryhiles.qweather.domain.weather.DailyWeatherData
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
@ -40,7 +42,7 @@ fun WeatherDay(dailyWeatherData: DailyWeatherData) {
|
||||||
) {
|
) {
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(id = dailyWeatherData.weatherType.iconRes),
|
painter = painterResource(id = dailyWeatherData.weatherType.iconRes),
|
||||||
contentDescription = "Image of ${dailyWeatherData.weatherType.weatherDesc}",
|
contentDescription = dailyWeatherData.weatherType.weatherDesc,
|
||||||
modifier = Modifier.width(48.dp)
|
modifier = Modifier.width(48.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -50,12 +52,18 @@ fun WeatherDay(dailyWeatherData: DailyWeatherData) {
|
||||||
text = formattedDate,
|
text = formattedDate,
|
||||||
fontWeight = FontWeight.Bold,
|
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))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
Text(
|
Text(
|
||||||
text = "${dailyWeatherData.temperatureMax}°C",
|
text = "${dailyWeatherData.temperatureMax}${dailyWeatherData.units.temperatureMax}",
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -67,23 +75,21 @@ fun WeatherDay(dailyWeatherData: DailyWeatherData) {
|
||||||
) {
|
) {
|
||||||
WeatherDataDisplay(
|
WeatherDataDisplay(
|
||||||
value = dailyWeatherData.precipitationProbabilityMax,
|
value = dailyWeatherData.precipitationProbabilityMax,
|
||||||
unit = "%",
|
unit = dailyWeatherData.units.precipitationProbabilityMax,
|
||||||
icon = Icons.Outlined.WaterDrop,
|
icon = Icons.Outlined.WaterDrop,
|
||||||
description = "Chance of rain"
|
description = stringResource(id = R.string.precipitation_probability)
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
|
||||||
WeatherDataDisplay(
|
WeatherDataDisplay(
|
||||||
value = dailyWeatherData.windSpeedMax,
|
value = dailyWeatherData.precipitationSum,
|
||||||
unit = "mm",
|
unit = dailyWeatherData.units.precipitationSum,
|
||||||
icon = Icons.Outlined.Water,
|
icon = Icons.Outlined.Water,
|
||||||
description = "Precipitation Amount"
|
description = stringResource(id = R.string.precipitation_amount)
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
|
||||||
WeatherDataDisplay(
|
WeatherDataDisplay(
|
||||||
value = dailyWeatherData.windSpeedMax,
|
value = dailyWeatherData.windSpeedMax,
|
||||||
unit = "km/h",
|
unit = dailyWeatherData.units.windSpeedMax,
|
||||||
icon = Icons.Outlined.WindPower,
|
icon = Icons.Outlined.WindPower,
|
||||||
description = "Wind Speed"
|
description = stringResource(id = R.string.wind_speed)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,10 +44,10 @@ fun WeatherHour(
|
||||||
painter = painterResource(
|
painter = painterResource(
|
||||||
id = getIcon(it, dailyData)
|
id = getIcon(it, dailyData)
|
||||||
),
|
),
|
||||||
contentDescription = "Image of ${it.weatherType.weatherDesc}",
|
contentDescription = it.weatherType.weatherDesc,
|
||||||
modifier = Modifier.width(40.dp)
|
modifier = Modifier.width(40.dp)
|
||||||
)
|
)
|
||||||
Text(text = "${it.temperature}°C")
|
Text(text = "${it.temperature}${it.units.temperature}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,19 +46,19 @@ fun WeatherToday(data: DailyWeatherData) {
|
||||||
) {
|
) {
|
||||||
WeatherDataDisplay(
|
WeatherDataDisplay(
|
||||||
value = data.temperatureMax,
|
value = data.temperatureMax,
|
||||||
unit = "°C",
|
unit = data.units.temperatureMax,
|
||||||
icon = Icons.Default.ArrowUpward,
|
icon = Icons.Default.ArrowUpward,
|
||||||
description = stringResource(R.string.weather_high, data.temperatureMax)
|
description = stringResource(R.string.weather_high, data.temperatureMax, data.units.temperatureMax)
|
||||||
)
|
)
|
||||||
WeatherDataDisplay(
|
WeatherDataDisplay(
|
||||||
value = data.temperatureMin,
|
value = data.temperatureMin,
|
||||||
unit = "°C",
|
unit = data.units.temperatureMin,
|
||||||
icon = Icons.Default.ArrowDownward,
|
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(
|
WeatherDataDisplay(
|
||||||
value = data.precipitationProbabilityMax,
|
value = data.precipitationProbabilityMax,
|
||||||
unit = "%",
|
unit = data.units.precipitationProbabilityMax,
|
||||||
icon = Icons.Outlined.WaterDrop,
|
icon = Icons.Outlined.WaterDrop,
|
||||||
description = data.precipitationProbabilityMax?.let {
|
description = data.precipitationProbabilityMax?.let {
|
||||||
stringResource(
|
stringResource(
|
||||||
|
|
|
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,8 @@ data class DailyWeatherState(
|
||||||
|
|
||||||
class DailyWeatherScreenModel(
|
class DailyWeatherScreenModel(
|
||||||
private val repository: WeatherRepository,
|
private val repository: WeatherRepository,
|
||||||
val locationPreferenceManager: LocationPreferenceManager
|
private val unitsPreferenceManager: UnitPreferenceManager,
|
||||||
|
val locationPreferenceManager: LocationPreferenceManager,
|
||||||
) : ScreenModel {
|
) : ScreenModel {
|
||||||
var state by mutableStateOf(DailyWeatherState())
|
var state by mutableStateOf(DailyWeatherState())
|
||||||
private set
|
private set
|
||||||
|
@ -31,7 +32,8 @@ class DailyWeatherScreenModel(
|
||||||
state = when (val result = repository.getDailyWeatherData(
|
state = when (val result = repository.getDailyWeatherData(
|
||||||
lat = location.latitude,
|
lat = location.latitude,
|
||||||
long = location.longitude,
|
long = location.longitude,
|
||||||
cache = cache
|
cache = cache,
|
||||||
|
units = unitsPreferenceManager,
|
||||||
)) {
|
)) {
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
state.copy(
|
state.copy(
|
||||||
|
|
|
@ -19,6 +19,7 @@ data class HourlyWeatherState(
|
||||||
|
|
||||||
class HourlyWeatherScreenModel(
|
class HourlyWeatherScreenModel(
|
||||||
private val repository: WeatherRepository,
|
private val repository: WeatherRepository,
|
||||||
|
private val unitsPreferenceManager: UnitPreferenceManager,
|
||||||
val locationPreferenceManager: LocationPreferenceManager,
|
val locationPreferenceManager: LocationPreferenceManager,
|
||||||
) : ScreenModel {
|
) : ScreenModel {
|
||||||
var state by mutableStateOf(HourlyWeatherState())
|
var state by mutableStateOf(HourlyWeatherState())
|
||||||
|
@ -32,6 +33,7 @@ class HourlyWeatherScreenModel(
|
||||||
repository.getHourlyWeatherData(
|
repository.getHourlyWeatherData(
|
||||||
lat = location.latitude,
|
lat = location.latitude,
|
||||||
long = location.longitude,
|
long = location.longitude,
|
||||||
|
units = unitsPreferenceManager,
|
||||||
cache = cache
|
cache = cache
|
||||||
)) {
|
)) {
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
|
|
|
@ -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
|
|
@ -3,6 +3,7 @@ package com.henryhiles.qweather.presentation.tabs
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Settings
|
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.GpsFixed
|
||||||
import androidx.compose.material.icons.outlined.Info
|
import androidx.compose.material.icons.outlined.Info
|
||||||
import androidx.compose.material.icons.outlined.Palette
|
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.AboutScreen
|
||||||
import com.henryhiles.qweather.presentation.screen.AppearanceSettingsScreen
|
import com.henryhiles.qweather.presentation.screen.AppearanceSettingsScreen
|
||||||
import com.henryhiles.qweather.presentation.screen.LocationPickerScreen
|
import com.henryhiles.qweather.presentation.screen.LocationPickerScreen
|
||||||
|
import com.henryhiles.qweather.presentation.screen.UnitsScreen
|
||||||
|
|
||||||
object SettingsTab : NavigationTab {
|
object SettingsTab : NavigationTab {
|
||||||
override val options: TabOptions
|
override val options: TabOptions
|
||||||
|
@ -52,6 +54,12 @@ object SettingsTab : NavigationTab {
|
||||||
subtext = stringResource(R.string.settings_location_description),
|
subtext = stringResource(R.string.settings_location_description),
|
||||||
destination = ::LocationPickerScreen
|
destination = ::LocationPickerScreen
|
||||||
)
|
)
|
||||||
|
SettingsCategory(
|
||||||
|
icon = Icons.Outlined.AcUnit,
|
||||||
|
text = stringResource(R.string.settings_units),
|
||||||
|
subtext = stringResource(R.string.settings_units_description),
|
||||||
|
destination = ::UnitsScreen
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,15 +46,15 @@ object WeekTab : NavigationTab {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val dailyWeatherViewModel = getScreenModel<DailyWeatherScreenModel>()
|
val weatherViewModel = getScreenModel<DailyWeatherScreenModel>()
|
||||||
|
|
||||||
LaunchedEffect(key1 = dailyWeatherViewModel.locationPreferenceManager.selectedIndex) {
|
LaunchedEffect(key1 = weatherViewModel.locationPreferenceManager.selectedIndex) {
|
||||||
dailyWeatherViewModel.loadWeatherInfo()
|
weatherViewModel.loadWeatherInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
when {
|
when {
|
||||||
dailyWeatherViewModel.state.isLoading -> {
|
weatherViewModel.state.isLoading -> {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
modifier = Modifier.align(
|
modifier = Modifier.align(
|
||||||
Alignment.Center
|
Alignment.Center
|
||||||
|
@ -62,15 +62,19 @@ object WeekTab : NavigationTab {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
dailyWeatherViewModel.state.error != null -> {
|
weatherViewModel.state.error != null -> {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = {},
|
onDismissRequest = {},
|
||||||
confirmButton = {},
|
confirmButton = {
|
||||||
title = { Text(text = stringResource(R.string.error)) },
|
TextButton(onClick = { weatherViewModel.loadWeatherInfo() }) {
|
||||||
|
Text(text = stringResource(id = R.string.action_try_again))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title = { Text(text = stringResource(id = R.string.error)) },
|
||||||
text = {
|
text = {
|
||||||
SelectionContainer {
|
SelectionContainer {
|
||||||
Text(
|
Text(
|
||||||
text = dailyWeatherViewModel.state.error!!,
|
text = weatherViewModel.state.error!!,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -79,7 +83,7 @@ object WeekTab : NavigationTab {
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
LazyColumn(contentPadding = PaddingValues(16.dp)) {
|
LazyColumn(contentPadding = PaddingValues(16.dp)) {
|
||||||
dailyWeatherViewModel.state.dailyWeatherData?.let { data ->
|
weatherViewModel.state.dailyWeatherData?.let { data ->
|
||||||
item { WeatherToday(data = data[0]) }
|
item { WeatherToday(data = data[0]) }
|
||||||
items(data) {
|
items(data) {
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
@ -94,10 +98,10 @@ object WeekTab : NavigationTab {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Actions() {
|
override fun Actions() {
|
||||||
val dailyWeatherViewModel = getScreenModel<DailyWeatherScreenModel>()
|
val weatherViewModel = getScreenModel<DailyWeatherScreenModel>()
|
||||||
|
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
dailyWeatherViewModel.loadWeatherInfo(cache = false)
|
weatherViewModel.loadWeatherInfo(cache = false)
|
||||||
}) {
|
}) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.Refresh,
|
imageVector = Icons.Filled.Refresh,
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
<string name="settings_appearance_description">Theme, dynamic colors</string>
|
<string name="settings_appearance_description">Theme, dynamic colors</string>
|
||||||
<string name="settings_location">Location</string>
|
<string name="settings_location">Location</string>
|
||||||
<string name="settings_location_description">Location to fetch data from</string>
|
<string name="settings_location_description">Location to fetch data from</string>
|
||||||
|
<string name="settings_units">Units</string>
|
||||||
|
<string name="settings_units_description">Units to fetch data in, e.g. imperial/metric</string>
|
||||||
|
|
||||||
<string name="location">Location</string>
|
<string name="location">Location</string>
|
||||||
<string name="locations">Locations</string>
|
<string name="locations">Locations</string>
|
||||||
|
@ -34,9 +36,33 @@
|
||||||
<string name="theme_light">Light</string>
|
<string name="theme_light">Light</string>
|
||||||
<string name="theme_dark">Dark</string>
|
<string name="theme_dark">Dark</string>
|
||||||
|
|
||||||
<string name="weather_high">High: %1$d°C</string>
|
|
||||||
<string name="weather_low">Low: %1$d°C</string>
|
<string name="unit_temp">Temperature Unit</string>
|
||||||
|
<string name="celsius">Celsius (C°)</string>
|
||||||
|
<string name="fahrenheit">Fahrenheit (F°)</string>
|
||||||
|
<string name="unit_temp_desc">Celsius, Fahrenheit</string>
|
||||||
|
|
||||||
|
<string name="unit_wind">Wind Speed Unit</string>
|
||||||
|
<string name="kmh">Kilometers per Hour (km/h)</string>
|
||||||
|
<string name="ms">Meters per second (m/s)</string>
|
||||||
|
<string name="mph">Miles per hour (mph)</string>
|
||||||
|
<string name="kn">Knots (kn)</string>
|
||||||
|
<string name="unit_wind_desc">Km/h, m/s, Mph, Knots</string>
|
||||||
|
|
||||||
|
<string name="unit_precipitation">Precipitation Unit</string>
|
||||||
|
<string name="mm">Millimeters (mm)</string>
|
||||||
|
<string name="inch">Inches (in)</string>
|
||||||
|
<string name="unit_precipitation_desc">Millimeters, Inches</string>
|
||||||
|
|
||||||
|
<string name="weather_high">High: %1$d%2$s</string>
|
||||||
|
<string name="weather_low">Low: %1$d%2$s</string>
|
||||||
|
<string name="feels_like">Feels like %1$d%2$s</string>
|
||||||
<string name="weather_precipitation">Precipitation: %1$d﹪</string>
|
<string name="weather_precipitation">Precipitation: %1$d﹪</string>
|
||||||
|
<string name="weather_description">%1$s - Feels like %2$d%3$s</string>
|
||||||
|
|
||||||
|
<string name="precipitation_probability">Precipitation Probability</string>
|
||||||
|
<string name="precipitation_amount">Precipitation Amount</string>
|
||||||
|
<string name="wind_speed">Wind Speed</string>
|
||||||
|
|
||||||
<string name="today_in">Today in %1$s</string>
|
<string name="today_in">Today in %1$s</string>
|
||||||
|
|
||||||
|
|
Reference in a new issue