Today's weather card shows the next few hours beside the weather now

This commit is contained in:
Seb3thehacker committed 2026-11-06 12:04:28 -08:00
1 parent 59a2d38480
commit 542dd6751f
1 file changed
+53 -2
@@ -14,6 +14,10 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
@@ -94,19 +98,66 @@ private fun WeatherCard(context: Context) {
is WeatherState.Ready -> {
val w = s.data
WeatherIcon(w.code, w.isDay, size = 44.dp)
Column {
Column(Modifier.weight(1f)) {
Text(
"${w.temp}${w.tempUnit} · ${Weather.condition(w.code)}",
style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.SemiBold,
maxLines = 1, overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
)
Text(w.place, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant)
Text(w.place, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, maxLines = 1)
}
NextHours(w.hours, Modifier.weight(1.3f))
}
}
}
}
}
/**
* The next few hours beside the weather now, in the same card, so a glance answers "will it rain
* on the way" without opening Weather. As many as fit, up to five; none if fewer than three would.
*/
@Composable
private fun NextHours(all: List<HourForecast>, modifier: Modifier) {
val now = System.currentTimeMillis()
// The forecast is cached, so its first hours may already be past.
val hours = all.filter { it.time > now }
androidx.compose.foundation.layout.BoxWithConstraints(modifier) {
val fit = (maxWidth / HOUR_WIDTH).toInt().coerceAtMost(5)
if (fit < 3 || hours.size < 3) return@BoxWithConstraints
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Box(Modifier.width(1.dp).height(64.dp).background(MaterialTheme.colorScheme.outlineVariant))
hours.take(fit).forEach { h ->
Column(
Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
Text(
DateFormat.getTimeInstance(DateFormat.SHORT).format(Date(h.time)).replace(":00", ""),
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
)
WeatherIcon(h.code, h.isDay, size = 24.dp)
Text("${h.temp}°", style = MaterialTheme.typography.titleSmall, fontWeight = FontWeight.SemiBold)
// Kept as a blank line when dry, so every column lines up.
Text(
if (h.rainChance >= RAIN_SHOWN) "${h.rainChance}%" else " ",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.primary,
)
}
}
}
}
}
private val HOUR_WIDTH = 64.dp
/** Below this a chance of rain is noise on a glance card; the Weather screen still shows it. */
private const val RAIN_SHOWN = 20
@Composable
private fun AgendaCard(context: Context) {
val events by produceState<List<AgendaEvent>?>(cachedAgenda) {