feat(settings): storage picker and export screen
The store picker and the export screen were the two frontend surfaces the own-store work left unbuilt, so both backends shipped unreachable. Settings gains a Storage section holding them: a full-screen picker over Own / an installed external provider (dimmed when none is present, named after the provider's own app), and an export screen with a per-list tick and the two SAF destinations, a folder or a single zip. The picker asks for the provider's runtime permission before writing the mode, so a denial leaves the readable store in place instead of dropping the user on the gate; a refusal is reported with a route to app settings. Making the mode switchable at runtime had two consequences: - reminders are armed off whichever store was active when they were scheduled, so a switch rebuilds the set. ReminderScheduler.sync() is now serialised — it is a read-modify-write over ScheduledReminderStore, and overlapping runs each wrote their own set as the whole truth - the permission gate is the only screen an External user can reach once their provider app stops answering, so it offers the way back to our own store ExportWriter no longer deletes a previous export before recreating it (a failure in between lost both), lists the target directory once instead of per document, and carries a typed ExportFailure so the screen can report in the user's language rather than an exception message.
This commit is contained in:
@@ -42,7 +42,7 @@
|
||||
|---|---|---|---|
|
||||
| 1 | Merge `fix/provider-interaction-review` | unmerged and rotting; touches the same permission flow as step 2 | ✅ done |
|
||||
| 2 | Vendor `:provider` under our own authority | the identity, done once — and it ships a complete local-first app | ✅ done |
|
||||
| 3 | Export / backup | our data now lives only in our app's private storage | ✅ backend done; no UI yet |
|
||||
| 3 | Export / backup | our data now lives only in our app's private storage | ✅ done, UI included |
|
||||
| 4 | File the DAVx5 issue | cheap, non-blocking, serves F-Droid users | ⬜ |
|
||||
| 5 | Sync adapter | the 1.x arc; designed in [`SYNC.md`](SYNC.md), not yet built | ⬜ |
|
||||
|
||||
@@ -202,8 +202,9 @@ than "rank ours first whenever it's non-empty", and needs no database probe:
|
||||
|
||||
That permission is dangerous-level, so it can only be there because an earlier
|
||||
version asked and the user agreed — which is exactly what "existing Posture A
|
||||
user" means. A fresh install holds nothing and gets local-first. ⬜ The Settings
|
||||
override the rule assumes is not built yet.
|
||||
user" means. A fresh install holds nothing and gets local-first. ✅ The Settings
|
||||
override the rule assumes is built: Storage → Task store, which asks for the
|
||||
external provider's permission before committing the switch rather than after.
|
||||
|
||||
**Note on the mode vocabulary.** The code has two modes, not three:
|
||||
`StorageMode.LOCAL` and `StorageMode.EXTERNAL`. As this document says two
|
||||
@@ -211,7 +212,7 @@ paragraphs up, Synced *is* Local with an account attached — so it is derived
|
||||
state, and giving it its own constant would imply switching sync on is a
|
||||
migration when the whole point is that it isn't.
|
||||
|
||||
**Export/backup is a v1 feature.** ✅ Backend built (no UI yet). Not, as
|
||||
**Export/backup is a v1 feature.** ✅ Built, screen included. Not, as
|
||||
previously framed, a migration safety net for "uninstall OpenTasks" — that
|
||||
scenario no longer exists. It's data portability for Local-mode users, whose
|
||||
tasks otherwise exist in exactly one place with no second copy. On Play, where
|
||||
|
||||
Reference in New Issue
Block a user