96 lines
2.9 KiB
Kotlin
96 lines
2.9 KiB
Kotlin
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
package org.mozilla.fenix.gleanplumb
|
|
|
|
import android.content.Context
|
|
import android.util.AtomicFile
|
|
import androidx.annotation.VisibleForTesting
|
|
import mozilla.components.support.ktx.util.readAndDeserialize
|
|
import mozilla.components.support.ktx.util.writeString
|
|
import org.json.JSONArray
|
|
import org.json.JSONObject
|
|
import java.io.File
|
|
|
|
internal const val FILE_NAME = "nimbus_messages_metadata.json"
|
|
|
|
/**
|
|
* A storage that persists [Message.Metadata] into disk.
|
|
*/
|
|
class OnDiskMessageMetadataStorage(
|
|
private val context: Context
|
|
) : MessageMetadataStorage {
|
|
private val diskCacheLock = Any()
|
|
|
|
@VisibleForTesting
|
|
internal var metadataMap: MutableMap<String, Message.Metadata> = hashMapOf()
|
|
|
|
override suspend fun getMetadata(): Map<String, Message.Metadata> {
|
|
if (metadataMap.isEmpty()) {
|
|
metadataMap = readFromDisk().toMutableMap()
|
|
}
|
|
return metadataMap
|
|
}
|
|
|
|
override suspend fun addMetadata(metadata: Message.Metadata): Message.Metadata {
|
|
metadataMap[metadata.id] = metadata
|
|
writeToDisk()
|
|
return metadata
|
|
}
|
|
|
|
override suspend fun updateMetadata(metadata: Message.Metadata) {
|
|
addMetadata(metadata)
|
|
}
|
|
|
|
@VisibleForTesting
|
|
internal fun readFromDisk(): Map<String, Message.Metadata> {
|
|
synchronized(diskCacheLock) {
|
|
return getFile().readAndDeserialize {
|
|
JSONArray(it).toMetadataMap()
|
|
} ?: emptyMap()
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
internal fun writeToDisk() {
|
|
synchronized(diskCacheLock) {
|
|
val json = metadataMap.values.toList().fold("") { acc, next ->
|
|
if (acc.isEmpty()) {
|
|
next.toJson()
|
|
} else {
|
|
"$acc,${next.toJson()}"
|
|
}
|
|
}
|
|
getFile().writeString { "[$json]" }
|
|
}
|
|
}
|
|
|
|
private fun getFile(): AtomicFile {
|
|
return AtomicFile(File(context.filesDir, FILE_NAME))
|
|
}
|
|
}
|
|
|
|
internal fun JSONArray.toMetadataMap(): Map<String, Message.Metadata> {
|
|
return (0 until length()).map { index ->
|
|
getJSONObject(index).toMetadata()
|
|
}.associateBy {
|
|
it.id
|
|
}
|
|
}
|
|
|
|
@Suppress("MaxLineLength") // To avoid adding any extra space to the string.
|
|
internal fun Message.Metadata.toJson(): String {
|
|
return """{"id":"$id","displayCount":$displayCount,"pressed":$pressed,"dismissed":$dismissed,"lastTimeShown":$lastTimeShown}"""
|
|
}
|
|
|
|
internal fun JSONObject.toMetadata(): Message.Metadata {
|
|
return Message.Metadata(
|
|
id = optString("id"),
|
|
displayCount = optInt("displayCount"),
|
|
pressed = optBoolean("pressed"),
|
|
dismissed = optBoolean("dismissed"),
|
|
lastTimeShown = optLong("lastTimeShown")
|
|
)
|
|
}
|