Use kotlin serialization for backups

pull/2883/head
Alex Baker 1 month ago
parent 55adbc2025
commit d556863fda

@ -11,6 +11,7 @@ plugins {
id("com.google.android.gms.oss-licenses-plugin")
id("kotlin-parcelize")
id("com.google.devtools.ksp")
kotlin("plugin.serialization") version "1.9.10"
}
repositories {
@ -215,6 +216,7 @@ dependencies {
implementation(libs.kotlin.jdk8)
implementation(libs.kotlin.immutable)
implementation(libs.kotlinx.serialization)
implementation(libs.okhttp)
implementation(libs.persistent.cookiejar)
implementation(libs.gson)

@ -1,8 +1,10 @@
package org.tasks.backup
import org.tasks.data.entity.Task
import kotlinx.serialization.Serializable
import org.tasks.backup.TasksJsonImporter.LegacyLocation
import org.tasks.data.*
import org.tasks.data.GoogleTask
import org.tasks.data.GoogleTaskAccount
import org.tasks.data.GoogleTaskList
import org.tasks.data.entity.Alarm
import org.tasks.data.entity.Attachment
import org.tasks.data.entity.CaldavAccount
@ -13,10 +15,13 @@ import org.tasks.data.entity.Geofence
import org.tasks.data.entity.Place
import org.tasks.data.entity.Tag
import org.tasks.data.entity.TagData
import org.tasks.data.entity.Task
import org.tasks.data.entity.TaskAttachment
import org.tasks.data.entity.TaskListMetadata
import org.tasks.data.entity.UserActivity
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
@Serializable
class BackupContainer(
val tasks: List<TaskBackup>?,
val places: List<Place>?,
@ -30,10 +35,11 @@ class BackupContainer(
val longPrefs: Map<String, java.lang.Long>?,
val stringPrefs: Map<String, String>?,
val boolPrefs: Map<String, java.lang.Boolean>?,
val setPrefs: Map<String, java.util.Set<*>>?,
val setPrefs: Map<String, java.util.Set<String>>?,
val googleTaskAccounts: List<GoogleTaskAccount>? = emptyList(),
val googleTaskLists: List<GoogleTaskList>? = emptyList(),
) {
@Serializable
class TaskBackup(
val task: Task,
val alarms: List<Alarm>,

@ -7,10 +7,12 @@ import android.content.Context
import android.net.Uri
import android.os.Handler
import com.google.common.io.Files
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.todoroo.andlib.utility.DialogUtilities
import org.tasks.data.entity.Task
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.encodeToJsonElement
import org.tasks.BuildConfig
import org.tasks.R
import org.tasks.backup.BackupContainer.TaskBackup
@ -26,6 +28,7 @@ import org.tasks.data.dao.TaskAttachmentDao
import org.tasks.data.dao.TaskDao
import org.tasks.data.dao.TaskListMetadataDao
import org.tasks.data.dao.UserActivityDao
import org.tasks.data.entity.Task
import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.extensions.Context.toast
import org.tasks.files.FileHelper
@ -132,27 +135,32 @@ class TasksJsonExporter @Inject constructor(
vtodoCache.getVtodo( caldavTasks.firstOrNull { !it.isDeleted() })
))
}
val data: MutableMap<String, Any> = HashMap()
data["version"] = BuildConfig.VERSION_CODE
data["timestamp"] = currentTimeMillis()
data["data"] = BackupContainer(
taskBackups,
locationDao.getPlaces(),
tagDataDao.getAll(),
filterDao.getFilters(),
caldavDao.getAccounts(),
caldavDao.getCalendars(),
taskListMetadataDao.getAll(),
taskAttachmentDao.getAttachments(),
preferences.getPrefs(Integer::class.java),
preferences.getPrefs(java.lang.Long::class.java),
preferences.getPrefs(String::class.java),
preferences.getPrefs(java.lang.Boolean::class.java),
preferences.getPrefs(java.util.Set::class.java),
val data = JsonObject(
mapOf(
"version" to JsonPrimitive(BuildConfig.VERSION_CODE),
"timestamp" to JsonPrimitive(currentTimeMillis()),
"data" to Json.encodeToJsonElement(
BackupContainer(
taskBackups,
locationDao.getPlaces(),
tagDataDao.getAll(),
filterDao.getFilters(),
caldavDao.getAccounts(),
caldavDao.getCalendars(),
taskListMetadataDao.getAll(),
taskAttachmentDao.getAttachments(),
preferences.getPrefs(Integer::class.java),
preferences.getPrefs(java.lang.Long::class.java),
preferences.getPrefs(String::class.java),
preferences.getPrefs(java.lang.Boolean::class.java),
preferences.getPrefs(java.util.Set::class.java) as Map<String, java.util.Set<String>>,
)
)
)
)
val out = OutputStreamWriter(os, UTF_8)
val gson = if (BuildConfig.DEBUG) GsonBuilder().setPrettyPrinting().create() else Gson()
out.write(gson.toJson(data))
val json = if (BuildConfig.DEBUG) Json { prettyPrint = true } else Json
out.write(json.encodeToString(data))
out.close()
exportCount = taskBackups.size
}

@ -4,10 +4,7 @@ import android.app.ProgressDialog
import android.content.Context
import android.net.Uri
import android.os.Handler
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.todoroo.astrid.dao.TaskDao
import org.tasks.data.entity.Task
import com.todoroo.astrid.service.TaskCreator.Companion.getDefaultAlarms
import com.todoroo.astrid.service.TaskMover
import com.todoroo.astrid.service.Upgrade_13_2
@ -16,27 +13,34 @@ import com.todoroo.astrid.service.Upgrader.Companion.V12_4
import com.todoroo.astrid.service.Upgrader.Companion.V12_8
import com.todoroo.astrid.service.Upgrader.Companion.V6_4
import com.todoroo.astrid.service.Upgrader.Companion.getAndroidColor
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.caldav.VtodoCache
import org.tasks.data.dao.AlarmDao
import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.FilterDao
import org.tasks.data.dao.LocationDao
import org.tasks.data.dao.TagDao
import org.tasks.data.dao.TagDataDao
import org.tasks.data.dao.TaskAttachmentDao
import org.tasks.data.dao.TaskListMetadataDao
import org.tasks.data.dao.UserActivityDao
import org.tasks.data.entity.Attachment
import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavAccount.Companion.TYPE_GOOGLE_TASKS
import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.dao.CaldavDao
import org.tasks.data.entity.CaldavTask
import org.tasks.data.dao.FilterDao
import org.tasks.data.entity.Geofence
import org.tasks.data.dao.LocationDao
import org.tasks.data.entity.Place
import org.tasks.data.entity.Tag
import org.tasks.data.dao.TagDao
import org.tasks.data.entity.TagData
import org.tasks.data.dao.TagDataDao
import org.tasks.data.dao.TaskAttachmentDao
import org.tasks.data.dao.TaskListMetadataDao
import org.tasks.data.dao.UserActivityDao
import org.tasks.data.entity.Task
import org.tasks.db.Migrations.repeatFrom
import org.tasks.db.Migrations.withoutFrom
import org.tasks.filters.FilterCriteriaProvider
@ -78,18 +82,17 @@ class TasksJsonImporter @Inject constructor(
suspend fun importTasks(context: Context, backupFile: Uri?, progressDialog: ProgressDialog?): ImportResult {
val handler = Handler(context.mainLooper)
val gson = Gson()
val `is`: InputStream? = try {
context.contentResolver.openInputStream(backupFile!!)
} catch (e: FileNotFoundException) {
throw IllegalStateException(e)
}
val reader = InputStreamReader(`is`, TasksJsonExporter.UTF_8)
val input = gson.fromJson(reader, JsonObject::class.java)
val input = Json.parseToJsonElement(reader.readText())
try {
val data = input["data"]
val version = input["version"].asInt
val backupContainer = gson.fromJson(data, BackupContainer::class.java)
val data = input.jsonObject["data"]!!
val version = input.jsonObject["version"]!!.jsonPrimitive.int
val backupContainer = Json.decodeFromJsonElement<BackupContainer>(data)
backupContainer.tags?.forEach { tagData ->
findTagData(tagData)?.let {
return@forEach
@ -345,6 +348,7 @@ class TasksJsonImporter @Inject constructor(
var skipCount = 0
}
@Serializable
class LegacyLocation {
var name: String? = null
var address: String? = null

@ -3,6 +3,7 @@ plugins {
kotlin("android")
id("com.google.devtools.ksp")
id("kotlin-parcelize")
kotlin("plugin.serialization") version "1.9.10"
}
repositories {
@ -51,7 +52,7 @@ android {
dependencies {
implementation(libs.androidx.lifecycle.livedata)
implementation(libs.androidx.room)
implementation(libs.gson)
implementation(libs.kotlinx.serialization)
implementation(libs.timber)
ksp(libs.androidx.room.compiler)
}

@ -1,6 +1,9 @@
package org.tasks.data
import kotlinx.serialization.Serializable
@Deprecated("For backup use only")
@Serializable
data class GoogleTask(
var remoteId: String? = "",
var listId: String? = "",

@ -1,6 +1,9 @@
package org.tasks.data
import kotlinx.serialization.Serializable
@Deprecated("Only used for backup migration")
@Serializable
data class GoogleTaskAccount(
var account: String? = null,
var etag: String? = null,

@ -1,6 +1,9 @@
package org.tasks.data
import kotlinx.serialization.Serializable
@Deprecated("Only used for backup migration")
@Serializable
data class GoogleTaskList(
var account: String? = null,
var remoteId: String? = null,

@ -7,10 +7,13 @@ import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Ignore
import androidx.room.PrimaryKey
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.data.db.Table
import org.tasks.time.printTimestamp
import java.util.concurrent.TimeUnit
@Serializable
@Entity(
tableName = Alarm.TABLE_NAME,
foreignKeys = [

@ -5,7 +5,11 @@ import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.data.entity.Task.Companion.NO_ID
@Serializable
@Entity(
tableName = "attachment",
foreignKeys = [
@ -31,10 +35,10 @@ data class Attachment(
val id: Long? = null,
@ColumnInfo(name = "task", index = true)
@Transient
val task: Long,
val task: Long = NO_ID,
@ColumnInfo(name = "file", index = true)
@Transient
val fileId: Long,
val fileId: Long = NO_ID,
@ColumnInfo(name = "file_uuid")
val attachmentUid: String,
)

@ -4,10 +4,13 @@ import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import org.tasks.data.db.Table
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.data.db.Table
import java.net.HttpURLConnection
@Serializable
@Parcelize
@Entity(tableName = "caldav_accounts")
data class CaldavAccount(

@ -4,11 +4,14 @@ import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import org.tasks.data.db.Table
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.data.LIST
import org.tasks.data.NO_ORDER
import org.tasks.data.db.Table
@Serializable
@Parcelize
@Entity(tableName = "caldav_lists")
data class CaldavCalendar(

@ -4,9 +4,13 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import org.tasks.data.db.Table
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.data.UUIDHelper
import org.tasks.data.db.Table
import org.tasks.data.entity.Task.Companion.NO_ID
@Serializable
@Entity(
tableName = "caldav_tasks",
foreignKeys = [
@ -25,7 +29,7 @@ data class CaldavTask(
val id: Long = 0,
@ColumnInfo(name = "cd_task", index = true)
@Transient
val task: Long,
val task: Long = NO_ID,
@ColumnInfo(name = "cd_calendar")
var calendar: String?,
@ColumnInfo(name = "cd_remote_id")

@ -5,8 +5,11 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.data.NO_ORDER
@Serializable
@Parcelize
@Entity(tableName = "filters")
data class Filter(

@ -6,10 +6,11 @@ import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Ignore
import androidx.room.PrimaryKey
import org.tasks.data.db.Table
import kotlinx.parcelize.Parcelize
import java.io.Serializable
import kotlinx.serialization.Serializable
import org.tasks.data.db.Table
@Serializable
@Parcelize
@Entity(
tableName = Geofence.TABLE_NAME,
@ -36,7 +37,7 @@ data class Geofence(
val isArrival: Boolean = false,
@ColumnInfo(name = "departure")
var isDeparture: Boolean = false,
) : Serializable, Parcelable {
) : java.io.Serializable, Parcelable {
@Ignore
constructor(
task: Long,

@ -6,14 +6,16 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import org.tasks.data.db.Table
import org.tasks.data.UUIDHelper
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.data.NO_ORDER
import java.io.Serializable
import org.tasks.data.UUIDHelper
import org.tasks.data.db.Table
import java.util.regex.Pattern
import kotlin.math.abs
@Serializable
@Parcelize
@Entity(
tableName = Place.TABLE_NAME,
@ -48,7 +50,7 @@ data class Place(
val order: Int = NO_ORDER,
@ColumnInfo(name = "radius", defaultValue = "250")
val radius: Int = 250,
) : Serializable, Parcelable {
) : java.io.Serializable, Parcelable {
val displayName: String
get() {
if (!name.isNullOrEmpty() && !COORDS.matcher(name!!).matches()) {

@ -5,8 +5,11 @@ import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Ignore
import androidx.room.PrimaryKey
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.data.db.Table
@Serializable
@Entity(
tableName = "tags",
foreignKeys = [

@ -7,9 +7,12 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.data.LABEL
import org.tasks.data.NO_ORDER
@Serializable
@Entity(tableName = "tagdata")
class TagData : Parcelable {
@PrimaryKey(autoGenerate = true)

@ -7,23 +7,28 @@ import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.Index
import androidx.room.PrimaryKey
import com.google.gson.annotations.SerializedName
import org.tasks.data.db.Table
import org.tasks.data.sql.Field
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.json.JsonNames
import org.tasks.data.db.Table
import org.tasks.data.sql.Field
import timber.log.Timber
const val SUPPRESS_SYNC = "suppress_sync"
const val FORCE_CALDAV_SYNC = "force_caldav_sync"
@Serializable
@Parcelize
@Entity(
tableName = Task.TABLE_NAME,
indices = [
Index(name = "t_rid", value = ["remoteId"], unique = true),
Index(name = "active_and_visible", value = ["completed", "deleted", "hideUntil"])])
data class Task(
data class Task @OptIn(ExperimentalSerializationApi::class) constructor(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
@Transient
@ -53,7 +58,8 @@ data class Task(
@ColumnInfo(name = "timerStart")
var timerStart: Long = 0L,
@ColumnInfo(name = "notificationFlags")
@SerializedName("ringFlags", alternate = ["reminderFlags"])
@SerialName("ringFlags")
@JsonNames("reminderFlags")
var ringFlags: Int = 0,
@ColumnInfo(name = "lastNotified")
var reminderLast: Long = 0L,

@ -4,9 +4,12 @@ import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import org.tasks.data.UUIDHelper
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.data.UUIDHelper
@Serializable
@Parcelize
@Entity(tableName = "attachment_file")
data class TaskAttachment(

@ -3,13 +3,10 @@ package org.tasks.data.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import org.tasks.data.entity.Task
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
/**
* Data Model which represents a user.
*
* @author Tim Su <tim></tim>@todoroo.com>
*/
@Serializable
@Entity(tableName = "task_list_metadata")
class TaskListMetadata {
@PrimaryKey(autoGenerate = true)

@ -7,12 +7,15 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
import org.tasks.data.db.Table
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.json.JSONException
import org.json.JSONObject
import org.tasks.data.db.Table
import timber.log.Timber
import java.io.File
@Serializable
@Entity(tableName = "userActivity")
class UserActivity : Parcelable {
@PrimaryKey(autoGenerate = true)

@ -240,7 +240,20 @@
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1 -> 1.7.3 (*)
+| | +--- androidx.room:room-common:2.6.1 (c)
+| | \--- androidx.room:room-runtime:2.6.1 (c)
+| +--- com.google.code.gson:gson:2.10.1
+| +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2
+| | \--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.2
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 1.9.23 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.2
+| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.2 (c)
+| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.2 (c)
+| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2 (c)
+| | | \--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.2 (c)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.9.21 -> 1.9.23 (*)
+| | \--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.2
+| | \--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.2
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 1.9.23 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.2 (*)
+| | \--- org.jetbrains.kotlin:kotlin-stdlib-common:1.9.21 -> 1.9.23 (*)
+| +--- com.jakewharton.timber:timber:5.0.1
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.9.23 (*)
+| | \--- org.jetbrains:annotations:20.1.0 -> 23.0.0
@ -602,6 +615,7 @@
++--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7
+| \--- org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.7
+| \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 1.9.23 (*)
++--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2 (*)
++--- com.squareup.okhttp3:okhttp:4.12.0 (*)
++--- com.github.franmontiel:PersistentCookieJar:1.0.1
+| \--- com.squareup.okhttp3:okhttp:3.1.2 -> 4.12.0 (*)

@ -600,7 +600,20 @@
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1 -> 1.7.3 (*)
+| | +--- androidx.room:room-common:2.6.1 (c)
+| | \--- androidx.room:room-runtime:2.6.1 (c)
+| +--- com.google.code.gson:gson:2.10.1
+| +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2
+| | \--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.2
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 1.9.23 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.2
+| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.2 (c)
+| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.2 (c)
+| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2 (c)
+| | | \--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.2 (c)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.9.21 -> 1.9.23 (*)
+| | \--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.2
+| | \--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.2
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 1.9.23 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.2 (*)
+| | \--- org.jetbrains.kotlin:kotlin-stdlib-common:1.9.21 -> 1.9.23 (*)
+| +--- com.jakewharton.timber:timber:5.0.1
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.9.23 (*)
+| | \--- org.jetbrains:annotations:20.1.0 -> 23.0.0
@ -831,6 +844,7 @@
++--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7
+| \--- org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.7
+| \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 1.9.23 (*)
++--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2 (*)
++--- com.squareup.okhttp3:okhttp:4.12.0 (*)
++--- com.github.franmontiel:PersistentCookieJar:1.0.1
+| \--- com.squareup.okhttp3:okhttp:3.1.2 -> 4.12.0 (*)

@ -131,6 +131,7 @@ kotlin-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutab
kotlin-jdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines-test" }
kotlinx-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.2" }
leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }
locale = { module = "com.github.twofortyfouram:android-plugin-api-for-locale", version.ref = "locale" }
make-it-easy = { module = "com.natpryce:make-it-easy", version.ref = "make-it-easy" }

Loading…
Cancel
Save