diff --git a/app/build.gradle b/app/build.gradle index a00bce24287f28a39f4e9f0bd79234a2b2b2896c..e798ac7b0c3487f12273229c151d40834a4777bf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,6 @@ /* * Copyright (C) 2023 MURENA SAS - * Copyright (C) 2022 - 2024 E FOUNDATION + * Copyright (C) 2022 - 2025 E FOUNDATION * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -183,6 +183,7 @@ dependencies { libs.eos.telemetry, libs.google.material, + libs.kotlinx.serialization, libs.maplibre, libs.mpandroidcharts, diff --git a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt index fa9f16b79650ab2a9891cc00e14efe3755f9937b..7938720d3b36efa03193f6d7b8792267a9322f66 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt @@ -1,4 +1,5 @@ /* + * Copyright (C) 2025 E FOUNDATION * Copyright (C) 2023 - 2024 MURENA SAS * * This program is free software: you can redistribute it and/or modify @@ -60,6 +61,7 @@ import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob +import kotlinx.serialization.json.Json import org.koin.android.ext.koin.androidContext import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.androidx.viewmodel.dsl.viewModelOf @@ -81,6 +83,18 @@ val appModule = module { ) } + single { + Json { + // the parser will not fail on unknown properties in the JSON and will simply ignore them. + ignoreUnknownKeys = true + // the parser will strictly follow the JSON specification and not allow non-compliant JSON. + isLenient = false + // default values of Kotlin properties will be included in the JSON during serialization. + encodeDefaults = true + useAlternativeNames = true + } + } + factory { androidContext().resources } single { LocalStateRepositoryImpl(context = androidContext()) diff --git a/build.gradle b/build.gradle index c334b631f262ac27b04afc067a305766d9da2224..51955812b678eadbf5821521f788a9ca623c6a83 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ /* * Copyright (C) 2023-2025 MURENA SAS - * Copyright (C) 2022 E FOUNDATION + * Copyright (C) 2022 - 2025 E FOUNDATION * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,6 +50,7 @@ plugins { alias libs.plugins.android.application apply false alias libs.plugins.androidx.navigation.safeargs apply false alias libs.plugins.android.library apply false + alias libs.plugins.kotlin.serialization apply false } allprojects { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 636f2f131ee12acf2244e6c19e8beea972a3040a..fddb583815a990250289704988275855cd8e64b6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -27,11 +27,11 @@ eos-elib = { group = "foundation.e", name = "elib", version = "0.0.1-alpha11" } eos-orbotservice = { group = "foundation.e", name = "orbotservice", version.ref = "orbotservice" } eos-telemetry = { group = "foundation.e.lib", name = "telemetry", version = "0.0.11-alpha" } google-material = { group = "com.google.android.material", name = "material", version = "1.12.0" } -google-gson = { group = "com.google.code.gson", name = "gson", version = "2.10.1" } junit = { group = "junit", name = "junit", version = "4.13.2" } koin-core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" } koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" } kotlinx-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } +kotlinx-serialization = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version = "1.8.0" } leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version = "2.10" } maplibre = { group = "org.maplibre.gl", name = "android-sdk", version = "11.8.2" } mpandroidcharts = { group = "com.github.PhilJay", name = "MPAndroidChart", version = "v3.1.0" } @@ -56,3 +56,4 @@ benmanes-versions = { id = "com.github.ben-manes.versions", version = "0.38.0" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } spotless = { id = "com.diffplug.spotless", version = "6.23.3" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version = "2.1.10" } diff --git a/trackers/build.gradle b/trackers/build.gradle index f75ce5b28cdbec711c0dde8a6c879fa3f077ee7d..807b2684244c985fa7e6f50722066c9cfce4e7af 100644 --- a/trackers/build.gradle +++ b/trackers/build.gradle @@ -1,4 +1,5 @@ /* + Copyright (C) 2025 E FOUNDATION Copyright (C) 2023 MURENA SAS Copyright (C) 2022 ECORP @@ -18,8 +19,11 @@ */ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' +plugins { + alias libs.plugins.android.library + alias libs.plugins.kotlin.android + alias libs.plugins.kotlin.serialization +} android { compileSdkVersion buildConfig.compileSdk @@ -49,7 +53,7 @@ dependencies { libs.androidx.work.ktx, libs.bundles.koin, libs.bundles.kotlin.android.coroutines, - libs.google.gson, + libs.kotlinx.serialization, libs.pcap4j, libs.retrofit, libs.retrofit.scalars, diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/ETrackersResponse.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/ETrackersResponse.kt index d11415e0025f0eb22fa6ece7bd556fb935893d7c..1882834d450b5d0af6bcebe4a4cb93dd9c2eda66 100644 --- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/ETrackersResponse.kt +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/ETrackersResponse.kt @@ -1,4 +1,5 @@ /* + * Copyright (C) 2025 E FOUNDATION * Copyright (C) 2023 MURENA SAS * * This program is free software: you can redistribute it and/or modify @@ -17,11 +18,15 @@ package foundation.e.advancedprivacy.trackers.data +import kotlinx.serialization.Serializable + +@Serializable data class ETrackersResponse(val trackers: List) { + @Serializable data class ETracker( - val id: String?, - val hostnames: List?, - val name: String?, - val link: String? + val id: String? = null, + val hostnames: List? = null, + val name: String? = null, + val link: String? = null ) } diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/TrackersRepository.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/TrackersRepository.kt index d640a5d4bd31a4b40147e145aec53c5b38865f37..858de13e0996130815ea1d24eda6a942fd56a1b3 100644 --- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/TrackersRepository.kt +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/TrackersRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 E FOUNDATION + * Copyright (C) 2022 - 2025 E FOUNDATION * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,18 +18,20 @@ package foundation.e.advancedprivacy.trackers.data import android.content.Context -import com.google.gson.Gson import foundation.e.advancedprivacy.trackers.domain.entities.Tracker import java.io.File import java.io.FileInputStream -import java.io.InputStreamReader import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromStream import timber.log.Timber class TrackersRepository( private val context: Context, + private val json: Json, coroutineScope: CoroutineScope ) { @@ -44,19 +46,17 @@ class TrackersRepository( initTrackersFile() } } + + @OptIn(ExperimentalSerializationApi::class) fun initTrackersFile() { try { var inputStream = context.assets.open(eTrackerFileName) if (eTrackerFile.exists()) { inputStream = FileInputStream(eTrackerFile) } - val reader = InputStreamReader(inputStream, "UTF-8") - val trackerResponse = - Gson().fromJson(reader, ETrackersResponse::class.java) + val trackerResponse = json.decodeFromStream(inputStream) setTrackersList(mapper(trackerResponse)) - - reader.close() inputStream.close() } catch (e: Exception) { Timber.e(e, "While parsing trackers in assets")