Crop timespan to 6 hours in forecast views, use am / pm time format if appropriate (#144)
Some checks failed
Build / build (push) Has been cancelled
Some checks failed
Build / build (push) Has been cancelled
* Crop timespan to 6 hours in forecast views, use am / pm time format if appropriate * Also crop timeframe in hourly forecast widget
This commit is contained in:
parent
4952d8fbf4
commit
ef5e980de3
@ -5,6 +5,7 @@ import android.util.Log
|
||||
import de.timklge.karooheadwind.datatypes.GpsCoordinates
|
||||
import de.timklge.karooheadwind.util.signedAngleDifference
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import kotlinx.coroutines.awaitCancellation
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@ -90,7 +91,8 @@ suspend fun KarooSystemService.updateLastKnownGps(context: Context) {
|
||||
|
||||
fun KarooSystemService.getGpsCoordinateFlow(context: Context): Flow<GpsCoordinates?> {
|
||||
/* return flow {
|
||||
emit(GpsCoordinates(52.5164069,13.3784))
|
||||
// emit(GpsCoordinates(52.5164069,13.3784))
|
||||
emit(GpsCoordinates(32.46,-111.524))
|
||||
awaitCancellation()
|
||||
} */
|
||||
|
||||
|
||||
@ -25,9 +25,9 @@ class CycleHoursAction : ActionCallback {
|
||||
|
||||
var hourOffset = currentSettings.currentForecastHourOffset + 3
|
||||
val requestedPositions = forecastData?.data?.size
|
||||
val requestedHours = forecastData?.data?.firstOrNull()?.forecasts?.size
|
||||
val requestedHours = forecastData?.data?.firstOrNull()?.forecasts?.size?.coerceAtMost(6)
|
||||
|
||||
if (forecastData == null || requestedHours == null || requestedPositions == null || hourOffset >= requestedHours || (requestedPositions in 2..hourOffset)) {
|
||||
if (forecastData == null || requestedHours == null || requestedPositions == null || (requestedPositions == 1 && hourOffset >= requestedHours) || (requestedPositions in 2..hourOffset)) {
|
||||
hourOffset = 0
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@ import de.timklge.karooheadwind.streamUserProfile
|
||||
import de.timklge.karooheadwind.streamWidgetSettings
|
||||
import de.timklge.karooheadwind.throttle
|
||||
import de.timklge.karooheadwind.util.celciusInUserUnit
|
||||
import de.timklge.karooheadwind.util.getTimeFormatter
|
||||
import de.timklge.karooheadwind.util.millimetersInUserUnit
|
||||
import de.timklge.karooheadwind.util.msInUserUnit
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
@ -89,10 +90,6 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
|
||||
@OptIn(ExperimentalGlanceRemoteViewsApi::class)
|
||||
private val glance = GlanceRemoteViews()
|
||||
|
||||
companion object {
|
||||
val timeFormatter = DateTimeFormatter.ofPattern("HH:mm").withZone(ZoneId.systemDefault())
|
||||
}
|
||||
|
||||
data class StreamData(val data: WeatherDataResponse?, val settings: SettingsAndProfile,
|
||||
val widgetSettings: HeadwindWidgetSettings? = null,
|
||||
val headingResponse: HeadingResponse? = null, val upcomingRoute: UpcomingRoute? = null, val isVisible: Boolean)
|
||||
@ -304,13 +301,22 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
|
||||
|
||||
val isCurrent = baseIndex == 0 && positionIndex == 0
|
||||
|
||||
val time = if (isCurrent && data?.current != null) {
|
||||
Instant.ofEpochSecond(data.current.time)
|
||||
} else {
|
||||
Instant.ofEpochSecond(data?.forecasts?.getOrNull(baseIndex)?.time ?: 0)
|
||||
}
|
||||
|
||||
if (time.isBefore(Instant.now().minus(1, ChronoUnit.HOURS)) || (upcomingRoute == null && time.isAfter(Instant.now().plus(6, ChronoUnit.HOURS)))) {
|
||||
Log.d(KarooHeadwindExtension.TAG, "Skipping forecast data for time $time as it is in the past or too close to now")
|
||||
continue
|
||||
}
|
||||
|
||||
if (isCurrent && data?.current != null) {
|
||||
val interpretation = WeatherInterpretation.fromWeatherCode(data.current.weatherCode)
|
||||
val unixTime = data.current.time
|
||||
val formattedTime =
|
||||
timeFormatter.format(Instant.ofEpochSecond(unixTime))
|
||||
val formattedDate =
|
||||
getShortDateFormatter().format(Instant.ofEpochSecond(unixTime))
|
||||
val formattedTime = getTimeFormatter(context).format(Instant.ofEpochSecond(unixTime).atZone(ZoneId.systemDefault()).toLocalTime())
|
||||
val formattedDate = getShortDateFormatter().format(Instant.ofEpochSecond(unixTime).atZone(ZoneId.systemDefault()))
|
||||
val hasNewDate = formattedDate != previousDate || baseIndex == 0
|
||||
|
||||
RenderWidget(
|
||||
@ -335,7 +341,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
|
||||
val weatherData = data?.forecasts?.getOrNull(baseIndex)
|
||||
val interpretation = WeatherInterpretation.fromWeatherCode(weatherData?.weatherCode ?: 0)
|
||||
val unixTime = data?.forecasts?.getOrNull(baseIndex)?.time ?: 0
|
||||
val formattedTime = timeFormatter.format(Instant.ofEpochSecond(unixTime))
|
||||
val formattedTime = getTimeFormatter(context).format(Instant.ofEpochSecond(unixTime).atZone(ZoneId.systemDefault()).toLocalTime())
|
||||
val formattedDate = getShortDateFormatter().format(Instant.ofEpochSecond(unixTime))
|
||||
val hasNewDate = formattedDate != previousDate || baseIndex == 0
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ import de.timklge.karooheadwind.streamUpcomingRoute
|
||||
import de.timklge.karooheadwind.streamUserProfile
|
||||
import de.timklge.karooheadwind.streamWidgetSettings
|
||||
import de.timklge.karooheadwind.throttle
|
||||
import de.timklge.karooheadwind.util.getTimeFormatter
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherDataForLocation
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherDataResponse
|
||||
@ -49,7 +50,6 @@ import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.launch
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.temporal.ChronoUnit
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.ceil
|
||||
@ -59,10 +59,6 @@ abstract class LineGraphForecastDataType(private val karooSystem: KarooSystemSer
|
||||
@OptIn(ExperimentalGlanceRemoteViewsApi::class)
|
||||
private val glance = GlanceRemoteViews()
|
||||
|
||||
companion object {
|
||||
val timeFormatter = DateTimeFormatter.ofPattern("HH:mm").withZone(ZoneId.of("UTC"))
|
||||
}
|
||||
|
||||
data class StreamData(val data: WeatherDataResponse?, val settings: SettingsAndProfile,
|
||||
val widgetSettings: HeadwindWidgetSettings? = null,
|
||||
val headingResponse: HeadingResponse? = null, val upcomingRoute: UpcomingRoute? = null, val isVisible: Boolean)
|
||||
@ -252,6 +248,11 @@ abstract class LineGraphForecastDataType(private val karooSystem: KarooSystemSer
|
||||
|
||||
val time = Instant.ofEpochSecond(data.time)
|
||||
|
||||
if (time.isBefore(Instant.now().minus(1, ChronoUnit.HOURS)) || (locationData?.coords?.distanceAlongRoute == null && time.isAfter(Instant.now().plus(6, ChronoUnit.HOURS)))) {
|
||||
Log.d(KarooHeadwindExtension.TAG, "Skipping forecast data for time $time as it is in the past or too close to now")
|
||||
continue
|
||||
}
|
||||
|
||||
add(LineData(
|
||||
time = time,
|
||||
distance = locationData?.coords?.distanceAlongRoute?.toFloat(),
|
||||
@ -264,7 +265,7 @@ abstract class LineGraphForecastDataType(private val karooSystem: KarooSystemSer
|
||||
val bitmap = LineGraphBuilder(context).drawLineGraph(config.viewSize.first, config.viewSize.second, config.gridSize.first, config.gridSize.second, pointData) { x ->
|
||||
val startTime = data.firstOrNull()?.time
|
||||
val time = startTime?.plus(floor(x).toLong(), ChronoUnit.HOURS)
|
||||
val timeLabel = timeFormatter.format(time)
|
||||
val timeLabel = getTimeFormatter(context).format(time?.atZone(ZoneId.systemDefault())?.toLocalTime())
|
||||
val beforeData = data.getOrNull(floor(x).toInt().coerceAtLeast(0))
|
||||
val afterData = data.getOrNull(ceil(x).toInt().coerceAtMost(data.size - 1))
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ import android.graphics.Path
|
||||
import androidx.annotation.ColorInt
|
||||
import kotlin.math.abs
|
||||
import androidx.core.graphics.createBitmap
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class LineGraphBuilder(val context: Context) {
|
||||
enum class YAxis {
|
||||
@ -120,26 +121,18 @@ class LineGraphBuilder(val context: Context) {
|
||||
val yLabelStringsLeft = mutableListOf<String>()
|
||||
val numYTicksForCalc = 2 // As used later for drawing Y-axis ticks
|
||||
|
||||
val minRange = numYTicksForCalc.toFloat()
|
||||
if (dataMaxYLeft - dataMinYLeft < minRange) {
|
||||
dataMaxYLeft += minRange - (dataMaxYLeft - dataMinYLeft)
|
||||
}
|
||||
|
||||
// Determine Y-axis label strings (mirrors logic from where labels are drawn)
|
||||
if (abs(dataMaxYLeft - dataMinYLeft) < 0.0001f) {
|
||||
yLabelStringsLeft.add(
|
||||
String.format(
|
||||
java.util.Locale.getDefault(),
|
||||
"%.0f",
|
||||
dataMinYLeft
|
||||
)
|
||||
)
|
||||
yLabelStringsLeft.add(dataMinYLeft.roundToInt().toString())
|
||||
} else {
|
||||
for (i in 0..numYTicksForCalc) {
|
||||
val value =
|
||||
dataMinYLeft + ((dataMaxYLeft - dataMinYLeft) / numYTicksForCalc) * i
|
||||
yLabelStringsLeft.add(
|
||||
String.format(
|
||||
java.util.Locale.getDefault(),
|
||||
"%.0f",
|
||||
value
|
||||
)
|
||||
)
|
||||
val value = dataMinYLeft + ((dataMaxYLeft - dataMinYLeft) / numYTicksForCalc) * i
|
||||
yLabelStringsLeft.add(value.roundToInt().toString())
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,25 +154,18 @@ class LineGraphBuilder(val context: Context) {
|
||||
val yLabelStringsRight = mutableListOf<String>()
|
||||
val numYTicksForCalc = 2 // As used later for drawing Y-axis ticks
|
||||
|
||||
// Adjust Y-axis range based on numYTicksForCalc.
|
||||
val minRange = numYTicksForCalc.toFloat()
|
||||
if (dataMaxYRight - dataMinYRight < minRange) {
|
||||
dataMaxYRight += minRange - (dataMaxYRight - dataMinYRight)
|
||||
}
|
||||
|
||||
if (abs(dataMaxYRight - dataMinYRight) < 0.0001f) {
|
||||
yLabelStringsRight.add(
|
||||
String.format(
|
||||
java.util.Locale.getDefault(),
|
||||
"%.0f",
|
||||
dataMinYRight
|
||||
)
|
||||
)
|
||||
yLabelStringsRight.add(dataMinYRight.roundToInt().toString())
|
||||
} else {
|
||||
for (i in 0..numYTicksForCalc) {
|
||||
val value =
|
||||
dataMinYRight + ((dataMaxYRight - dataMinYRight) / numYTicksForCalc) * i
|
||||
yLabelStringsRight.add(
|
||||
String.format(
|
||||
java.util.Locale.getDefault(),
|
||||
"%.0f",
|
||||
value
|
||||
)
|
||||
)
|
||||
val value = dataMinYRight + ((dataMaxYRight - dataMinYRight) / numYTicksForCalc) * i
|
||||
yLabelStringsRight.add(value.roundToInt().toString())
|
||||
}
|
||||
}
|
||||
|
||||
@ -405,7 +391,7 @@ class LineGraphBuilder(val context: Context) {
|
||||
// Draw faint horizontal grid line
|
||||
canvas.drawLine(graphLeft, yPos, graphRight, yPos, gridLinePaint)
|
||||
canvas.drawText(
|
||||
String.format(java.util.Locale.getDefault(), "%.0f", value),
|
||||
value.roundToInt().toString(),
|
||||
graphLeft - 15f,
|
||||
yPos + (textPaint.textSize / 3),
|
||||
textPaint
|
||||
@ -418,7 +404,7 @@ class LineGraphBuilder(val context: Context) {
|
||||
// Draw faint horizontal grid line
|
||||
canvas.drawLine(graphLeft, yPos, graphRight, yPos, gridLinePaint)
|
||||
canvas.drawText(
|
||||
String.format(java.util.Locale.getDefault(), "%.0f", dataMinYLeft),
|
||||
dataMinYLeft.roundToInt().toString(),
|
||||
graphLeft - 15f,
|
||||
yPos + (textPaint.textSize / 3),
|
||||
textPaint
|
||||
@ -445,7 +431,7 @@ class LineGraphBuilder(val context: Context) {
|
||||
// Draw faint horizontal grid line
|
||||
canvas.drawLine(graphLeft, yPos, graphRight, yPos, gridLinePaint)
|
||||
canvas.drawText(
|
||||
String.format(java.util.Locale.getDefault(), "%.0f", value),
|
||||
value.roundToInt().toString(),
|
||||
graphRight + 15f,
|
||||
yPos + (textPaint.textSize / 3),
|
||||
textPaint
|
||||
@ -464,7 +450,7 @@ class LineGraphBuilder(val context: Context) {
|
||||
// Draw faint horizontal grid line
|
||||
canvas.drawLine(graphLeft, yPos, graphRight, yPos, gridLinePaint)
|
||||
canvas.drawText(
|
||||
String.format(java.util.Locale.getDefault(), "%.0f", dataMinYRight),
|
||||
dataMinYRight.roundToInt().toString(),
|
||||
graphRight + 15f,
|
||||
yPos + (textPaint.textSize / 3),
|
||||
textPaint
|
||||
@ -500,7 +486,7 @@ class LineGraphBuilder(val context: Context) {
|
||||
}
|
||||
|
||||
textPaint.textAlign = Align.CENTER
|
||||
val numXTicks = if (gridHeight > 15) 3 else 1
|
||||
val numXTicks = if (gridHeight > 15) 2 else 1
|
||||
if (abs(dataMaxX - dataMinX) > 0.0001f) {
|
||||
for (i in 0..numXTicks) {
|
||||
val value = dataMinX + ((dataMaxX - dataMinX) / numXTicks) * i
|
||||
|
||||
@ -27,7 +27,6 @@ import de.timklge.karooheadwind.HeadwindStats
|
||||
import de.timklge.karooheadwind.R
|
||||
import de.timklge.karooheadwind.ServiceStatusSingleton
|
||||
import de.timklge.karooheadwind.TemperatureUnit
|
||||
import de.timklge.karooheadwind.datatypes.ForecastDataType
|
||||
import de.timklge.karooheadwind.datatypes.getShortDateFormatter
|
||||
import de.timklge.karooheadwind.getGpsCoordinateFlow
|
||||
import de.timklge.karooheadwind.streamCurrentForecastWeatherData
|
||||
@ -36,6 +35,7 @@ import de.timklge.karooheadwind.streamStats
|
||||
import de.timklge.karooheadwind.streamUpcomingRoute
|
||||
import de.timklge.karooheadwind.streamUserProfile
|
||||
import de.timklge.karooheadwind.util.celciusInUserUnit
|
||||
import de.timklge.karooheadwind.util.getTimeFormatter
|
||||
import de.timklge.karooheadwind.util.millimetersInUserUnit
|
||||
import de.timklge.karooheadwind.util.msInUserUnit
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherInterpretation
|
||||
@ -43,6 +43,7 @@ import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.ZoneOffset
|
||||
import java.time.temporal.ChronoUnit
|
||||
import kotlin.math.roundToInt
|
||||
@ -104,7 +105,7 @@ fun WeatherScreen(onFinish: () -> Unit) {
|
||||
|
||||
val requestedWeatherPosition = forecastData?.data?.firstOrNull()?.coords
|
||||
|
||||
val formattedTime = currentWeatherData?.let { ForecastDataType.timeFormatter.format(Instant.ofEpochSecond(it.time)) }
|
||||
val formattedTime = currentWeatherData?.let { getTimeFormatter(ctx).format(Instant.ofEpochSecond(it.time).atZone(ZoneId.systemDefault()).toLocalTime()) }
|
||||
val formattedDate = currentWeatherData?.let { getShortDateFormatter().format(Instant.ofEpochSecond(it.time)) }
|
||||
|
||||
if (karooConnected == true && currentWeatherData != null) {
|
||||
@ -225,7 +226,7 @@ fun WeatherScreen(onFinish: () -> Unit) {
|
||||
val weatherData = data?.forecasts?.getOrNull(index)
|
||||
val interpretation = WeatherInterpretation.fromWeatherCode(weatherData?.weatherCode ?: 0)
|
||||
val unixTime = weatherData?.time ?: 0
|
||||
val formattedForecastTime = ForecastDataType.timeFormatter.format(Instant.ofEpochSecond(unixTime))
|
||||
val formattedForecastTime = getTimeFormatter(ctx).format(Instant.ofEpochSecond(unixTime).atZone(ZoneId.systemDefault()).toLocalTime())
|
||||
val formattedForecastDate = getShortDateFormatter().format(Instant.ofEpochSecond(unixTime))
|
||||
|
||||
WeatherWidget(
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
package de.timklge.karooheadwind.util
|
||||
|
||||
import android.content.Context
|
||||
import android.text.format.DateFormat
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
fun getTimeFormatter(context: Context): DateTimeFormatter {
|
||||
val is24HourFormat = DateFormat.is24HourFormat(context)
|
||||
|
||||
return if (is24HourFormat) {
|
||||
DateTimeFormatter.ofPattern("HH:mm")
|
||||
} else {
|
||||
DateTimeFormatter.ofPattern("h a")
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user