1package com.peachpasswords.sync23import android.content.Context4import androidx.security.crypto.EncryptedSharedPreferences5import androidx.security.crypto.MasterKey6import dagger.hilt.android.qualifiers.ApplicationContext7import kotlinx.coroutines.flow.MutableStateFlow8import kotlinx.coroutines.flow.StateFlow9import kotlinx.coroutines.flow.asStateFlow10import kotlinx.serialization.json.Json11import javax.inject.Inject12import javax.inject.Singleton1314151617181920212223@Singleton24class SyncOutcomeStore @Inject constructor(25 @ApplicationContext context: Context,26) {27 private val masterKey = MasterKey.Builder(context)28 .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)29 .build()3031 private val prefs = EncryptedSharedPreferences.create(32 context,33 PREFS_FILE,34 masterKey,35 EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,36 EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,37 )3839 private val _summary = MutableStateFlow(decodeSyncOutcome(prefs.getString(KEY_LAST, null)))40 val summary: StateFlow<SyncOutcomeSummary> = _summary.asStateFlow()4142 43 fun begin() {44 _summary.value = _summary.value.copy(syncing = true)45 }4647 48495051 fun report(outcome: SyncOutcomeSummary) {52 val next = outcome.copy(syncing = false, finishedAt = System.currentTimeMillis())53 prefs.edit().putString(KEY_LAST, encodeSyncOutcome(next)).apply()54 _summary.value = next55 }5657 58 fun reset() {59 prefs.edit().remove(KEY_LAST).apply()60 _summary.value = SyncOutcomeSummary()61 }6263 private companion object {64 const val PREFS_FILE = "peach_sync_outcome"65 const val KEY_LAST = "last_outcome"66 }67}6869private val outcomeJson = Json { encodeDefaults = true; ignoreUnknownKeys = true }7071internal fun encodeSyncOutcome(summary: SyncOutcomeSummary): String =72 outcomeJson.encodeToString(SyncOutcomeSummary.serializer(), summary.copy(syncing = false))737475767778internal fun decodeSyncOutcome(raw: String?): SyncOutcomeSummary {79 if (raw == null) return SyncOutcomeSummary()80 return runCatching {81 outcomeJson.decodeFromString(SyncOutcomeSummary.serializer(), raw).copy(syncing = false)82 }.getOrDefault(SyncOutcomeSummary())83}