feat(store): import the dmfs database and make Room the default

Phase 4 of docs/OWN-STORE.md. OneShotImport reads databases/tasks.db
directly — read-only, no provider, no ContentResolver — and writes it
into Room in one verified transaction. dmfs row ids are remapped in two
passes, because a parent can carry a higher _id than its child.

The archive happens before the import, not after, and the import always
replaces. That is what actually closes the crash window the plan's "flag
*and* rename" is meant to cover: renaming last leaves the flag unset with
tasks.db still in place, so the next launch imports a second copy. In
this order every kill point re-enters correctly.

Recurrence overrides are carried across as master_id/recurrence_id rather
than ignored. dmfs stores them as ordinary rows sharing their master's
_uid, so importing one as a second master would collide on the unique
index and abort the whole import.

autoMode now answers OWN, and a stored LOCAL reads as OWN — after the
import the dmfs file has been renamed away, so someone who chose local
storage explicitly must land on the store their data is now in.

StartupGate holds the first store read until the mode has landed and the
import has run; showing an upgrading user an empty app is the worst thing
this migration could do. The backup rules take the database with its WAL
sidecars and exclude the archive, and the app checkpoints on ON_STOP.
This commit is contained in:
2026-08-13 16:24:19 +02:00
parent 2e915da588
commit 76f9ae6780
13 changed files with 819 additions and 21 deletions

View File

@@ -154,6 +154,7 @@ dependencies {
implementation(libs.androidx.appcompat)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.process)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))