Starting shift to 1.x sources

This commit is contained in:
Jays2Kings 2021-03-21 13:30:11 -04:00
parent 7963a607b8
commit 73df525939
11 changed files with 137 additions and 102 deletions

View File

@ -24,8 +24,9 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.fetchMangaDetailsAsync
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toMangaInfo
import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.storage.getUriCompat
@ -92,10 +93,10 @@ class LibraryUpdateService(
// List containing categories that get included in downloads.
private val categoriesToDownload =
preferences.downloadNewCategories().getOrDefault().map(String::toInt)
preferences.downloadNewCategories().get().map(String::toInt)
// Boolean to determine if user wants to automatically download new chapters.
private val downloadNew: Boolean = preferences.downloadNew().getOrDefault()
private val downloadNew: Boolean = preferences.downloadNew().get()
// Boolean to determine if DownloadManager has downloads
private var hasDownloads = false
@ -198,7 +199,7 @@ class LibraryUpdateService(
db.getLibraryMangas().executeAsBlocking().filter { it.category == categoryId }
} else {
val categoriesToUpdate =
preferences.libraryUpdateCategories().getOrDefault().map(String::toInt)
preferences.libraryUpdateCategories().get().map(String::toInt)
if (categoriesToUpdate.isNotEmpty()) {
categoryIds.addAll(categoriesToUpdate)
db.getLibraryMangas().executeAsBlocking()
@ -451,7 +452,7 @@ class LibraryUpdateService(
)
val networkManga = try {
source.fetchMangaDetailsAsync(manga)
source.getMangaDetails(manga.toMangaInfo()).toSManga()
} catch (e: java.lang.Exception) {
Timber.e(e)
null

View File

@ -174,7 +174,7 @@ class PreferencesHelper(val context: Context) {
fun libraryUpdateRestriction() = prefs.getStringSet(Keys.libraryUpdateRestriction, emptySet())
fun libraryUpdateCategories() = rxPrefs.getStringSet(Keys.libraryUpdateCategories, emptySet())
fun libraryUpdateCategories() = flowPrefs.getStringSet(Keys.libraryUpdateCategories, emptySet())
fun libraryUpdatePrioritization() = rxPrefs.getInteger(Keys.libraryUpdatePrioritization, 0)
@ -212,9 +212,9 @@ class PreferencesHelper(val context: Context) {
fun pinnedCatalogues() = rxPrefs.getStringSet("pinned_catalogues", emptySet())
fun downloadNew() = rxPrefs.getBoolean(Keys.downloadNew, false)
fun downloadNew() = flowPrefs.getBoolean(Keys.downloadNew, false)
fun downloadNewCategories() = rxPrefs.getStringSet(Keys.downloadNewCategories, emptySet())
fun downloadNewCategories() = flowPrefs.getStringSet(Keys.downloadNewCategories, emptySet())
fun lang() = prefs.getString(Keys.lang, "")

View File

@ -41,6 +41,7 @@ interface Source : tachiyomi.source.Source {
*
* @param manga the manga to update.
*/
@Deprecated("Use getMangaDetails instead")
fun fetchMangaDetails(manga: SManga): Observable<SManga>
/**
@ -48,6 +49,7 @@ interface Source : tachiyomi.source.Source {
*
* @param manga the manga to update.
*/
@Deprecated("Use getChapterList instead")
fun fetchChapterList(manga: SManga): Observable<List<SChapter>>
/**
@ -55,6 +57,7 @@ interface Source : tachiyomi.source.Source {
*
* @param chapter the chapter.
*/
@Deprecated("Use getPageList instead")
fun fetchPageList(chapter: SChapter): Observable<List<Page>>
/**
@ -92,17 +95,5 @@ interface Source : tachiyomi.source.Source {
}
}
suspend fun Source.fetchMangaDetailsAsync(manga: SManga): SManga? {
return withContext(Dispatchers.IO) {
fetchMangaDetails(manga).toBlocking().single()
}
}
suspend fun Source.fetchChapterListAsync(manga: SManga): List<SChapter>? {
return withContext(Dispatchers.IO) {
fetchChapterList(manga).toBlocking().single()
}
}
fun Source.icon(): Drawable? =
Injekt.get<ExtensionManager>().getAppIconForSource(this)

View File

@ -5,9 +5,11 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.fetchChapterListAsync
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.toSChapter
import eu.kanade.tachiyomi.source.model.toSManga
import uy.kohesive.injekt.injectLazy
abstract class DelegatedHttpSource {
@ -24,10 +26,10 @@ abstract class DelegatedHttpSource {
open fun pageNumber(uri: Uri): Int? = uri.pathSegments.lastOrNull()?.toIntOrNull()
abstract suspend fun fetchMangaFromChapterUrl(uri: Uri): Triple<Chapter, Manga, List<SChapter>>?
protected open fun getMangaInfo(url: String): Manga? {
protected open suspend fun getMangaInfo(url: String): Manga? {
val id = delegate?.id ?: return null
val manga = Manga.create(url, "", id)
val networkManga = delegate?.fetchMangaDetails(manga)?.toBlocking()?.single() ?: return null
val networkManga = delegate?.getMangaDetails(manga.toMangaInfo())?.toSManga() ?: return null
val newManga = MangaImpl().apply {
this.url = url
title = try { networkManga.title } catch (e: Exception) { "" }
@ -40,6 +42,6 @@ abstract class DelegatedHttpSource {
suspend fun getChapters(url: String): List<SChapter>? {
val id = delegate?.id ?: return null
val manga = Manga.create(url, "", id)
return delegate?.fetchChapterListAsync(manga)
return delegate?.getChapterList(manga.toMangaInfo())?.map { it.toSChapter() }
}
}

View File

@ -6,7 +6,7 @@ import android.view.View
import android.widget.CompoundButton
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import com.f2prateek.rx.preferences.Preference
import com.tfcporciuncula.flow.Preference
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
@ -99,18 +99,18 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
}
view.title.hint = category.name
view.title.append(category.name)
val downloadNew = preferences.downloadNew().getOrDefault()
val downloadNew = preferences.downloadNew().get()
setCheckbox(
view.download_new,
preferences.downloadNewCategories(),
true
)
if (downloadNew && preferences.downloadNewCategories().getOrDefault().isEmpty())
if (downloadNew && preferences.downloadNewCategories().get().isEmpty())
view.download_new.gone()
else if (!downloadNew)
view.download_new.visible()
view.download_new.isChecked =
preferences.downloadNew().getOrDefault() && view.download_new.isChecked
preferences.downloadNew().get() && view.download_new.isChecked
setCheckbox(
view.include_global,
preferences.libraryUpdateCategories(),
@ -121,7 +121,7 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
/** Update a pref based on checkbox, and return if the pref is not empty */
private fun updatePref(categories: Preference<Set<String>>, box: CompoundButton): Boolean {
val categoryId = category.id ?: return true
val updateCategories = categories.getOrDefault().toMutableSet()
val updateCategories = categories.get().toMutableSet()
if (box.isChecked) {
updateCategories.add(categoryId.toString())
} else {
@ -136,7 +136,7 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
categories: Preference<Set<String>>,
shouldShow: Boolean
) {
val updateCategories = categories.getOrDefault()
val updateCategories = categories.get()
box.visibleIf(updateCategories.isNotEmpty() && shouldShow)
if (updateCategories.isNotEmpty() && shouldShow) box.isChecked =
updateCategories.any { category.id == it.toIntOrNull() }

View File

@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
@ -25,9 +26,8 @@ import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.fetchChapterListAsync
import eu.kanade.tachiyomi.source.fetchMangaDetailsAsync
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.toSChapter
import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
@ -35,6 +35,7 @@ import eu.kanade.tachiyomi.util.chapter.ChapterFilter
import eu.kanade.tachiyomi.util.chapter.ChapterUtil
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.trimOrNull
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.system.executeOnIO
@ -316,16 +317,16 @@ class MangaDetailsPresenter(
var chapterError: java.lang.Exception? = null
val chapters = async(Dispatchers.IO) {
try {
source.fetchChapterListAsync(manga)
source.getChapterList(manga.toMangaInfo()).map { it.toSChapter() }
} catch (e: Exception) {
chapterError = e
emptyList<SChapter>()
} ?: emptyList()
emptyList()
}
}
val thumbnailUrl = manga.thumbnail_url
val nManga = async(Dispatchers.IO) {
try {
source.fetchMangaDetailsAsync(manga)
source.getMangaDetails(manga.toMangaInfo()).toSManga()
} catch (e: java.lang.Exception) {
mangaError = e
null
@ -350,18 +351,13 @@ class MangaDetailsPresenter(
if (finChapters.isNotEmpty()) {
val newChapters = syncChaptersWithSource(db, finChapters, manga, source)
if (newChapters.first.isNotEmpty()) {
val downloadNew = preferences.downloadNew().getOrDefault()
if (downloadNew && !controller.fromCatalogue && mangaWasInitalized) {
val categoriesToDownload = preferences.downloadNewCategories().getOrDefault().map(String::toInt)
val shouldDownload = categoriesToDownload.isEmpty() || getMangaCategoryIds().any { it in categoriesToDownload }
if (shouldDownload) {
if (manga.shouldDownloadNewChapters(db, preferences)) {
downloadChapters(
newChapters.first.sortedBy { it.chapter_number }
.map { it.toModel() }
)
}
}
}
if (newChapters.second.isNotEmpty()) {
val removedChaptersId = newChapters.second.map { it.id }
val removedChapters = this@MangaDetailsPresenter.chapters.filter {
@ -403,7 +399,7 @@ class MangaDetailsPresenter(
scope.launch(Dispatchers.IO) {
val chapters = try {
source.fetchChapterListAsync(manga)
source.getChapterList(manga.toMangaInfo()).map { it.toSChapter() }
} catch (e: Exception) {
withContext(Dispatchers.Main) { controller.showError(trimException(e)) }
return@launch

View File

@ -18,6 +18,7 @@ import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.smartsearch.SmartSearchEngine
@ -25,6 +26,8 @@ import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.toSChapter
import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.ui.main.BottomNavBarInterface
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
@ -184,10 +187,7 @@ class MigrationListController(bundle: Bundle? = null) :
source.id
)
val chapters =
source.fetchChapterList(localManga).toSingle()
.await(
Schedulers.io()
)
source.getChapterList(localManga.toMangaInfo()).map { it.toSChapter() }
try {
syncChaptersWithSource(
db,
@ -226,8 +226,7 @@ class MigrationListController(bundle: Bundle? = null) :
source.id
)
val chapters = try {
source.fetchChapterList(localManga).toSingle()
.await(Schedulers.io())
source.getChapterList(localManga.toMangaInfo()).map { it.toSChapter() }
} catch (e: java.lang.Exception) {
Timber.e(e)
emptyList<SChapter>()
@ -260,8 +259,7 @@ class MigrationListController(bundle: Bundle? = null) :
if (result != null && result.thumbnail_url == null) {
try {
val newManga =
sourceManager.getOrStub(result.source).fetchMangaDetails(result)
.toSingle().await()
sourceManager.getOrStub(result.source).getMangaDetails(result.toMangaInfo()).toSManga()
result.copyFrom(newManga)
db.insertManga(result).executeAsBlocking()
@ -366,9 +364,7 @@ class MigrationListController(bundle: Bundle? = null) :
launchUI {
val result = CoroutineScope(migratingManga.manga.migrationJob).async {
val localManga = smartSearchEngine.networkToLocalManga(manga, source.id)
val chapters = source.fetchChapterList(localManga).toSingle().await(
Schedulers.io()
)
val chapters = source.getChapterList(localManga.toMangaInfo()).map { it.toSChapter() }
try {
syncChaptersWithSource(db, chapters, localManga, source)
} catch (e: Exception) {
@ -380,8 +376,8 @@ class MigrationListController(bundle: Bundle? = null) :
if (result != null) {
try {
val newManga =
sourceManager.getOrStub(result.source).fetchMangaDetails(result).toSingle()
.await()
sourceManager.getOrStub(result.source).getMangaDetails(result.toMangaInfo())
.toSManga()
result.copyFrom(newManga)
db.insertManga(result).executeAsBlocking()

View File

@ -16,6 +16,7 @@ import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.system.getFilePicker
@ -93,12 +94,10 @@ class SettingsDownloadController : SettingsController() {
entryValues = dbCategories.map { it.id.toString() }
allSelectionRes = R.string.all
preferences.downloadNew().asObservable()
.subscribeUntilDestroy { isVisible = it }
preferences.downloadNew().asImmediateFlow { isVisible = it }
preferences.downloadNewCategories().asObservable()
.subscribeUntilDestroy {
val selectedCategories = it
preferences.downloadNewCategories().asImmediateFlow { list ->
val selectedCategories = list
.mapNotNull { id -> dbCategories.find { it.id == id.toInt() } }
.sortedBy { it.order }

View File

@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.ui.category.CategoryController
import eu.kanade.tachiyomi.util.view.withFadeTransaction
import uy.kohesive.injekt.Injekt
@ -133,9 +134,9 @@ class SettingsLibraryController : SettingsController() {
entryValues = dbCategories.map { it.id.toString() }
allSelectionRes = R.string.all
preferences.libraryUpdateCategories().asObservable().subscribeUntilDestroy {
preferences.libraryUpdateCategories().asImmediateFlow { list ->
val selectedCategories =
it.mapNotNull { id -> dbCategories.find { it.id == id.toInt() } }
list.mapNotNull { id -> dbCategories.find { it.id == id.toInt() } }
.sortedBy { it.order }
customSummary =

View File

@ -0,0 +1,73 @@
package eu.kanade.tachiyomi.util
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.model.SManga
import java.util.Date
fun Manga.isLocal() = source == LocalSource.ID
/*
/**
* Call before updating [Manga.thumbnail_url] to ensure old cover can be cleared from cache
*/
fun Manga.prepUpdateCover(coverCache: CoverCache, remoteManga: SManga, refreshSameUrl: Boolean) {
// Never refresh covers if the new url is null, as the current url has possibly become invalid
val newUrl = remoteManga.thumbnail_url ?: return
// Never refresh covers if the url is empty to avoid "losing" existing covers
if (newUrl.isEmpty()) return
if (!refreshSameUrl && thumbnail_url == newUrl) return
when {
isLocal() -> {
cover_last_modified = Date().time
}
hasCustomCover(coverCache) -> {
coverCache.deleteFromCache(this, false)
}
else -> {
cover_last_modified = Date().time
coverCache.deleteFromCache(this, false)
}
}
}
fun Manga.hasCustomCover(coverCache: CoverCache): Boolean {
return coverCache.getCustomCoverFile(this).exists()
}
fun Manga.removeCovers(coverCache: CoverCache) {
if (isLocal()) return
cover_last_modified = Date().time
coverCache.deleteFromCache(this, true)
}
fun Manga.updateCoverLastModified(db: DatabaseHelper) {
cover_last_modified = Date().time
db.updateMangaCoverLastModified(this).executeAsBlocking()
}*/
fun Manga.shouldDownloadNewChapters(db: DatabaseHelper, prefs: PreferencesHelper): Boolean {
if (!favorite) return false
// Boolean to determine if user wants to automatically download new chapters.
val downloadNew = prefs.downloadNew().get()
if (!downloadNew) return false
val categoriesToDownload = prefs.downloadNewCategories().get().map(String::toInt)
if (categoriesToDownload.isEmpty()) return true
// Get all categories, else default category (0)
val categoriesForManga =
db.getCategoriesForManga(this).executeAsBlocking()
.mapNotNull { it.id }
.takeUnless { it.isEmpty() } ?: listOf(0)
return categoriesForManga.intersect(categoriesToDownload).isNotEmpty()
}

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.data.backup
import android.app.Application
import android.app.backup.BackupManager
import android.content.Context
import android.os.Build
import com.github.salomonbrys.kotson.fromJson
@ -8,8 +9,9 @@ import com.google.gson.JsonArray
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.CustomRobolectricGradleTestRunner
import eu.kanade.tachiyomi.data.backup.models.Backup
import eu.kanade.tachiyomi.data.backup.models.DHistory
import eu.kanade.tachiyomi.data.backup.legacy.LegacyBackupManager
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup
import eu.kanade.tachiyomi.data.backup.legacy.models.DHistory
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Chapter
@ -62,7 +64,7 @@ class BackupTest {
lateinit var context: Context
lateinit var source: HttpSource
lateinit var backupManager: BackupManager
lateinit var backupManager: LegacyBackupManager
lateinit var db: DatabaseHelper
@ -70,7 +72,7 @@ class BackupTest {
fun setup() {
app = RuntimeEnvironment.application
context = app.applicationContext
backupManager = BackupManager(context)
backupManager = LegacyBackupManager(context)
db = backupManager.databaseHelper
// Mock the source manager
@ -93,9 +95,6 @@ class BackupTest {
*/
@Test
fun testRestoreEmptyCategory() {
// Initialize json with version 2
initializeJsonTest(2)
// Create backup of empty database
backupManager.backupCategories(categoryEntries)
@ -112,9 +111,6 @@ class BackupTest {
*/
@Test
fun testRestoreSingleCategory() {
// Initialize json with version 2
initializeJsonTest(2)
// Create category and add to json
val category = addSingleCategory("category")
@ -132,9 +128,6 @@ class BackupTest {
*/
@Test
fun testRestoreMultipleCategories() {
// Initialize json with version 2
initializeJsonTest(2)
// Create category and add to json
val category = addSingleCategory("category")
val category2 = addSingleCategory("category2")
@ -163,9 +156,6 @@ class BackupTest {
*/
@Test
fun testRestoreManga() {
// Initialize json with version 2
initializeJsonTest(2)
// Add manga to database
val manga = getSingleManga("One Piece")
manga.viewer = 3
@ -215,7 +205,7 @@ class BackupTest {
GlobalScope.launch {
try {
backupManager.restoreMangaFetch(source, jsonManga)
backupManager.fetchManga(source, jsonManga)
} catch (e: Exception) {
fail("Unexpected onError events")
}
@ -233,9 +223,6 @@ class BackupTest {
*/
@Test
fun testRestoreChapters() {
// Initialize json with version 2
initializeJsonTest(2)
// Insert manga
val manga = getSingleManga("One Piece")
manga.id = backupManager.databaseHelper.insertManga(manga).executeAsBlocking().insertedId()
@ -261,7 +248,7 @@ class BackupTest {
// Call restoreChapterFetchObservable
GlobalScope.launch {
try {
backupManager.restoreChapterFetch(source, manga, restoredChapters)
backupManager.restoreChapters(source, manga, restoredChapters)
} catch (e: Exception) {
fail("Unexpected onError events")
}
@ -277,9 +264,6 @@ class BackupTest {
*/
@Test
fun restoreHistoryForManga() {
// Initialize json with version 2
initializeJsonTest(2)
val manga = getSingleManga("One Piece")
manga.id = backupManager.databaseHelper.insertManga(manga).executeAsBlocking().insertedId()
@ -311,9 +295,6 @@ class BackupTest {
*/
@Test
fun restoreTrackForManga() {
// Initialize json with version 2
initializeJsonTest(2)
// Create mangas
val manga = getSingleManga("One Piece")
val manga2 = getSingleManga("Bleach")
@ -377,11 +358,6 @@ class BackupTest {
categoryEntries = JsonArray()
}
fun initializeJsonTest(version: Int) {
clearJson()
backupManager.setVersion(version)
}
fun addSingleCategory(name: String): Category {
val category = Category.create(name)
val catJson = backupManager.parser.toJsonTree(category)