Backup and restore bookmarks

This commit is contained in:
Koitharu
2023-07-18 14:27:14 +03:00
parent 967e8df7c9
commit fff77cf208
9 changed files with 93 additions and 7 deletions

View File

@@ -5,6 +5,7 @@ import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Upsert
import kotlinx.coroutines.flow.Flow
import org.koitharu.kotatsu.core.db.entity.MangaWithTags
@@ -14,6 +15,12 @@ abstract class BookmarksDao {
@Query("SELECT * FROM bookmarks WHERE manga_id = :mangaId AND page_id = :pageId")
abstract suspend fun find(mangaId: Long, pageId: Long): BookmarkEntity?
@Transaction
@Query(
"SELECT * FROM manga JOIN bookmarks ON bookmarks.manga_id = manga.manga_id ORDER BY bookmarks.created_at",
)
abstract suspend fun findAll(): Map<MangaWithTags, List<BookmarkEntity>>
@Query("SELECT * FROM bookmarks WHERE manga_id = :mangaId AND chapter_id = :chapterId AND page = :page")
abstract fun observe(mangaId: Long, chapterId: Long, page: Int): Flow<BookmarkEntity?>
@@ -37,4 +44,7 @@ abstract class BookmarksDao {
@Query("DELETE FROM bookmarks WHERE manga_id = :mangaId AND chapter_id = :chapterId AND page = :page")
abstract suspend fun delete(mangaId: Long, chapterId: Long, page: Int): Int
@Upsert
abstract suspend fun upsert(bookmarks: Collection<BookmarkEntity>)
}

View File

@@ -14,5 +14,6 @@ class BackupEntry(
const val CATEGORIES = "categories"
const val FAVOURITES = "favourites"
const val SETTINGS = "settings"
const val BOOKMARKS = "bookmarks"
}
}

View File

