package com.peachpasswords.license import android.content.Context import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import javax.inject.Singleton /** * Validation lives in [LicenseCodec]; this class owns storage only. */ @Singleton class LicenseRepository @Inject constructor(@ApplicationContext context: Context) { companion object { private const val PREFS_FILE = "peach_license_prefs" private const val KEY_LICENSE = "peach_license" private const val KEY_PRO_CELEBRATED = "peach_pro_celebrated" } 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, ) fun storeLicense(key: String): Boolean { if (!validateLicense(key)) return false prefs.edit() .putString(KEY_LICENSE, key) .remove(KEY_PRO_CELEBRATED) .apply() return true } fun getLicenseKey(): String? = prefs.getString(KEY_LICENSE, null) fun getLicenseIssuedAtMillis(): Long? = getLicenseKey()?.let(::licenseIssuedAtMillis) fun clearLicense() = prefs.edit().remove(KEY_LICENSE).apply() fun isProCelebrated(): Boolean = prefs.getBoolean(KEY_PRO_CELEBRATED, false) fun markProCelebrated() = prefs.edit().putBoolean(KEY_PRO_CELEBRATED, true).apply() fun isProActive(): Boolean = getLicenseKey()?.let(::validateLicense) == true // The signed payload is only 12 bytes and carries no per-feature data; gates use the licence verdict directly. fun hasFeature(): Boolean = isProActive() fun validateLicense(licenseKey: String): Boolean = LicenseCodec.validate(licenseKey) fun licenseIssuedAtMillis(licenseKey: String): Long? = LicenseCodec.issuedAtMillis(licenseKey) }