Migrate to kotlinx.serialization for download store and deleter

This commit is contained in:
Jays2Kings 2022-04-25 20:16:29 -04:00
parent 152fa0cb9c
commit f81a6dd8d1
2 changed files with 46 additions and 53 deletions

View file

@ -1,10 +1,13 @@
package eu.kanade.tachiyomi.data.download package eu.kanade.tachiyomi.data.download
import android.content.Context import android.content.Context
import com.github.salomonbrys.kotson.fromJson import androidx.core.content.edit
import com.google.gson.Gson
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
/** /**
@ -14,15 +17,12 @@ import uy.kohesive.injekt.injectLazy
*/ */
class DownloadPendingDeleter(context: Context) { class DownloadPendingDeleter(context: Context) {
/** private val json: Json by injectLazy()
* Gson instance to encode and decode chapters.
*/
private val gson by injectLazy<Gson>()
/** /**
* Preferences used to store the list of chapters to delete. * Preferences used to store the list of chapters to delete.
*/ */
private val prefs = context.getSharedPreferences("chapters_to_delete", Context.MODE_PRIVATE) private val preferences = context.getSharedPreferences("chapters_to_delete", Context.MODE_PRIVATE)
/** /**
* Last added chapter, used to avoid decoding from the preference too often. * Last added chapter, used to avoid decoding from the preference too often.
@ -49,10 +49,10 @@ class DownloadPendingDeleter(context: Context) {
// Last entry matches the manga, reuse it to avoid decoding json from preferences // Last entry matches the manga, reuse it to avoid decoding json from preferences
lastEntry.copy(chapters = newChapters) lastEntry.copy(chapters = newChapters)
} else { } else {
val existingEntry = prefs.getString(manga.id!!.toString(), null) val existingEntry = preferences.getString(manga.id!!.toString(), null)
if (existingEntry != null) { if (existingEntry != null) {
// Existing entry found on preferences, decode json and add the new chapter // Existing entry found on preferences, decode json and add the new chapter
val savedEntry = gson.fromJson<Entry>(existingEntry) val savedEntry = json.decodeFromString<Entry>(existingEntry)
// Append new chapters // Append new chapters
val newChapters = savedEntry.chapters.addUniqueById(chapters) val newChapters = savedEntry.chapters.addUniqueById(chapters)
@ -68,8 +68,10 @@ class DownloadPendingDeleter(context: Context) {
} }
// Save current state // Save current state
val json = gson.toJson(newEntry) val json = json.encodeToString(newEntry)
prefs.edit().putString(newEntry.manga.id.toString(), json).apply() preferences.edit {
putString(newEntry.manga.id.toString(), json)
}
lastAddedEntry = newEntry lastAddedEntry = newEntry
} }
@ -82,37 +84,23 @@ class DownloadPendingDeleter(context: Context) {
@Synchronized @Synchronized
fun getPendingChapters(): Map<Manga, List<Chapter>> { fun getPendingChapters(): Map<Manga, List<Chapter>> {
val entries = decodeAll() val entries = decodeAll()
prefs.edit().clear().apply() preferences.edit {
clear()
}
lastAddedEntry = null lastAddedEntry = null
return entries.associate { entry -> return entries.associate { (chapters, manga) ->
entry.manga.toModel() to entry.chapters.map { it.toModel() } manga.toModel() to chapters.map { it.toModel() }
} }
} }
/**
* Returns the list of chapters to be deleted grouped by its manga.
*
* Note: the returned list of manga and chapters only contain basic information needed by the
* downloader, so don't use them for anything else.
*/
@Synchronized
fun getPendingChapters(manga: Manga): List<Chapter>? {
val entries = decodeAll()
prefs.edit().clear().apply()
lastAddedEntry = null
val entry = entries.find { it.manga.id == manga.id }
return entry?.chapters?.map { it.toModel() }
}
/** /**
* Decodes all the chapters from preferences. * Decodes all the chapters from preferences.
*/ */
private fun decodeAll(): List<Entry> { private fun decodeAll(): List<Entry> {
return prefs.all.values.mapNotNull { rawEntry -> return preferences.all.values.mapNotNull { rawEntry ->
try { try {
(rawEntry as? String)?.let { gson.fromJson<Entry>(it) } (rawEntry as? String)?.let { json.decodeFromString<Entry>(it) }
} catch (e: Exception) { } catch (e: Exception) {
null null
} }
@ -135,29 +123,32 @@ class DownloadPendingDeleter(context: Context) {
/** /**
* Class used to save an entry of chapters with their manga into preferences. * Class used to save an entry of chapters with their manga into preferences.
*/ */
@Serializable
private data class Entry( private data class Entry(
val chapters: List<ChapterEntry>, val chapters: List<ChapterEntry>,
val manga: MangaEntry val manga: MangaEntry,
) )
/** /**
* Class used to save an entry for a chapter into preferences. * Class used to save an entry for a chapter into preferences.
*/ */
@Serializable
private data class ChapterEntry( private data class ChapterEntry(
val id: Long, val id: Long,
val url: String, val url: String,
val name: String, val name: String,
val scanlator: String? val scanlator: String? = null,
) )
/** /**
* Class used to save an entry for a manga into preferences. * Class used to save an entry for a manga into preferences.
*/ */
@Serializable
private data class MangaEntry( private data class MangaEntry(
val id: Long, val id: Long,
val url: String, val url: String,
val title: String, val title: String,
val source: Long val source: Long,
) )
/** /**
@ -189,9 +180,9 @@ class DownloadPendingDeleter(context: Context) {
private fun ChapterEntry.toModel(): Chapter { private fun ChapterEntry.toModel(): Chapter {
return Chapter.create().also { return Chapter.create().also {
it.id = id it.id = id
it.scanlator = scanlator
it.url = url it.url = url
it.name = name it.name = name
it.scanlator = scanlator
} }
} }
} }

View file

@ -1,12 +1,16 @@
package eu.kanade.tachiyomi.data.download package eu.kanade.tachiyomi.data.download
import android.content.Context import android.content.Context
import com.google.gson.Gson import androidx.core.content.edit
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
/** /**
@ -16,7 +20,7 @@ import uy.kohesive.injekt.injectLazy
*/ */
class DownloadStore( class DownloadStore(
context: Context, context: Context,
private val sourceManager: SourceManager private val sourceManager: SourceManager,
) { ) {
/** /**
@ -24,14 +28,7 @@ class DownloadStore(
*/ */
private val preferences = context.getSharedPreferences("active_downloads", Context.MODE_PRIVATE) private val preferences = context.getSharedPreferences("active_downloads", Context.MODE_PRIVATE)
/** private val json: Json by injectLazy()
* Gson instance to serialize/deserialize downloads.
*/
private val gson: Gson by injectLazy()
/**
* Database helper.
*/
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()
/** /**
@ -45,9 +42,9 @@ class DownloadStore(
* @param downloads the list of downloads to add. * @param downloads the list of downloads to add.
*/ */
fun addAll(downloads: List<Download>) { fun addAll(downloads: List<Download>) {
val editor = preferences.edit() preferences.edit {
downloads.forEach { editor.putString(getKey(it), serialize(it)) } downloads.forEach { putString(getKey(it), serialize(it)) }
editor.apply() }
} }
/** /**
@ -56,14 +53,18 @@ class DownloadStore(
* @param download the download to remove. * @param download the download to remove.
*/ */
fun remove(download: Download) { fun remove(download: Download) {
preferences.edit().remove(getKey(download)).apply() preferences.edit {
remove(getKey(download))
}
} }
/** /**
* Removes all the downloads from the store. * Removes all the downloads from the store.
*/ */
fun clear() { fun clear() {
preferences.edit().clear().apply() preferences.edit {
clear()
}
} }
/** /**
@ -109,7 +110,7 @@ class DownloadStore(
*/ */
private fun serialize(download: Download): String { private fun serialize(download: Download): String {
val obj = DownloadObject(download.manga.id!!, download.chapter.id!!, counter++) val obj = DownloadObject(download.manga.id!!, download.chapter.id!!, counter++)
return gson.toJson(obj) return json.encodeToString(obj)
} }
/** /**
@ -119,7 +120,7 @@ class DownloadStore(
*/ */
private fun deserialize(string: String): DownloadObject? { private fun deserialize(string: String): DownloadObject? {
return try { return try {
gson.fromJson(string, DownloadObject::class.java) json.decodeFromString<DownloadObject>(string)
} catch (e: Exception) { } catch (e: Exception) {
null null
} }
@ -132,5 +133,6 @@ class DownloadStore(
* @param chapterId the id of the chapter. * @param chapterId the id of the chapter.
* @param order the order of the download in the queue. * @param order the order of the download in the queue.
*/ */
@Serializable
data class DownloadObject(val mangaId: Long, val chapterId: Long, val order: Int) data class DownloadObject(val mangaId: Long, val chapterId: Long, val order: Int)
} }