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.builtins.ListSerializer11import kotlinx.serialization.json.Json12import javax.inject.Inject13import javax.inject.Singleton1415@Singleton16class SyncAlertStore @Inject constructor(17 @ApplicationContext context: Context,18) : SyncAlertRecorder {19 private val masterKey = MasterKey.Builder(context)20 .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)21 .build()2223 private val prefs = EncryptedSharedPreferences.create(24 context,25 PREFS_FILE,26 masterKey,27 EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,28 EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,29 )3031 private val listSerializer = ListSerializer(SyncAlert.serializer())32 private val _alerts = MutableStateFlow(load())33 val alerts: StateFlow<List<SyncAlert>> = _alerts.asStateFlow()3435 override fun record(alerts: List<SyncAlert>) {36 if (alerts.isEmpty()) return37 persist(boundedSyncAlerts(load(), alerts))38 }3940 fun dismiss(alert: SyncAlert) {41 val current = load().toMutableList()42 val index = current.indexOf(alert)43 if (index != -1) {44 current.removeAt(index)45 persist(current)46 }47 }4849 fun clear() {50 persist(emptyList())51 }5253 private fun load(): List<SyncAlert> {54 val raw = prefs.getString(KEY_ALERTS, null) ?: return emptyList()55 return runCatching { json.decodeFromString(listSerializer, raw) }.getOrDefault(emptyList())56 }5758 private fun persist(alerts: List<SyncAlert>) {59 prefs.edit()60 .putString(KEY_ALERTS, json.encodeToString(listSerializer, alerts))61 .apply()62 _alerts.value = alerts63 }6465 private companion object {66 const val PREFS_FILE = "peach_sync_alerts"67 const val KEY_ALERTS = "sync_alerts"68 val json = Json { encodeDefaults = true; ignoreUnknownKeys = true }69 }70}