diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-07-16 10:34:38 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-07-16 10:34:38 +0000 |
| commit | bc39b8512f7451751d322a33b433f2db51ddc0ee (patch) | |
| tree | 192996f7f873f8cd2be32ef280df0c40a9991f12 | |
| parent | 6651dc17110a558342a4ef0a97a5b37adbfa04c7 (diff) | |
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.
4 files changed, 37 insertions, 3 deletions
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 @@ -21,9 +21,22 @@ data class WidgetItem( ) @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<WidgetItem> + val items: List<WidgetItem>, + @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<BudgetStatus>(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<WidgetItem>, now: Instant, isRefreshing: Boolean, textSize: WidgetTextSize) { +fun WidgetRoot(items: List<WidgetItem>, 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<WidgetItem>, 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) |