@@ -71,6 +71,24 @@ class BackupRepository @Inject constructor(
return entry
}
suspend fun dumpBookmarks(): BackupEntry {
val entry = BackupEntry(BackupEntry.BOOKMARKS, JSONArray())
val all = db.bookmarksDao.findAll()
for ((m, b) in all) {
val json = JSONObject()
val manga = JsonSerializer(m.manga).toJson()
json.put("manga", manga)
val tags = JSONArray()
m.tags.forEach { tags.put(JsonSerializer(it).toJson()) }
json.put("tags", tags)
val bookmarks = JSONArray()
b.forEach { bookmarks.put(JsonSerializer(it).toJson()) }
json.put("bookmarks", bookmarks)
entry.data.put(json)
}
return entry
}
fun dumpSettings(): BackupEntry {
val entry = BackupEntry(BackupEntry.SETTINGS, JSONArray())
val settingsDump = settings.getAllValues().toMutableMap()
@@ -144,6 +162,28 @@ class BackupRepository @Inject constructor(
return result
}
suspend fun restoreBookmarks(entry: BackupEntry): CompositeResult {
val result = CompositeResult()
for (item in entry.data.JSONIterator()) {
val mangaJson = item.getJSONObject("manga")
val manga = JsonDeserializer(mangaJson).toMangaEntity()
val tags = item.getJSONArray("tags").mapJSON {
JsonDeserializer(it).toTagEntity()
}
val bookmarks = item.getJSONArray("bookmarks").mapJSON {
JsonDeserializer(it).toBookmarkEntity()
}
result += runCatchingCancellable {
db.withTransaction {
db.tagsDao.upsert(tags)
db.mangaDao.upsert(manga, tags)
db.bookmarksDao.upsert(bookmarks)
}
}
}
return result
}
fun restoreSettings(entry: BackupEntry): CompositeResult {
val result = CompositeResult()
for (item in entry.data.JSONIterator()) {

View File

@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.backup
import org.json.JSONArray
import org.json.JSONObject
import org.koitharu.kotatsu.bookmarks.data.BookmarkEntity
import org.koitharu.kotatsu.core.db.entity.MangaEntity
import org.koitharu.kotatsu.core.db.entity.TagEntity
import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity
@@ -67,6 +68,17 @@ class JsonDeserializer(private val json: JSONObject) {
deletedAt = 0L,
)
fun toBookmarkEntity() = BookmarkEntity(
mangaId = json.getLong("manga_id"),
pageId = json.getLong("page_id"),
chapterId = json.getLong("chapter_id"),
page = json.getInt("page"),
scroll = json.getInt("scroll"),
imageUrl = json.getString("image_url"),
createdAt = json.getLong("created_at"),
percent = json.getDouble("percent").toFloat(),
)
fun toMap(): Map<String, Any?> {
val map = mutableMapOf<String, Any?>()
val keys = json.keys()

View File

@@ -1,6 +1,7 @@
package org.koitharu.kotatsu.core.backup
import org.json.JSONObject
import org.koitharu.kotatsu.bookmarks.data.BookmarkEntity
import org.koitharu.kotatsu.core.db.entity.MangaEntity
import org.koitharu.kotatsu.core.db.entity.TagEntity
import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity
@@ -68,6 +69,19 @@ class JsonSerializer private constructor(private val json: JSONObject) {
},
)
constructor(e: BookmarkEntity) : this(
JSONObject().apply {
put("manga_id", e.mangaId)
put("page_id", e.pageId)
put("chapter_id", e.chapterId)
put("page", e.page)
put("scroll", e.scroll)
put("image_url", e.imageUrl)
put("created_at", e.createdAt)
put("percent", e.percent)
},
)
constructor(m: Map<String, *>) : this(
JSONObject(m),
)

View File

@@ -68,6 +68,7 @@ class AppBackupAgent : BackupAgent() {
backup.put(repository.dumpHistory())
backup.put(repository.dumpCategories())
backup.put(repository.dumpFavourites())
backup.put(repository.dumpBookmarks())
backup.put(repository.dumpSettings())
backup.finish()
backup.file
@@ -88,6 +89,7 @@ class AppBackupAgent : BackupAgent() {
backup.getEntry(BackupEntry.HISTORY)?.let { repository.restoreHistory(it) }
backup.getEntry(BackupEntry.CATEGORIES)?.let { repository.restoreCategories(it) }
backup.getEntry(BackupEntry.FAVOURITES)?.let { repository.restoreFavourites(it) }
backup.getEntry(BackupEntry.BOOKMARKS)?.let { repository.restoreBookmarks(it) }
backup.getEntry(BackupEntry.SETTINGS)?.let { repository.restoreSettings(it) }
}
} finally {

View File

@@ -29,13 +29,16 @@ class BackupViewModel @Inject constructor(
progress.value = 0f
backup.put(repository.dumpHistory())
progress.value = 0.25f
progress.value = 0.2f
backup.put(repository.dumpCategories())
progress.value = 0.5f
progress.value = 0.4f
backup.put(repository.dumpFavourites())
progress.value = 0.75f
progress.value = 0.6f
backup.put(repository.dumpBookmarks())
progress.value = 0.8f
backup.put(repository.dumpSettings())
backup.finish()

View File

@@ -22,7 +22,6 @@ import kotlin.math.roundToInt
@AndroidEntryPoint
class RestoreDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
private val viewModel: RestoreViewModel by viewModels()
override fun onCreateViewBinding(

View File

@@ -52,17 +52,22 @@ class RestoreViewModel @Inject constructor(
result += repository.restoreHistory(it)
}
progress.value = 0.25f
progress.value = 0.2f
backup.getEntry(BackupEntry.CATEGORIES)?.let {
result += repository.restoreCategories(it)
}
progress.value = 0.5f
progress.value = 0.4f
backup.getEntry(BackupEntry.FAVOURITES)?.let {
result += repository.restoreFavourites(it)
}
progress.value = 0.75f
progress.value = 0.6f
backup.getEntry(BackupEntry.BOOKMARKS)?.let {
result += repository.restoreBookmarks(it)
}
progress.value = 0.8f
backup.getEntry(BackupEntry.SETTINGS)?.let {
result += repository.restoreSettings(it)
}