* fix #23: Show date of weather data in weather forecast, main menu * Use localized date format
This commit is contained in:
parent
a948ed4561
commit
69626dce67
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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...")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user