fix #23: Show date of weather data in weather forecast, main menu (#24)

* fix #23: Show date of weather data in weather forecast, main menu

* Use localized date format
This commit is contained in:
timklge 2025-01-17 18:01:02 +01:00 committed by GitHub
parent a948ed4561
commit 69626dce67
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 59 additions and 19 deletions

View File

@ -39,6 +39,7 @@ import kotlinx.coroutines.launch
import java.time.Instant import java.time.Instant
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import kotlin.math.roundToInt import kotlin.math.roundToInt
@OptIn(ExperimentalGlanceRemoteViewsApi::class) @OptIn(ExperimentalGlanceRemoteViewsApi::class)
@ -99,6 +100,8 @@ class WeatherDataType(
val interpretation = WeatherInterpretation.fromWeatherCode(data.current.weatherCode) val interpretation = WeatherInterpretation.fromWeatherCode(data.current.weatherCode)
val formattedTime = timeFormatter.format(Instant.ofEpochSecond(data.current.time)) val formattedTime = timeFormatter.format(Instant.ofEpochSecond(data.current.time))
val formattedDate = Instant.ofEpochSecond(data.current.time).atZone(ZoneId.systemDefault()).toLocalDate().format(DateTimeFormatter.ofLocalizedDate(
FormatStyle.SHORT))
val result = glance.compose(context, DpSize.Unspecified) { val result = glance.compose(context, DpSize.Unspecified) {
Box(modifier = GlanceModifier.fillMaxSize(), contentAlignment = Alignment.CenterEnd) { Box(modifier = GlanceModifier.fillMaxSize(), contentAlignment = Alignment.CenterEnd) {
@ -114,11 +117,13 @@ class WeatherDataType(
temperature = data.current.temperature.roundToInt(), temperature = data.current.temperature.roundToInt(),
temperatureUnit = if (userProfile?.preferredUnit?.temperature != UserProfile.PreferredUnit.UnitType.IMPERIAL) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT, temperatureUnit = if (userProfile?.preferredUnit?.temperature != UserProfile.PreferredUnit.UnitType.IMPERIAL) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT,
timeLabel = formattedTime, timeLabel = formattedTime,
dateLabel = formattedDate,
rowAlignment = when (config.alignment){ rowAlignment = when (config.alignment){
ViewConfig.Alignment.LEFT -> Alignment.Horizontal.Start ViewConfig.Alignment.LEFT -> Alignment.Horizontal.Start
ViewConfig.Alignment.CENTER -> Alignment.Horizontal.CenterHorizontally ViewConfig.Alignment.CENTER -> Alignment.Horizontal.CenterHorizontally
ViewConfig.Alignment.RIGHT -> Alignment.Horizontal.End ViewConfig.Alignment.RIGHT -> Alignment.Horizontal.End
} },
singleDisplay = true
) )
} }
} }

View File

