feat: add history sync worker

This commit is contained in:
2026-06-07 17:16:47 +02:00
parent 3f25ee4c41
commit 3b14f2fba9
25 changed files with 807 additions and 120 deletions
@@ -0,0 +1,77 @@
package com.bitcointxoko.gudariwallet.sync
import android.content.Context
import android.util.Log
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.bitcointxoko.gudariwallet.data.HistoricalSyncStore
import com.bitcointxoko.gudariwallet.data.PaymentCacheRepository
import com.bitcointxoko.gudariwallet.data.WalletRepository
import com.bitcointxoko.gudariwallet.util.NotificationHelper
private const val TAG = "HistoricalSyncWorker"
private const val PAGE_SIZE = 100
class HistoricalSyncWorker(
appContext : Context,
params : WorkerParameters,
private val repo : WalletRepository,
private val paymentCache : PaymentCacheRepository,
private val syncStore : HistoricalSyncStore
) : CoroutineWorker(appContext, params) {
companion object {
const val KEY_FETCHED = "fetched"
const val KEY_TOTAL = "total"
}
override suspend fun doWork(): Result {
// Guard: already completed — safe if WorkManager ever retries a success
if (syncStore.isComplete()) {
Log.d(TAG, "Already complete — skipping")
return Result.success()
}
Log.d(TAG, "Starting historical payment sync")
var offset = 0
var total = -1
while (true) {
val page = runCatching {
repo.getPaymentsPaginated(offset = offset, limit = PAGE_SIZE)
}.getOrElse { e ->
Log.w(TAG, "Network error on offset=$offset: ${e.message} — scheduling retry")
return Result.retry() // WorkManager applies exponential backoff automatically
}
// First page: learn the real total from the server
if (total == -1) {
total = page.total
Log.d(TAG, "Server reports $total total payments")
if (total == 0) {
syncStore.markComplete()
return Result.success()
}
}
// Full overwrite — mergePayments uses INSERT OR REPLACE, no skip logic
paymentCache.mergePayments(page.data)
offset += page.data.size
Log.d(TAG, "Progress: $offset / $total")
setProgress(workDataOf(
KEY_FETCHED to offset,
KEY_TOTAL to total
))
// Stop if server returned a short page (end of data) or we've reached total
if (page.data.size < PAGE_SIZE || offset >= total) break
}
syncStore.markComplete()
Log.d(TAG, "Historical sync complete — $offset payments stored")
NotificationHelper.notifySyncComplete(applicationContext, offset)
return Result.success()
}
}
@@ -0,0 +1,26 @@
package com.bitcointxoko.gudariwallet.sync
import android.content.Context
import androidx.work.ListenableWorker
import androidx.work.WorkerFactory
import androidx.work.WorkerParameters
import com.bitcointxoko.gudariwallet.data.HistoricalSyncStore
import com.bitcointxoko.gudariwallet.data.PaymentCacheRepository
import com.bitcointxoko.gudariwallet.data.WalletRepository
class HistoricalSyncWorkerFactory(
private val repo : WalletRepository,
private val paymentCache : PaymentCacheRepository,
private val syncStore : HistoricalSyncStore
) : WorkerFactory() {
override fun createWorker(
appContext : Context,
workerClassName : String,
workerParameters : WorkerParameters
): ListenableWorker? =
if (workerClassName == HistoricalSyncWorker::class.java.name)
HistoricalSyncWorker(appContext, workerParameters, repo, paymentCache, syncStore)
else
null // return null to delegate to the next factory in the chain
}