From 58328e6ee8fdbd50af3459c38fb20f47be9a4d17 Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Mon, 30 Jun 2025 10:50:47 -0300 Subject: [PATCH] Remove gson --- app/build.gradle | 3 - .../nostrsigner/models/Permission.kt | 58 ++++++++ .../nostrsigner/service/IntentUtils.kt | 11 +- .../nostrsigner/service/model/AmberEvent.kt | 129 +++++++++--------- .../ui/components/MultiEventHomeScreen.kt | 12 +- gradle/libs.versions.toml | 2 - 6 files changed, 133 insertions(+), 82 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ca0c960c..e435c314 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -227,9 +227,6 @@ dependencies { implementation libs.core implementation libs.zxing.android.embedded - // Json Serialization - implementation libs.converter.gson - // Markdown (With fix for full-image bleeds) implementation(libs.richtext.ui) implementation(libs.richtext.ui.material3) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/models/Permission.kt b/app/src/main/java/com/greenart7c3/nostrsigner/models/Permission.kt index 57e28ed0..2e8bebec 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/models/Permission.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/models/Permission.kt @@ -1,13 +1,71 @@ package com.greenart7c3.nostrsigner.models import android.content.Context +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.json.JsonReadFeature +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.greenart7c3.nostrsigner.R +import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper.Companion.defaultPrettyPrinter data class Permission( val type: String, val kind: Int?, var checked: Boolean = true, ) { + private class PermissionSerializer : StdSerializer(Permission::class.java) { + override fun serialize( + permission: Permission, + gen: JsonGenerator, + provider: SerializerProvider, + ) { + gen.writeStartObject() + gen.writeStringField("type", permission.type) + permission.kind?.let { + gen.writeNumberField("kind", it) + } + gen.writeEndObject() + } + } + + private class PermissionDeserializer : StdDeserializer(Permission::class.java) { + override fun deserialize( + jp: JsonParser, + ctxt: DeserializationContext, + ): Permission = PermissionManualDeserializer.fromJson(jp.codec.readTree(jp)) + } + + private class PermissionManualDeserializer { + companion object { + fun fromJson(jsonObject: JsonNode): Permission = + Permission( + type = jsonObject.get("type").asText().intern(), + kind = jsonObject.get("kind").asText()?.toIntOrNull(), + ) + } + } + + companion object { + val mapper: ObjectMapper = + jacksonObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature()) + .setDefaultPrettyPrinter(defaultPrettyPrinter) + .registerModule( + SimpleModule() + .addSerializer(Permission::class.java, PermissionSerializer()) + .addDeserializer(Permission::class.java, PermissionDeserializer()), + ) + } + // LAST_COMMIT_CHECKED = "14ec14dac92d891715239bfb83ed1c9bdf2ac382" fun toLocalizedString(context: Context, shouldTranslateConnect: Boolean = false): String { return when (type) { diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt index c625ca87..d0b85641 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt @@ -13,8 +13,7 @@ import androidx.compose.ui.platform.Clipboard import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.toLowerCase import androidx.core.net.toUri -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken +import com.fasterxml.jackson.module.kotlin.readValue import com.greenart7c3.nostrsigner.Amber import com.greenart7c3.nostrsigner.BuildConfig import com.greenart7c3.nostrsigner.LocalPreferences @@ -303,8 +302,10 @@ object IntentUtils { val compressionType = if (intent.extras?.getString("compression") == "gzip") CompressionType.GZIP else CompressionType.NONE val returnType = if (intent.extras?.getString("returnType") == "event") ReturnType.EVENT else ReturnType.SIGNATURE - val listType = object : TypeToken>() {}.type - val permissions = Gson().fromJson?>(intent.extras?.getString("permissions"), listType) + val json = intent.extras?.getString("permissions") + val permissions: MutableList? = json?.let { + Permission.mapper.readValue>(it) + } permissions?.forEach { it.checked = true } @@ -582,7 +583,7 @@ object IntentUtils { Amber.instance.applicationIOScope.launch { val database = Amber.instance.getDatabase(account.npub) val defaultRelays = Amber.instance.settings.defaultRelays - var savedApplication = database.applicationDao().getByKey(key) + val savedApplication = database.applicationDao().getByKey(key) val relays = savedApplication?.application?.relays?.ifEmpty { defaultRelays } ?: defaultRelays val localAppName = if (packageName != null) { diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/model/AmberEvent.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/model/AmberEvent.kt index ad1c182c..25905a2f 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/model/AmberEvent.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/model/AmberEvent.kt @@ -1,92 +1,91 @@ package com.greenart7c3.nostrsigner.service.model import androidx.compose.runtime.Immutable -import com.google.gson.Gson -import com.google.gson.GsonBuilder -import com.google.gson.JsonArray -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import com.google.gson.JsonSerializationContext -import com.google.gson.JsonSerializer -import com.google.gson.annotations.SerializedName +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.json.JsonReadFeature +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper.Companion.defaultPrettyPrinter +import com.vitorpamplona.quartz.nip01Core.jackson.toTypedArray import com.vitorpamplona.quartz.utils.TimeUtils -import java.lang.reflect.Type @Immutable open class AmberEvent( var id: HexKey, - @SerializedName("pubkey") var pubKey: HexKey, - @SerializedName("created_at") val createdAt: Long, + var pubKey: HexKey, + val createdAt: Long, val kind: Int, val tags: Array>, val content: String, val sig: HexKey, ) { - private class EventDeserializer : JsonDeserializer { - override fun deserialize( - json: JsonElement, - typeOfT: Type?, - context: JsonDeserializationContext?, - ): AmberEvent { - val jsonObject = json.asJsonObject - return AmberEvent( - id = jsonObject.get("id")?.asString ?: "", - pubKey = jsonObject.get("pubkey")?.asString ?: "", - createdAt = jsonObject.get("created_at")?.asLong ?: TimeUtils.now(), - kind = jsonObject.get("kind").asInt, - tags = jsonObject.get("tags")?.asJsonArray?.map { - it.asJsonArray.mapNotNull { s -> if (s.isJsonNull) null else s.asString }.toTypedArray() - }?.toTypedArray() ?: emptyArray(), - content = jsonObject.get("content").asString, - sig = jsonObject.get("sig")?.asString ?: "", - ) + private class EventSerializer : StdSerializer(AmberEvent::class.java) { + override fun serialize( + event: AmberEvent, + gen: JsonGenerator, + provider: SerializerProvider, + ) { + gen.writeStartObject() + gen.writeStringField("id", event.id) + gen.writeStringField("pubkey", event.pubKey) + gen.writeNumberField("created_at", event.createdAt) + gen.writeNumberField("kind", event.kind) + gen.writeArrayFieldStart("tags") + event.tags.forEach { tag -> gen.writeArray(tag, 0, tag.size) } + gen.writeEndArray() + gen.writeStringField("content", event.content) + gen.writeStringField("sig", event.sig) + gen.writeEndObject() } } - private class EventSerializer : JsonSerializer { - override fun serialize( - src: AmberEvent, - typeOfSrc: Type?, - context: JsonSerializationContext?, - ): JsonElement { - return JsonObject().apply { - addProperty("id", src.id) - addProperty("pubkey", src.pubKey) - addProperty("created_at", src.createdAt) - addProperty("kind", src.kind) - add( - "tags", - JsonArray().also { jsonTags -> - src.tags.forEach { tag -> - jsonTags.add( - JsonArray().also { jsonTagElement -> - tag.forEach { tagElement -> - jsonTagElement.add(tagElement) - } - }, - ) - } - }, + private class EventDeserializer : StdDeserializer(AmberEvent::class.java) { + override fun deserialize( + jp: JsonParser, + ctxt: DeserializationContext, + ): AmberEvent = EventManualDeserializer.fromJson(jp.codec.readTree(jp)) + } + + private class EventManualDeserializer { + companion object { + fun fromJson(jsonObject: JsonNode): AmberEvent = + AmberEvent( + id = jsonObject.get("id")?.asText()?.intern() ?: "", + pubKey = jsonObject.get("pubkey")?.asText()?.intern() ?: "", + createdAt = jsonObject.get("created_at")?.asLong() ?: TimeUtils.now(), + kind = jsonObject.get("kind").asInt(), + tags = jsonObject.get("tags")?.toTypedArray { + it.toTypedArray { s -> if (s.isNull) "" else s.asText().intern() } + } ?: emptyArray(), + content = jsonObject.get("content").asText(), + sig = jsonObject.get("sig")?.asText() ?: "", ) - addProperty("content", src.content) - addProperty("sig", src.sig) - } } } companion object { - private val gson: Gson = - GsonBuilder() - .disableHtmlEscaping() - .registerTypeAdapter(AmberEvent::class.java, EventSerializer()) - .registerTypeAdapter(AmberEvent::class.java, EventDeserializer()) - .create() + val mapper: ObjectMapper = + jacksonObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature()) + .setDefaultPrettyPrinter(defaultPrettyPrinter) + .registerModule( + SimpleModule() + .addSerializer(AmberEvent::class.java, EventSerializer()) + .addDeserializer(AmberEvent::class.java, EventDeserializer()), + ) - fun fromJson(json: String): AmberEvent = gson.fromJson(json, AmberEvent::class.java) + fun fromJson(json: String): AmberEvent = mapper.readValue(json, AmberEvent::class.java) fun toEvent(amberEvent: AmberEvent): Event { return Event( diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt index cba23cde..d5373bea 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt @@ -40,7 +40,6 @@ import androidx.compose.ui.text.withLink import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController -import com.google.gson.GsonBuilder import com.greenart7c3.nostrsigner.Amber import com.greenart7c3.nostrsigner.LocalPreferences import com.greenart7c3.nostrsigner.R @@ -191,8 +190,8 @@ fun IntentMultiEventHomeScreen( onReject = {}, onChanged = { rememberType = it - intents.forEach { - it.rememberType.value = rememberType + intents.forEach { intent -> + intent.rememberType.value = rememberType } }, packageName = packageName, @@ -544,8 +543,8 @@ fun BunkerMultiEventHomeScreen( onReject = {}, onChanged = { rememberType = it - bunkerRequests.forEach { - it.rememberType.value = rememberType + bunkerRequests.forEach { bunkerRequest -> + bunkerRequest.rememberType.value = rememberType } }, packageName = packageName, @@ -907,8 +906,7 @@ private fun finishActivity(closeApp: Boolean) { private fun sendResultIntent( results: MutableList, ) { - val gson = GsonBuilder().serializeNulls().create() - val json = gson.toJson(results) + val json = Permission.mapper.writeValueAsString(results) val intent = Intent() intent.putExtra("results", json) Amber.instance.getMainActivity()?.setResult(Activity.RESULT_OK, intent) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fb2f15d7..f2be3a8e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,6 @@ activityCompose = "1.10.1" appcompat = "1.7.1" biometricKtx = "1.2.0-alpha05" -converterGson = "3.0.0" core = "3.5.3" coreKtx = "1.16.0" coreSplashscreen = "1.0.1" @@ -38,7 +37,6 @@ jna = { module = "net.java.dev.jna:jna", version.ref = "jna" } activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" } appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } biometric-ktx = { module = "androidx.biometric:biometric-ktx", version.ref = "biometricKtx" } -converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" } core = { module = "com.google.zxing:core", version.ref = "core" } core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" } core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }