9c9e6009c7
Android UI Tests / ui-tests (pull_request) Has been cancelled
Mirrors the iOS implementation. Adds a Glance configuration activity
that launches when the user pins a new honeyDue widget tile and again
on "Edit Widget", lets them pick one of their residences (or "All
residences"), and persists the choice per-`appWidgetId`. Each tile's
`provideGlance` resolves its own scope and filters tasks (and stats,
on the large widget) accordingly.
Pieces:
- `WidgetConfigActivity` — Compose `ComponentActivity` hosting the
residence-picker UI; reads the persisted residences sidecar, reads
any prior scope for the current `appWidgetId`, writes the new
selection on Save, and re-renders every widget tile.
- `WidgetDataStore` — new `widget_residences_json` key + a per-instance
`widget_residence_id_<appWidgetId>` key. `clearAll()` sweeps the
per-instance keys by prefix so logout doesn't leave dangling state.
- `WidgetDataRepository`:
* `saveResidences(_)` / `loadResidences()` for the picker.
* `saveResidenceIdFor(appWidgetId, residenceId)` /
`loadResidenceIdFor(appWidgetId)` /
`clearResidenceIdFor(appWidgetId)` for per-tile scope.
* `loadTasksForResidence(residenceId)` and the
`appWidgetId`-driven `loadTasksForWidget(appWidgetId)`.
* `computeStatsFromTasks(tasks)` so the large widget's tiles
reflect only the scoped task list (instead of the whole cache).
* Pure `Filter.filterTasksForResidence(_, _)` on the companion
object — easy to exercise from unit tests.
- `WidgetTaskDto` already carries `residenceId`. New `WidgetResidenceDto`
added (id + name) — JSON-persisted via the sidecar.
- `WidgetRefreshWorker` / `DefaultWidgetRefreshDataSource` — pull
`myResidences` alongside tasks/tier on each refresh and write the
sidecar (best-effort; non-fatal if the call fails).
- `HoneyDue{Small,Medium,Large}Widget.provideGlance` — resolve
`appWidgetId` via `GlanceAppWidgetManager(context).getAppWidgetId(id)`
and call `loadTasksForWidget(appWidgetId)`.
- `HoneyDue{Small,Medium,Large}WidgetReceiver.onDeleted` — purge the
per-instance residence scope key when the tile is removed.
- Manifest: register the configure activity with the
`APPWIDGET_CONFIGURE` action.
- `honeydue_{small,medium,large}_widget_info.xml` — declare
`android:configure="com.tt.honeyDue.widget.WidgetConfigActivity"`.
Migration / safety:
- A tile that's never been through the picker has no residence id
saved → `loadTasksForWidget` returns every task (legacy "All
residences" behaviour). Existing tiles keep working without the
user touching anything.
- The picker handles an empty residences list (signed-out / first
install before background refresh) with an explicit helper message
pointing at the main app.
Tests: new `WidgetResidenceFilterTest` (commonTest-style under
`androidUnitTest`, 9 cases). All green.
$ ./gradlew :composeApp:testDebugUnitTest \\
--tests "com.tt.honeyDue.widget.WidgetResidenceFilterTest"
BUILD SUCCESSFUL
$ ./gradlew :composeApp:assembleDebug
BUILD SUCCESSFUL
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
64 lines
1.8 KiB
Kotlin
64 lines
1.8 KiB
Kotlin
package com.tt.honeyDue.widget
|
|
|
|
import kotlinx.serialization.Serializable
|
|
|
|
/**
|
|
* DTO persisted to the widget DataStore as JSON, mirroring iOS
|
|
* `WidgetDataManager.swift`'s on-disk task representation.
|
|
*
|
|
* iOS field map (for reference — keep in sync):
|
|
* - id Int (task id)
|
|
* - title String
|
|
* - priority Int (priority id)
|
|
* - dueDate String? ISO-8601 ("yyyy-MM-dd" or full datetime)
|
|
* - isOverdue Bool
|
|
* - daysUntilDue Int
|
|
* - residenceId Int
|
|
* - residenceName String
|
|
* - categoryIcon String SF-symbol-style identifier
|
|
* - completed Bool
|
|
*
|
|
* Kotlin uses [Long] for ids to accommodate any server-side auto-increment range.
|
|
*/
|
|
@Serializable
|
|
data class WidgetTaskDto(
|
|
val id: Long,
|
|
val title: String,
|
|
val priority: Long,
|
|
val dueDate: String?,
|
|
val isOverdue: Boolean,
|
|
val daysUntilDue: Int,
|
|
val residenceId: Long,
|
|
val residenceName: String,
|
|
val categoryIcon: String,
|
|
val completed: Boolean
|
|
)
|
|
|
|
/**
|
|
* Lightweight residence identifier persisted to the widget DataStore.
|
|
*
|
|
* Written by the main app whenever [com.tt.honeyDue.data.DataManager.myResidences]
|
|
* updates so the widget configuration activity can offer the current
|
|
* residence list (gitea#6 — per-residence widget selection). Mirrors
|
|
* iOS' `WidgetDataManager.WidgetResidence` shape.
|
|
*/
|
|
@Serializable
|
|
data class WidgetResidenceDto(
|
|
val id: Long,
|
|
val name: String
|
|
)
|
|
|
|
/**
|
|
* Summary metrics computed from the cached task list.
|
|
*
|
|
* Windows match iOS `calculateMetrics` semantics:
|
|
* - overdueCount tasks with isOverdue == true
|
|
* - dueWithin7 tasks with 0 <= daysUntilDue <= 7
|
|
* - dueWithin8To30 tasks with 8 <= daysUntilDue <= 30
|
|
*/
|
|
data class WidgetStats(
|
|
val overdueCount: Int,
|
|
val dueWithin7: Int,
|
|
val dueWithin8To30: Int
|
|
)
|