package com.peachpasswords.sync import android.content.Context import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.json.Json import javax.inject.Inject import javax.inject.Singleton @Singleton class SyncAlertStore @Inject constructor( @ApplicationContext context: Context, ) : SyncAlertRecorder { private val masterKey = MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() private val prefs = EncryptedSharedPreferences.create( context, PREFS_FILE, masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, ) private val listSerializer = ListSerializer(SyncAlert.serializer()) private val _alerts = MutableStateFlow(load()) val alerts: StateFlow> = _alerts.asStateFlow() override fun record(alerts: List) { val trustAlerts = alerts.filter(::isDeviceTrustAlert) if (trustAlerts.isEmpty()) return persist(boundedSyncAlerts(load(), trustAlerts)) } fun dismiss(alert: SyncAlert) { val current = load().toMutableList() val index = current.indexOf(alert) if (index != -1) { current.removeAt(index) persist(current) } } fun clear() { persist(emptyList()) } private fun load(): List { val raw = prefs.getString(KEY_ALERTS, null) ?: return emptyList() return runCatching { json.decodeFromString(listSerializer, raw) } .getOrDefault(emptyList()) .filter(::isDeviceTrustAlert) } private fun persist(alerts: List) { prefs.edit() .putString(KEY_ALERTS, json.encodeToString(listSerializer, alerts)) .apply() _alerts.value = alerts } private companion object { const val PREFS_FILE = "peach_sync_alerts" const val KEY_ALERTS = "sync_alerts" val json = Json { encodeDefaults = true; ignoreUnknownKeys = true } } }