Export retained notification images in HTML

This commit is contained in:
ajp_anton
2026-07-23 10:51:07 +00:00
parent 6eb9058595
commit dd1c9e5a01
8 changed files with 123 additions and 7 deletions
@@ -2,6 +2,8 @@ package se.ajpanton.notificationlog.data
import android.content.Context
import java.io.File
import java.io.DataInputStream
import java.io.FileNotFoundException
/** Stores copied notification image bytes independently of the source app's URI lifetime. */
class EncryptedImageStore(context: Context) {
@@ -9,6 +11,7 @@ class EncryptedImageStore(context: Context) {
private val cipher = AesGcmCipher(LogEncryptionKeyProvider().getOrCreate())
fun save(id: String, bytes: ByteArray) {
require(id.matches(IMAGE_ID_PATTERN)) { "Invalid notification image ID." }
val payload = cipher.encrypt(bytes)
File(directory, "$id.bin").outputStream().use { output ->
output.write(payload.initializationVector.size)
@@ -16,4 +19,26 @@ class EncryptedImageStore(context: Context) {
output.write(payload.cipherText)
}
}
fun read(id: String): ByteArray? {
require(id.matches(IMAGE_ID_PATTERN)) { "Invalid notification image ID." }
val file = File(directory, "$id.bin")
if (!file.exists()) return null
try {
DataInputStream(file.inputStream()).use { input ->
val ivLength = input.readUnsignedByte()
require(ivLength in 12..32) { "Invalid notification image IV length." }
val initializationVector = ByteArray(ivLength).also(input::readFully)
val cipherText = input.readBytes()
require(cipherText.isNotEmpty()) { "Empty encrypted notification image." }
return cipher.decrypt(EncryptedPayload(initializationVector, cipherText))
}
} catch (error: FileNotFoundException) {
return null
}
}
private companion object {
val IMAGE_ID_PATTERN = Regex("[0-9a-f-]{36}")
}
}
@@ -16,7 +16,8 @@ object NotificationLogEntryJson {
.put("packageName", entry.packageName)
.put("appName", entry.appName)
.put("action", entry.action.name)
.put("contents", entry.contents),
.put("contents", entry.contents)
.put("imageId", entry.imageId),
)
}
}.toString().encodeToByteArray()
@@ -32,6 +33,7 @@ object NotificationLogEntryJson {
appName = entry.getString("appName"),
action = NotificationAction.valueOf(entry.getString("action")),
contents = if (entry.isNull("contents")) null else entry.getString("contents"),
imageId = entry.optString("imageId").takeIf { it.isNotEmpty() },
)
}
}