@ -55,6 +55,7 @@ import kotlinx.coroutines.launch
import java.time.Instant import java.time.Instant
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import kotlin.math.roundToInt import kotlin.math.roundToInt
class CycleHoursAction : ActionCallback { class CycleHoursAction : ActionCallback {
@ -139,6 +140,13 @@ class WeatherForecastDataType(
Row(modifier = GlanceModifier.fillMaxSize().clickable(onClick = actionRunCallback<CycleHoursAction>()), horizontalAlignment = Alignment.Horizontal.CenterHorizontally) { Row(modifier = GlanceModifier.fillMaxSize().clickable(onClick = actionRunCallback<CycleHoursAction>()), horizontalAlignment = Alignment.Horizontal.CenterHorizontally) {
val hourOffset = widgetSettings?.currentForecastHourOffset ?: 0 val hourOffset = widgetSettings?.currentForecastHourOffset ?: 0
var previousDate: String? = let {
val unixTime = data.forecastData.time.firstOrNull()
val formattedDate = unixTime?.let { Instant.ofEpochSecond(it).atZone(ZoneId.systemDefault()).toLocalDate().toString() }
formattedDate
}
for (index in hourOffset..hourOffset + 2){ for (index in hourOffset..hourOffset + 2){
if (index >= data.forecastData.weatherCode.size) { if (index >= data.forecastData.weatherCode.size) {
break break
@ -155,6 +163,8 @@ class WeatherForecastDataType(
val interpretation = WeatherInterpretation.fromWeatherCode(data.forecastData.weatherCode[index]) val interpretation = WeatherInterpretation.fromWeatherCode(data.forecastData.weatherCode[index])
val unixTime = data.forecastData.time[index] val unixTime = data.forecastData.time[index]
val formattedTime = timeFormatter.format(Instant.ofEpochSecond(unixTime)) val formattedTime = timeFormatter.format(Instant.ofEpochSecond(unixTime))
val formattedDate = Instant.ofEpochSecond(unixTime).atZone(ZoneId.systemDefault()).toLocalDate().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT))
val hasNewDate = formattedDate != previousDate || index == 0
Weather(baseBitmap, Weather(baseBitmap,
current = interpretation, current = interpretation,
@ -167,8 +177,11 @@ class WeatherForecastDataType(
precipitationUnit = if (userProfile?.preferredUnit?.distance != UserProfile.PreferredUnit.UnitType.IMPERIAL) PrecipitationUnit.MILLIMETERS else PrecipitationUnit.INCH, precipitationUnit = if (userProfile?.preferredUnit?.distance != UserProfile.PreferredUnit.UnitType.IMPERIAL) PrecipitationUnit.MILLIMETERS else PrecipitationUnit.INCH,
temperature = data.forecastData.temperature[index].roundToInt(), temperature = data.forecastData.temperature[index].roundToInt(),
temperatureUnit = if (userProfile?.preferredUnit?.temperature != UserProfile.PreferredUnit.UnitType.IMPERIAL) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT, temperatureUnit = if (userProfile?.preferredUnit?.temperature != UserProfile.PreferredUnit.UnitType.IMPERIAL) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT,
timeLabel = formattedTime timeLabel = formattedTime,
dateLabel = if (hasNewDate) formattedDate else null
) )
previousDate = formattedDate
} }
} }
} }

View File

@ -17,9 +17,12 @@ import androidx.glance.layout.ContentScale
import androidx.glance.layout.Row import androidx.glance.layout.Row
import androidx.glance.layout.Spacer import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxHeight import androidx.glance.layout.fillMaxHeight
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.height import androidx.glance.layout.height
import androidx.glance.layout.padding import androidx.glance.layout.padding
import androidx.glance.layout.width import androidx.glance.layout.width
import androidx.glance.layout.wrapContentWidth
import androidx.glance.preview.ExperimentalGlancePreviewApi import androidx.glance.preview.ExperimentalGlancePreviewApi
import androidx.glance.preview.Preview import androidx.glance.preview.Preview
import androidx.glance.text.FontFamily import androidx.glance.text.FontFamily
@ -51,14 +54,15 @@ fun getWeatherIcon(interpretation: WeatherInterpretation): Int {
@Composable @Composable
fun Weather(baseBitmap: Bitmap, current: WeatherInterpretation, windBearing: Int, windSpeed: Int, windGusts: Int, windSpeedUnit: WindUnit, fun Weather(baseBitmap: Bitmap, current: WeatherInterpretation, windBearing: Int, windSpeed: Int, windGusts: Int, windSpeedUnit: WindUnit,
precipitation: Double, precipitationProbability: Int?, precipitationUnit: PrecipitationUnit, precipitation: Double, precipitationProbability: Int?, precipitationUnit: PrecipitationUnit,
temperature: Int, temperatureUnit: TemperatureUnit, timeLabel: String? = null, rowAlignment: Alignment.Horizontal = Alignment.Horizontal.CenterHorizontally) { temperature: Int, temperatureUnit: TemperatureUnit, timeLabel: String? = null, rowAlignment: Alignment.Horizontal = Alignment.Horizontal.CenterHorizontally,
dateLabel: String? = null, singleDisplay: Boolean = false) {
val fontSize = 14f val fontSize = 14f
Column(modifier = GlanceModifier.fillMaxHeight().padding(2.dp).width(85.dp), horizontalAlignment = rowAlignment) { Column(modifier = if (singleDisplay) GlanceModifier.fillMaxSize().padding(1.dp) else GlanceModifier.fillMaxHeight().padding(1.dp).width(86.dp), horizontalAlignment = rowAlignment) {
Row(modifier = GlanceModifier.defaultWeight(), horizontalAlignment = rowAlignment, verticalAlignment = Alignment.CenterVertically) { Row(modifier = GlanceModifier.defaultWeight().wrapContentWidth(), horizontalAlignment = rowAlignment, verticalAlignment = Alignment.CenterVertically) {
Image( Image(
modifier = GlanceModifier.defaultWeight(), modifier = GlanceModifier.defaultWeight().wrapContentWidth().padding(1.dp),
provider = ImageProvider(getWeatherIcon(current)), provider = ImageProvider(getWeatherIcon(current)),
contentDescription = "Current weather information", contentDescription = "Current weather information",
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
@ -66,6 +70,19 @@ fun Weather(baseBitmap: Bitmap, current: WeatherInterpretation, windBearing: Int
) )
} }
if (dateLabel != null && !singleDisplay){
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = dateLabel,
style = TextStyle(
color = ColorProvider(Color.Black, Color.White),
fontFamily = FontFamily.Monospace,
fontSize = TextUnit(fontSize, TextUnitType.Sp)
)
)
}
}
Row(verticalAlignment = Alignment.CenterVertically, horizontalAlignment = rowAlignment) { Row(verticalAlignment = Alignment.CenterVertically, horizontalAlignment = rowAlignment) {
if (timeLabel != null){ if (timeLabel != null){
Text( Text(
@ -78,7 +95,7 @@ fun Weather(baseBitmap: Bitmap, current: WeatherInterpretation, windBearing: Int
} }
Image( Image(
modifier = GlanceModifier.height(20.dp).width(12.dp), modifier = GlanceModifier.height(16.dp).width(12.dp).padding(1.dp),
provider = ImageProvider(R.drawable.thermometer), provider = ImageProvider(R.drawable.thermometer),
contentDescription = "Temperature", contentDescription = "Temperature",
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
@ -91,14 +108,16 @@ fun Weather(baseBitmap: Bitmap, current: WeatherInterpretation, windBearing: Int
) )
} }
Row(verticalAlignment = Alignment.CenterVertically, horizontalAlignment = rowAlignment) { Row(verticalAlignment = Alignment.CenterVertically, horizontalAlignment = rowAlignment, modifier = GlanceModifier.fillMaxWidth()) {
/* Image( if (dateLabel != null && singleDisplay){
modifier = GlanceModifier.height(20.dp).width(12.dp), Text(
provider = ImageProvider(R.drawable.water_regular), text = "$dateLabel",
contentDescription = "Rain", style = TextStyle(color = ColorProvider(Color.Black, Color.White),
contentScale = ContentScale.Fit, fontFamily = FontFamily.Monospace, fontSize = TextUnit(fontSize, TextUnitType.Sp))
colorFilter = ColorFilter.tint(ColorProvider(Color.Black, Color.White)) )
) */
Spacer(modifier = GlanceModifier.width(5.dp))
}
val precipitationProbabilityLabel = if (precipitationProbability != null) "${precipitationProbability}%," else "" val precipitationProbabilityLabel = if (precipitationProbability != null) "${precipitationProbability}%," else ""
Text( Text(
@ -109,7 +128,7 @@ fun Weather(baseBitmap: Bitmap, current: WeatherInterpretation, windBearing: Int
Spacer(modifier = GlanceModifier.width(5.dp)) Spacer(modifier = GlanceModifier.width(5.dp))
Image( Image(
modifier = GlanceModifier.height(20.dp).width(12.dp), modifier = GlanceModifier.height(16.dp).width(12.dp).padding(1.dp),
provider = ImageProvider(getArrowBitmapByBearing(baseBitmap, windBearing + 180)), provider = ImageProvider(getArrowBitmapByBearing(baseBitmap, windBearing + 180)),
contentDescription = "Current wind direction", contentDescription = "Current wind direction",
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,

View File

@ -222,16 +222,19 @@ fun MainScreen() {
if (stats.failedWeatherRequest != null && (stats.lastSuccessfulWeatherRequest == null || stats.failedWeatherRequest!! > stats.lastSuccessfulWeatherRequest!!)){ if (stats.failedWeatherRequest != null && (stats.lastSuccessfulWeatherRequest == null || stats.failedWeatherRequest!! > stats.lastSuccessfulWeatherRequest!!)){
val successfulTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(stats.lastSuccessfulWeatherRequest ?: 0), ZoneOffset.systemDefault()).toLocalTime().truncatedTo( val successfulTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(stats.lastSuccessfulWeatherRequest ?: 0), ZoneOffset.systemDefault()).toLocalTime().truncatedTo(
ChronoUnit.SECONDS) ChronoUnit.SECONDS)
val successfulDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(stats.lastSuccessfulWeatherRequest ?: 0), ZoneOffset.systemDefault()).toLocalDate()
val lastTryTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(stats.failedWeatherRequest ?: 0), ZoneOffset.systemDefault()).toLocalTime().truncatedTo( val lastTryTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(stats.failedWeatherRequest ?: 0), ZoneOffset.systemDefault()).toLocalTime().truncatedTo(
ChronoUnit.SECONDS) ChronoUnit.SECONDS)
val lastTryDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(stats.failedWeatherRequest ?: 0), ZoneOffset.systemDefault()).toLocalDate()
val successStr = if(lastPosition != null) " Last data received at ${successfulTime}${lastPositionDistanceStr}." else "" val successStr = if(lastPosition != null) " Last data received at $successfulDate ${successfulTime}${lastPositionDistanceStr}." else ""
Text(modifier = Modifier.padding(5.dp), text = "Failed to update weather data; last try at ${lastTryTime}.${successStr}") Text(modifier = Modifier.padding(5.dp), text = "Failed to update weather data; last try at $lastTryDate ${lastTryTime}.${successStr}")
} else if(stats.lastSuccessfulWeatherRequest != null){ } else if(stats.lastSuccessfulWeatherRequest != null){
val localDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(stats.lastSuccessfulWeatherRequest ?: 0), ZoneOffset.systemDefault()).toLocalDate()
val localTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(stats.lastSuccessfulWeatherRequest ?: 0), ZoneOffset.systemDefault()).toLocalTime().truncatedTo( val localTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(stats.lastSuccessfulWeatherRequest ?: 0), ZoneOffset.systemDefault()).toLocalTime().truncatedTo(
ChronoUnit.SECONDS) ChronoUnit.SECONDS)
Text(modifier = Modifier.padding(5.dp), text = "Last weather data received at ${localTime}${lastPositionDistanceStr}") Text(modifier = Modifier.padding(5.dp), text = "Last weather data received at $localDate ${localTime}${lastPositionDistanceStr}")
} else { } else {
Text(modifier = Modifier.padding(5.dp), text = "No weather data received yet, waiting for GPS fix...") Text(modifier = Modifier.padding(5.dp), text = "No weather data received yet, waiting for GPS fix...")
} }