From bc39b8512f7451751d322a33b433f2db51ddc0ee Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Thu, 16 Jul 2026 10:34:38 +0000 Subject: Show scheduled/available badge on widget TODAY header Threads the /api/widget budget_status field through the Android widget's DataStore-backed cache (a new BUDGET_STATUS_JSON pref, since the widget decomposes WidgetResponse into individual prefs rather than caching it whole) and renders a small "Nm/Nm" badge next to TODAY when there's a nonzero tracked load. --- .../main/java/org/terst/doot/widget/data/DataStore.kt | 1 + .../java/org/terst/doot/widget/data/WidgetItem.kt | 15 ++++++++++++++- .../org/terst/doot/widget/data/WidgetRepository.kt | 5 +++++ .../main/java/org/terst/doot/widget/ui/DootWidget.kt | 19 +++++++++++++++++-- 4 files changed, 37 insertions(+), 3 deletions(-) (limited to 'android') diff --git a/android/app/src/main/java/org/terst/doot/widget/data/DataStore.kt b/android/app/src/main/java/org/terst/doot/widget/data/DataStore.kt index 7153cf8..23ee566 100644 --- a/android/app/src/main/java/org/terst/doot/widget/data/DataStore.kt +++ b/android/app/src/main/java/org/terst/doot/widget/data/DataStore.kt @@ -18,4 +18,5 @@ object Keys { val LAST_UPDATED = longPreferencesKey("last_updated") val IS_REFRESHING = booleanPreferencesKey("is_refreshing") val TEXT_SIZE = stringPreferencesKey("text_size") + val BUDGET_STATUS_JSON = stringPreferencesKey("budget_status_json") } diff --git a/android/app/src/main/java/org/terst/doot/widget/data/WidgetItem.kt b/android/app/src/main/java/org/terst/doot/widget/data/WidgetItem.kt index 92598ba..ed1bceb 100644 --- a/android/app/src/main/java/org/terst/doot/widget/data/WidgetItem.kt +++ b/android/app/src/main/java/org/terst/doot/widget/data/WidgetItem.kt @@ -20,10 +20,23 @@ data class WidgetItem( @SerialName("project_color") val projectColor: String? = null ) +@Serializable +data class BudgetPeriod( + @SerialName("scheduled_minutes") val scheduledMinutes: Int, + @SerialName("available_minutes") val availableMinutes: Int +) + +@Serializable +data class BudgetStatus( + val today: BudgetPeriod, + val week: BudgetPeriod +) + @Serializable data class WidgetResponse( val now: String, - val items: List + val items: List, + @SerialName("budget_status") val budgetStatus: BudgetStatus? = null ) @Serializable diff --git a/android/app/src/main/java/org/terst/doot/widget/data/WidgetRepository.kt b/android/app/src/main/java/org/terst/doot/widget/data/WidgetRepository.kt index 16c76f2..4aab37c 100644 --- a/android/app/src/main/java/org/terst/doot/widget/data/WidgetRepository.kt +++ b/android/app/src/main/java/org/terst/doot/widget/data/WidgetRepository.kt @@ -58,6 +58,11 @@ class WidgetRepository( prefs[Keys.ITEMS_JSON] = json.encodeToString(resp.items) prefs[Keys.NOW] = resp.now prefs[Keys.LAST_UPDATED] = System.currentTimeMillis() + if (resp.budgetStatus != null) { + prefs[Keys.BUDGET_STATUS_JSON] = json.encodeToString(resp.budgetStatus) + } else { + prefs.remove(Keys.BUDGET_STATUS_JSON) + } } } } diff --git a/android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt b/android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt index 4a134b8..9f0a555 100644 --- a/android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt +++ b/android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt @@ -34,9 +34,12 @@ class DootWidget : GlanceAppWidget() { ?: Instant.now() val isRefreshing = prefs[Keys.IS_REFRESHING] ?: false val textSize = WidgetTextSize.fromPref(prefs[Keys.TEXT_SIZE]) + val budgetStatus = prefs[Keys.BUDGET_STATUS_JSON]?.let { + runCatching { json.decodeFromString(it) }.getOrNull() + } provideContent { - WidgetRoot(items, now, isRefreshing, textSize) + WidgetRoot(items, now, isRefreshing, textSize, budgetStatus) } } @@ -47,7 +50,7 @@ class DootWidget : GlanceAppWidget() { } @Composable -fun WidgetRoot(items: List, now: Instant, isRefreshing: Boolean, textSize: WidgetTextSize) { +fun WidgetRoot(items: List, now: Instant, isRefreshing: Boolean, textSize: WidgetTextSize, budgetStatus: BudgetStatus? = null) { val zone = ZoneId.systemDefault() val nowZoned: ZonedDateTime = now.atZone(zone) val todayDate: LocalDate = nowZoned.toLocalDate() @@ -131,6 +134,18 @@ fun WidgetRoot(items: List, now: Instant, isRefreshing: Boolean, tex ), modifier = GlanceModifier.defaultWeight() ) + budgetStatus?.today?.let { today -> + if (today.scheduledMinutes > 0) { + Text( + "${today.scheduledMinutes}m/${today.availableMinutes}m", + style = TextStyle( + color = ColorProvider(Color(0x99FFFFFF)), + fontSize = textSize.scaledHeaderSize(10) + ), + modifier = GlanceModifier.padding(end = 4.dp) + ) + } + } QuickAddButton() Spacer(modifier = GlanceModifier.width(4.dp)) RefreshButton(isRefreshing) -- cgit v1.2.3