Rewrite Migrations (#577)
* Rewrite Migrations * Fix Detekt errors * Do migrations synchronous * Filter and sort migrations * Review changes * Review changes 2 * Fix Detekt errors (cherry picked from commit 666d6aa117756f0a9a57b31f91b7acb0ee5d7409) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/Migrations.kt # app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
This commit is contained in:
parent
ceff887a10
commit
90d5104bdc
@ -1,71 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
|
||||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import logcat.LogPriority
|
|
||||||
import mihon.domain.extensionrepo.exception.SaveExtensionRepoException
|
|
||||||
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
|
||||||
import tachiyomi.core.common.preference.Preference
|
|
||||||
import tachiyomi.core.common.preference.PreferenceStore
|
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
|
||||||
import tachiyomi.core.common.util.system.logcat
|
|
||||||
|
|
||||||
object Migrations {
|
|
||||||
|
|
||||||
// TODO NATIVE TACHIYOMI MIGRATIONS ARE FUCKED UP DUE TO DIFFERING VERSION NUMBERS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs a migration when the application is updated.
|
|
||||||
*
|
|
||||||
* @return true if a migration is performed, false otherwise.
|
|
||||||
*/
|
|
||||||
@Suppress("SameReturnValue", "MagicNumber")
|
|
||||||
fun upgrade(
|
|
||||||
context: Context,
|
|
||||||
preferenceStore: PreferenceStore,
|
|
||||||
sourcePreferences: SourcePreferences,
|
|
||||||
extensionRepoRepository: ExtensionRepoRepository,
|
|
||||||
): Boolean {
|
|
||||||
val lastVersionCode = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0)
|
|
||||||
val oldVersion = lastVersionCode.get()
|
|
||||||
if (oldVersion < BuildConfig.VERSION_CODE) {
|
|
||||||
lastVersionCode.set(BuildConfig.VERSION_CODE)
|
|
||||||
|
|
||||||
// Always set up background tasks to ensure they're running
|
|
||||||
LibraryUpdateJob.setupTask(context)
|
|
||||||
BackupCreateJob.setupTask(context)
|
|
||||||
|
|
||||||
// Fresh install
|
|
||||||
if (oldVersion == 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
val coroutineScope = CoroutineScope(Dispatchers.IO)
|
|
||||||
|
|
||||||
if (oldVersion < 6) {
|
|
||||||
coroutineScope.launchIO {
|
|
||||||
for ((index, source) in sourcePreferences.extensionRepos().get().withIndex()) {
|
|
||||||
try {
|
|
||||||
extensionRepoRepository.upsertRepo(
|
|
||||||
source,
|
|
||||||
"Repo #${index + 1}",
|
|
||||||
null,
|
|
||||||
source,
|
|
||||||
"NOFINGERPRINT-${index + 1}",
|
|
||||||
)
|
|
||||||
} catch (e: SaveExtensionRepoException) {
|
|
||||||
logcat(LogPriority.ERROR, e) { "Error Migrating Extension Repo with baseUrl: $source" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sourcePreferences.extensionRepos().delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
@ -50,8 +50,6 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
|||||||
import com.google.firebase.analytics.ktx.analytics
|
import com.google.firebase.analytics.ktx.analytics
|
||||||
import com.google.firebase.ktx.Firebase
|
import com.google.firebase.ktx.Firebase
|
||||||
import eu.kanade.domain.base.BasePreferences
|
import eu.kanade.domain.base.BasePreferences
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
|
||||||
import eu.kanade.presentation.components.AppStateBanners
|
import eu.kanade.presentation.components.AppStateBanners
|
||||||
import eu.kanade.presentation.components.DownloadedOnlyBannerBackgroundColor
|
import eu.kanade.presentation.components.DownloadedOnlyBannerBackgroundColor
|
||||||
import eu.kanade.presentation.components.IncognitoModeBannerBackgroundColor
|
import eu.kanade.presentation.components.IncognitoModeBannerBackgroundColor
|
||||||
@ -80,7 +78,6 @@ import eu.kanade.tachiyomi.util.system.dpToPx
|
|||||||
import eu.kanade.tachiyomi.util.system.isNavigationBarNeedsScrim
|
import eu.kanade.tachiyomi.util.system.isNavigationBarNeedsScrim
|
||||||
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
||||||
import eu.kanade.tachiyomi.util.view.setComposeContent
|
import eu.kanade.tachiyomi.util.view.setComposeContent
|
||||||
import exh.EXHMigrations
|
|
||||||
import exh.debug.DebugToggles
|
import exh.debug.DebugToggles
|
||||||
import exh.eh.EHentaiUpdateWorker
|
import exh.eh.EHentaiUpdateWorker
|
||||||
import exh.log.DebugModeOverlay
|
import exh.log.DebugModeOverlay
|
||||||
@ -97,7 +94,11 @@ import kotlinx.coroutines.flow.launchIn
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
import mihon.core.migration.Migrator
|
||||||
|
import mihon.core.migration.migrations.migrations
|
||||||
import tachiyomi.core.common.Constants
|
import tachiyomi.core.common.Constants
|
||||||
|
import tachiyomi.core.common.preference.Preference
|
||||||
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.UnsortedPreferences
|
import tachiyomi.domain.UnsortedPreferences
|
||||||
@ -113,9 +114,7 @@ import androidx.compose.ui.graphics.Color.Companion as ComposeColor
|
|||||||
|
|
||||||
class MainActivity : BaseActivity() {
|
class MainActivity : BaseActivity() {
|
||||||
|
|
||||||
private val sourcePreferences: SourcePreferences by injectLazy()
|
|
||||||
private val libraryPreferences: LibraryPreferences by injectLazy()
|
private val libraryPreferences: LibraryPreferences by injectLazy()
|
||||||
private val uiPreferences: UiPreferences by injectLazy()
|
|
||||||
private val preferences: BasePreferences by injectLazy()
|
private val preferences: BasePreferences by injectLazy()
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -165,21 +164,7 @@ class MainActivity : BaseActivity() {
|
|||||||
|
|
||||||
val didMigration = if (isLaunch) {
|
val didMigration = if (isLaunch) {
|
||||||
addAnalytics()
|
addAnalytics()
|
||||||
EXHMigrations.upgrade(
|
migrate()
|
||||||
context = applicationContext,
|
|
||||||
basePreferences = preferences,
|
|
||||||
uiPreferences = uiPreferences,
|
|
||||||
preferenceStore = Injekt.get(),
|
|
||||||
networkPreferences = Injekt.get(),
|
|
||||||
sourcePreferences = sourcePreferences,
|
|
||||||
securityPreferences = Injekt.get(),
|
|
||||||
libraryPreferences = libraryPreferences,
|
|
||||||
readerPreferences = Injekt.get(),
|
|
||||||
backupPreferences = Injekt.get(),
|
|
||||||
trackerManager = Injekt.get(),
|
|
||||||
pagePreviewCache = Injekt.get(),
|
|
||||||
extensionRepoRepository = Injekt.get(),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -423,6 +408,23 @@ class MainActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun migrate(): Boolean {
|
||||||
|
val preferenceStore = Injekt.get<PreferenceStore>()
|
||||||
|
// SY -->
|
||||||
|
val preference = preferenceStore.getInt(Preference.appStateKey("eh_last_version_code"), 0)
|
||||||
|
// SY <--
|
||||||
|
logcat { "Migration from ${preference.get()} to ${BuildConfig.VERSION_CODE}" }
|
||||||
|
return Migrator.migrate(
|
||||||
|
old = preference.get(),
|
||||||
|
new = BuildConfig.VERSION_CODE,
|
||||||
|
migrations = migrations,
|
||||||
|
onMigrationComplete = {
|
||||||
|
logcat { "Updating last version to ${BuildConfig.VERSION_CODE}" }
|
||||||
|
preference.set(BuildConfig.VERSION_CODE)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets custom splash screen exit animation on devices prior to Android 12.
|
* Sets custom splash screen exit animation on devices prior to Android 12.
|
||||||
*
|
*
|
||||||
|
@ -1,723 +1,15 @@
|
|||||||
package exh
|
package exh
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.content.edit
|
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import eu.kanade.domain.base.BasePreferences
|
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
|
||||||
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
|
||||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
|
||||||
import eu.kanade.tachiyomi.data.cache.PagePreviewCache
|
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
|
||||||
import eu.kanade.tachiyomi.data.sync.SyncDataJob
|
|
||||||
import eu.kanade.tachiyomi.data.track.TrackerManager
|
|
||||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
|
||||||
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
|
|
||||||
import eu.kanade.tachiyomi.source.Source
|
|
||||||
import eu.kanade.tachiyomi.source.online.all.NHentai
|
import eu.kanade.tachiyomi.source.online.all.NHentai
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
|
||||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
|
||||||
import eu.kanade.tachiyomi.util.system.workManager
|
|
||||||
import exh.eh.EHentaiUpdateWorker
|
|
||||||
import exh.log.xLogE
|
|
||||||
import exh.source.BlacklistedSources
|
import exh.source.BlacklistedSources
|
||||||
import exh.source.EH_SOURCE_ID
|
import exh.source.EH_SOURCE_ID
|
||||||
import exh.source.HBROWSE_SOURCE_ID
|
import exh.source.HBROWSE_SOURCE_ID
|
||||||
import exh.source.MERGED_SOURCE_ID
|
|
||||||
import exh.source.TSUMINO_SOURCE_ID
|
import exh.source.TSUMINO_SOURCE_ID
|
||||||
import exh.util.nullIfBlank
|
|
||||||
import exh.util.under
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.serialization.SerialName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlinx.serialization.json.JsonObject
|
|
||||||
import kotlinx.serialization.json.contentOrNull
|
|
||||||
import kotlinx.serialization.json.jsonArray
|
|
||||||
import kotlinx.serialization.json.jsonPrimitive
|
|
||||||
import logcat.LogPriority
|
|
||||||
import mihon.domain.extensionrepo.exception.SaveExtensionRepoException
|
|
||||||
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
|
||||||
import tachiyomi.core.common.preference.Preference
|
|
||||||
import tachiyomi.core.common.preference.PreferenceStore
|
|
||||||
import tachiyomi.core.common.preference.TriState
|
|
||||||
import tachiyomi.core.common.preference.getAndSet
|
|
||||||
import tachiyomi.core.common.preference.getEnum
|
|
||||||
import tachiyomi.core.common.preference.minusAssign
|
|
||||||
import tachiyomi.core.common.util.system.logcat
|
|
||||||
import tachiyomi.data.DatabaseHandler
|
|
||||||
import tachiyomi.data.category.CategoryMapper
|
|
||||||
import tachiyomi.data.chapter.ChapterMapper
|
|
||||||
import tachiyomi.domain.backup.service.BackupPreferences
|
|
||||||
import tachiyomi.domain.chapter.interactor.DeleteChapters
|
|
||||||
import tachiyomi.domain.chapter.interactor.UpdateChapter
|
|
||||||
import tachiyomi.domain.chapter.model.ChapterUpdate
|
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED
|
|
||||||
import tachiyomi.domain.manga.interactor.GetManga
|
|
||||||
import tachiyomi.domain.manga.interactor.GetMangaBySource
|
|
||||||
import tachiyomi.domain.manga.interactor.InsertMergedReference
|
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.manga.model.MangaUpdate
|
|
||||||
import tachiyomi.domain.manga.model.MergedMangaReference
|
|
||||||
import tachiyomi.domain.source.interactor.InsertFeedSavedSearch
|
|
||||||
import tachiyomi.domain.source.interactor.InsertSavedSearch
|
|
||||||
import tachiyomi.domain.source.model.FeedSavedSearch
|
|
||||||
import tachiyomi.domain.source.model.SavedSearch
|
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
import java.io.File
|
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URISyntaxException
|
import java.net.URISyntaxException
|
||||||
|
|
||||||
object EXHMigrations {
|
object EXHMigrations {
|
||||||
private val handler: DatabaseHandler by injectLazy()
|
|
||||||
private val sourceManager: SourceManager by injectLazy()
|
|
||||||
private val getManga: GetManga by injectLazy()
|
|
||||||
private val getMangaBySource: GetMangaBySource by injectLazy()
|
|
||||||
private val updateManga: UpdateManga by injectLazy()
|
|
||||||
private val updateChapter: UpdateChapter by injectLazy()
|
|
||||||
private val deleteChapters: DeleteChapters by injectLazy()
|
|
||||||
private val insertMergedReference: InsertMergedReference by injectLazy()
|
|
||||||
private val insertSavedSearch: InsertSavedSearch by injectLazy()
|
|
||||||
private val insertFeedSavedSearch: InsertFeedSavedSearch by injectLazy()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs a migration when the application is updated.
|
|
||||||
*
|
|
||||||
* @return true if a migration is performed, false otherwise.
|
|
||||||
*/
|
|
||||||
fun upgrade(
|
|
||||||
context: Context,
|
|
||||||
preferenceStore: PreferenceStore,
|
|
||||||
basePreferences: BasePreferences,
|
|
||||||
uiPreferences: UiPreferences,
|
|
||||||
networkPreferences: NetworkPreferences,
|
|
||||||
sourcePreferences: SourcePreferences,
|
|
||||||
securityPreferences: SecurityPreferences,
|
|
||||||
libraryPreferences: LibraryPreferences,
|
|
||||||
readerPreferences: ReaderPreferences,
|
|
||||||
backupPreferences: BackupPreferences,
|
|
||||||
trackerManager: TrackerManager,
|
|
||||||
pagePreviewCache: PagePreviewCache,
|
|
||||||
extensionRepoRepository: ExtensionRepoRepository,
|
|
||||||
): Boolean {
|
|
||||||
val lastVersionCode = preferenceStore.getInt("eh_last_version_code", 0)
|
|
||||||
val oldVersion = lastVersionCode.get()
|
|
||||||
try {
|
|
||||||
if (oldVersion < BuildConfig.VERSION_CODE) {
|
|
||||||
lastVersionCode.set(BuildConfig.VERSION_CODE)
|
|
||||||
|
|
||||||
LibraryUpdateJob.setupTask(context)
|
|
||||||
BackupCreateJob.setupTask(context)
|
|
||||||
EHentaiUpdateWorker.scheduleBackground(context)
|
|
||||||
SyncDataJob.setupTask(context)
|
|
||||||
|
|
||||||
// Fresh install
|
|
||||||
if (oldVersion == 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
|
||||||
if (oldVersion under 4) {
|
|
||||||
updateSourceId(HBROWSE_SOURCE_ID, 6912)
|
|
||||||
// Migrate BHrowse URLs
|
|
||||||
val hBrowseManga = runBlocking { getMangaBySource.await(HBROWSE_SOURCE_ID) }
|
|
||||||
val mangaUpdates = hBrowseManga.map {
|
|
||||||
MangaUpdate(it.id, url = it.url + "/c00001/")
|
|
||||||
}
|
|
||||||
|
|
||||||
runBlocking {
|
|
||||||
updateManga.awaitAll(mangaUpdates)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 6) {
|
|
||||||
updateSourceId(NHentai.otherId, 6907)
|
|
||||||
}
|
|
||||||
if (oldVersion under 7) {
|
|
||||||
val mergedMangas = runBlocking { getMangaBySource.await(MERGED_SOURCE_ID) }
|
|
||||||
|
|
||||||
if (mergedMangas.isNotEmpty()) {
|
|
||||||
val mangaConfigs = mergedMangas.mapNotNull { mergedManga ->
|
|
||||||
readMangaConfig(mergedManga)?.let { mergedManga to it }
|
|
||||||
}
|
|
||||||
if (mangaConfigs.isNotEmpty()) {
|
|
||||||
val mangaToUpdate = mutableListOf<MangaUpdate>()
|
|
||||||
val mergedMangaReferences = mutableListOf<MergedMangaReference>()
|
|
||||||
mangaConfigs.onEach { mergedManga ->
|
|
||||||
val newFirst = mergedManga.second.children.firstOrNull()?.url?.let {
|
|
||||||
if (runBlocking { getManga.await(it, MERGED_SOURCE_ID) } != null) return@onEach
|
|
||||||
mangaToUpdate += MangaUpdate(id = mergedManga.first.id, url = it)
|
|
||||||
mergedManga.first.copy(url = it)
|
|
||||||
} ?: mergedManga.first
|
|
||||||
mergedMangaReferences += MergedMangaReference(
|
|
||||||
id = -1,
|
|
||||||
isInfoManga = false,
|
|
||||||
getChapterUpdates = false,
|
|
||||||
chapterSortMode = 0,
|
|
||||||
chapterPriority = 0,
|
|
||||||
downloadChapters = false,
|
|
||||||
mergeId = newFirst.id,
|
|
||||||
mergeUrl = newFirst.url,
|
|
||||||
mangaId = newFirst.id,
|
|
||||||
mangaUrl = newFirst.url,
|
|
||||||
mangaSourceId = MERGED_SOURCE_ID,
|
|
||||||
)
|
|
||||||
mergedManga.second.children.distinct().forEachIndexed { index, mangaSource ->
|
|
||||||
val load = mangaSource.load() ?: return@forEachIndexed
|
|
||||||
mergedMangaReferences += MergedMangaReference(
|
|
||||||
id = -1,
|
|
||||||
isInfoManga = index == 0,
|
|
||||||
getChapterUpdates = true,
|
|
||||||
chapterSortMode = 0,
|
|
||||||
chapterPriority = 0,
|
|
||||||
downloadChapters = true,
|
|
||||||
mergeId = newFirst.id,
|
|
||||||
mergeUrl = newFirst.url,
|
|
||||||
mangaId = load.manga.id,
|
|
||||||
mangaUrl = load.manga.url,
|
|
||||||
mangaSourceId = load.source.id,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runBlocking {
|
|
||||||
updateManga.awaitAll(mangaToUpdate)
|
|
||||||
insertMergedReference.awaitAll(mergedMangaReferences)
|
|
||||||
}
|
|
||||||
|
|
||||||
val loadedMangaList = mangaConfigs
|
|
||||||
.map { it.second.children }
|
|
||||||
.flatten()
|
|
||||||
.mapNotNull { it.load() }
|
|
||||||
.distinct()
|
|
||||||
val chapters =
|
|
||||||
runBlocking {
|
|
||||||
handler.awaitList {
|
|
||||||
ehQueries.getChaptersByMangaIds(
|
|
||||||
mergedMangas.map { it.id },
|
|
||||||
ChapterMapper::mapChapter,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val mergedMangaChapters =
|
|
||||||
runBlocking {
|
|
||||||
handler.awaitList {
|
|
||||||
ehQueries.getChaptersByMangaIds(
|
|
||||||
loadedMangaList.map { it.manga.id },
|
|
||||||
ChapterMapper::mapChapter,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val mergedMangaChaptersMatched = mergedMangaChapters.mapNotNull { chapter ->
|
|
||||||
loadedMangaList.firstOrNull {
|
|
||||||
it.manga.id == chapter.id
|
|
||||||
}?.let { it to chapter }
|
|
||||||
}
|
|
||||||
val parsedChapters = chapters.filter {
|
|
||||||
it.read || it.lastPageRead != 0L
|
|
||||||
}.mapNotNull { chapter -> readUrlConfig(chapter.url)?.let { chapter to it } }
|
|
||||||
val chaptersToUpdate = mutableListOf<ChapterUpdate>()
|
|
||||||
parsedChapters.forEach { parsedChapter ->
|
|
||||||
mergedMangaChaptersMatched.firstOrNull {
|
|
||||||
it.second.url == parsedChapter.second.url &&
|
|
||||||
it.first.source.id == parsedChapter.second.source &&
|
|
||||||
it.first.manga.url == parsedChapter.second.mangaUrl
|
|
||||||
}?.let {
|
|
||||||
chaptersToUpdate += ChapterUpdate(
|
|
||||||
it.second.id,
|
|
||||||
read = parsedChapter.first.read,
|
|
||||||
lastPageRead = parsedChapter.first.lastPageRead,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runBlocking {
|
|
||||||
deleteChapters.await(mergedMangaChapters.map { it.id })
|
|
||||||
updateChapter.awaitAll(chaptersToUpdate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 12) {
|
|
||||||
// Force MAL log out due to login flow change
|
|
||||||
trackerManager.myAnimeList.logout()
|
|
||||||
}
|
|
||||||
if (oldVersion under 14) {
|
|
||||||
// Migrate DNS over HTTPS setting
|
|
||||||
val wasDohEnabled = prefs.getBoolean("enable_doh", false)
|
|
||||||
if (wasDohEnabled) {
|
|
||||||
prefs.edit {
|
|
||||||
putInt(networkPreferences.dohProvider().key(), PREF_DOH_CLOUDFLARE)
|
|
||||||
remove("enable_doh")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 16) {
|
|
||||||
// Reset rotation to Free after replacing Lock
|
|
||||||
if (prefs.contains("pref_rotation_type_key")) {
|
|
||||||
prefs.edit {
|
|
||||||
putInt("pref_rotation_type_key", 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Disable update check for Android 5.x users
|
|
||||||
// if (BuildConfig.INCLUDE_UPDATER && Build.VERSION.SDK_INT under Build.VERSION_CODES.M) {
|
|
||||||
// UpdaterJob.cancelTask(context)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
if (oldVersion under 17) {
|
|
||||||
// Migrate Rotation and Viewer values to default values for viewer_flags
|
|
||||||
val newOrientation = when (prefs.getInt("pref_rotation_type_key", 1)) {
|
|
||||||
1 -> ReaderOrientation.FREE.flagValue
|
|
||||||
2 -> ReaderOrientation.PORTRAIT.flagValue
|
|
||||||
3 -> ReaderOrientation.LANDSCAPE.flagValue
|
|
||||||
4 -> ReaderOrientation.LOCKED_PORTRAIT.flagValue
|
|
||||||
5 -> ReaderOrientation.LOCKED_LANDSCAPE.flagValue
|
|
||||||
else -> ReaderOrientation.FREE.flagValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reading mode flag and prefValue is the same value
|
|
||||||
val newReadingMode = prefs.getInt("pref_default_viewer_key", 1)
|
|
||||||
|
|
||||||
prefs.edit {
|
|
||||||
putInt("pref_default_orientation_type_key", newOrientation)
|
|
||||||
remove("pref_rotation_type_key")
|
|
||||||
putInt("pref_default_reading_mode_key", newReadingMode)
|
|
||||||
remove("pref_default_viewer_key")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete old mangadex trackers
|
|
||||||
runBlocking {
|
|
||||||
handler.await { ehQueries.deleteBySyncId(6) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 18) {
|
|
||||||
val readerTheme = readerPreferences.readerTheme().get()
|
|
||||||
if (readerTheme == 4) {
|
|
||||||
readerPreferences.readerTheme().set(3)
|
|
||||||
}
|
|
||||||
val updateInterval = libraryPreferences.autoUpdateInterval().get()
|
|
||||||
if (updateInterval == 1 || updateInterval == 2) {
|
|
||||||
libraryPreferences.autoUpdateInterval().set(3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 20) {
|
|
||||||
try {
|
|
||||||
val oldSortingMode = prefs.getInt(libraryPreferences.sortingMode().key(), 0 /* ALPHABETICAL */)
|
|
||||||
val oldSortingDirection = prefs.getBoolean("library_sorting_ascending", true)
|
|
||||||
|
|
||||||
val newSortingMode = when (oldSortingMode) {
|
|
||||||
0 -> "ALPHABETICAL"
|
|
||||||
1 -> "LAST_READ"
|
|
||||||
2 -> "LAST_MANGA_UPDATE"
|
|
||||||
3 -> "UNREAD_COUNT"
|
|
||||||
4 -> "TOTAL_CHAPTERS"
|
|
||||||
6 -> "LATEST_CHAPTER"
|
|
||||||
7 -> "DRAG_AND_DROP"
|
|
||||||
8 -> "DATE_ADDED"
|
|
||||||
9 -> "TAG_LIST"
|
|
||||||
10 -> "CHAPTER_FETCH_DATE"
|
|
||||||
else -> "ALPHABETICAL"
|
|
||||||
}
|
|
||||||
|
|
||||||
val newSortingDirection = when (oldSortingDirection) {
|
|
||||||
true -> "ASCENDING"
|
|
||||||
else -> "DESCENDING"
|
|
||||||
}
|
|
||||||
|
|
||||||
prefs.edit(commit = true) {
|
|
||||||
remove(libraryPreferences.sortingMode().key())
|
|
||||||
remove("library_sorting_ascending")
|
|
||||||
}
|
|
||||||
|
|
||||||
prefs.edit {
|
|
||||||
putString(libraryPreferences.sortingMode().key(), newSortingMode)
|
|
||||||
putString("library_sorting_ascending", newSortingDirection)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logcat(throwable = e) { "Already done migration" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 22) {
|
|
||||||
// Handle removed every 3, 4, 6, and 8 hour library updates
|
|
||||||
val updateInterval = libraryPreferences.autoUpdateInterval().get()
|
|
||||||
if (updateInterval in listOf(3, 4, 6, 8)) {
|
|
||||||
libraryPreferences.autoUpdateInterval().set(12)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 23) {
|
|
||||||
val oldUpdateOngoingOnly = prefs.getBoolean("pref_update_only_non_completed_key", true)
|
|
||||||
if (!oldUpdateOngoingOnly) {
|
|
||||||
libraryPreferences.autoUpdateMangaRestrictions() -= MANGA_NON_COMPLETED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 24) {
|
|
||||||
try {
|
|
||||||
sequenceOf(
|
|
||||||
"fav-sync",
|
|
||||||
"fav-sync.management",
|
|
||||||
"fav-sync.lock",
|
|
||||||
"fav-sync.note",
|
|
||||||
).map {
|
|
||||||
File(context.filesDir, it)
|
|
||||||
}.filter(File::exists).forEach {
|
|
||||||
if (it.isDirectory) {
|
|
||||||
it.deleteRecursively()
|
|
||||||
} else {
|
|
||||||
it.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
xLogE("Failed to delete old favorites database", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 27) {
|
|
||||||
val oldSecureScreen = prefs.getBoolean("secure_screen", false)
|
|
||||||
if (oldSecureScreen) {
|
|
||||||
securityPreferences.secureScreen().set(SecurityPreferences.SecureScreenMode.ALWAYS)
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
DeviceUtil.isMiui &&
|
|
||||||
basePreferences.extensionInstaller().get() == BasePreferences.ExtensionInstaller
|
|
||||||
.PACKAGEINSTALLER
|
|
||||||
) {
|
|
||||||
basePreferences.extensionInstaller().set(BasePreferences.ExtensionInstaller.LEGACY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 28) {
|
|
||||||
if (prefs.getString("pref_display_mode_library", null) == "NO_TITLE_GRID") {
|
|
||||||
prefs.edit(commit = true) {
|
|
||||||
putString("pref_display_mode_library", "COVER_ONLY_GRID")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 29) {
|
|
||||||
if (prefs.getString("pref_display_mode_catalogue", null) == "NO_TITLE_GRID") {
|
|
||||||
prefs.edit(commit = true) {
|
|
||||||
putString("pref_display_mode_catalogue", "COMPACT_GRID")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 31) {
|
|
||||||
runBlocking {
|
|
||||||
val savedSearch = prefs.getStringSet("eh_saved_searches", emptySet())?.mapNotNull {
|
|
||||||
runCatching {
|
|
||||||
val content = Json.decodeFromString<JsonObject>(it.substringAfter(':'))
|
|
||||||
SavedSearch(
|
|
||||||
id = -1,
|
|
||||||
source = it.substringBefore(':').toLongOrNull()
|
|
||||||
?: return@runCatching null,
|
|
||||||
name = content["name"]!!.jsonPrimitive.content,
|
|
||||||
query = content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
|
|
||||||
filtersJson = Json.encodeToString(content["filters"]!!.jsonArray),
|
|
||||||
)
|
|
||||||
}.getOrNull()
|
|
||||||
}
|
|
||||||
if (!savedSearch.isNullOrEmpty()) {
|
|
||||||
insertSavedSearch.awaitAll(savedSearch)
|
|
||||||
}
|
|
||||||
val feedSavedSearch = prefs.getStringSet("latest_tab_sources", emptySet())?.map {
|
|
||||||
FeedSavedSearch(
|
|
||||||
id = -1,
|
|
||||||
source = it.toLong(),
|
|
||||||
savedSearch = null,
|
|
||||||
global = true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (!feedSavedSearch.isNullOrEmpty()) {
|
|
||||||
insertFeedSavedSearch.awaitAll(feedSavedSearch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prefs.edit(commit = true) {
|
|
||||||
remove("eh_saved_searches")
|
|
||||||
remove("latest_tab_sources")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 32) {
|
|
||||||
val oldReaderTap = prefs.getBoolean("reader_tap", false)
|
|
||||||
if (!oldReaderTap) {
|
|
||||||
readerPreferences.navigationModePager().set(5)
|
|
||||||
readerPreferences.navigationModeWebtoon().set(5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 38) {
|
|
||||||
// Handle renamed enum values
|
|
||||||
val newSortingMode = when (
|
|
||||||
val oldSortingMode = prefs.getString(libraryPreferences.sortingMode().key(), "ALPHABETICAL")
|
|
||||||
) {
|
|
||||||
"LAST_CHECKED" -> "LAST_MANGA_UPDATE"
|
|
||||||
"UNREAD" -> "UNREAD_COUNT"
|
|
||||||
"DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
|
|
||||||
"DRAG_AND_DROP" -> "ALPHABETICAL"
|
|
||||||
else -> oldSortingMode
|
|
||||||
}
|
|
||||||
prefs.edit {
|
|
||||||
putString(libraryPreferences.sortingMode().key(), newSortingMode)
|
|
||||||
}
|
|
||||||
runBlocking {
|
|
||||||
handler.await(true) {
|
|
||||||
categoriesQueries.getCategories(CategoryMapper::mapCategory).executeAsList()
|
|
||||||
.filter { (it.flags and 0b00111100L) == 0b00100000L }
|
|
||||||
.forEach {
|
|
||||||
categoriesQueries.update(
|
|
||||||
categoryId = it.id,
|
|
||||||
flags = it.flags and 0b00111100L.inv(),
|
|
||||||
name = null,
|
|
||||||
order = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 39) {
|
|
||||||
prefs.edit {
|
|
||||||
val sort = prefs.getString(libraryPreferences.sortingMode().key(), null) ?: return@edit
|
|
||||||
val direction = prefs.getString("library_sorting_ascending", "ASCENDING")!!
|
|
||||||
putString(libraryPreferences.sortingMode().key(), "$sort,$direction")
|
|
||||||
remove("library_sorting_ascending")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 40) {
|
|
||||||
if (backupPreferences.backupInterval().get() == 0) {
|
|
||||||
backupPreferences.backupInterval().set(12)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 41) {
|
|
||||||
val preferences = listOf(
|
|
||||||
libraryPreferences.filterChapterByRead(),
|
|
||||||
libraryPreferences.filterChapterByDownloaded(),
|
|
||||||
libraryPreferences.filterChapterByBookmarked(),
|
|
||||||
libraryPreferences.sortChapterBySourceOrNumber(),
|
|
||||||
libraryPreferences.displayChapterByNameOrNumber(),
|
|
||||||
libraryPreferences.sortChapterByAscendingOrDescending(),
|
|
||||||
)
|
|
||||||
|
|
||||||
prefs.edit {
|
|
||||||
preferences.forEach { preference ->
|
|
||||||
val key = preference.key()
|
|
||||||
val value = prefs.getInt(key, Int.MIN_VALUE)
|
|
||||||
if (value == Int.MIN_VALUE) return@forEach
|
|
||||||
remove(key)
|
|
||||||
putLong(key, value.toLong())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 42) {
|
|
||||||
if (uiPreferences.themeMode().isSet()) {
|
|
||||||
prefs.edit {
|
|
||||||
val themeMode = prefs.getString(uiPreferences.themeMode().key(), null) ?: return@edit
|
|
||||||
putString(uiPreferences.themeMode().key(), themeMode.uppercase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 43) {
|
|
||||||
if (preferenceStore.getBoolean("start_reading_button").get()) {
|
|
||||||
libraryPreferences.showContinueReadingButton().set(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 44) {
|
|
||||||
val trackingQueuePref = context.getSharedPreferences("tracking_queue", Context.MODE_PRIVATE)
|
|
||||||
trackingQueuePref.all.forEach {
|
|
||||||
val (_, lastChapterRead) = it.value.toString().split(":")
|
|
||||||
trackingQueuePref.edit {
|
|
||||||
remove(it.key)
|
|
||||||
putFloat(it.key, lastChapterRead.toFloat())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 45) {
|
|
||||||
// Force MangaDex log out due to login flow change
|
|
||||||
trackerManager.mdList.logout()
|
|
||||||
}
|
|
||||||
if (oldVersion under 52) {
|
|
||||||
// Removed background jobs
|
|
||||||
context.workManager.cancelAllWorkByTag("UpdateChecker")
|
|
||||||
context.workManager.cancelAllWorkByTag("ExtensionUpdate")
|
|
||||||
prefs.edit {
|
|
||||||
remove("automatic_ext_updates")
|
|
||||||
}
|
|
||||||
val prefKeys = listOf(
|
|
||||||
"pref_filter_library_downloaded",
|
|
||||||
"pref_filter_library_unread",
|
|
||||||
"pref_filter_library_started",
|
|
||||||
"pref_filter_library_bookmarked",
|
|
||||||
"pref_filter_library_completed",
|
|
||||||
"pref_filter_library_lewd",
|
|
||||||
) + trackerManager.trackers.map { "pref_filter_library_tracked_${it.id}" }
|
|
||||||
|
|
||||||
prefKeys.forEach { key ->
|
|
||||||
val pref = preferenceStore.getInt(key, 0)
|
|
||||||
prefs.edit {
|
|
||||||
remove(key)
|
|
||||||
|
|
||||||
val newValue = when (pref.get()) {
|
|
||||||
1 -> TriState.ENABLED_IS
|
|
||||||
2 -> TriState.ENABLED_NOT
|
|
||||||
else -> TriState.DISABLED
|
|
||||||
}
|
|
||||||
|
|
||||||
preferenceStore.getEnum("${key}_v2", TriState.DISABLED).set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if (oldVersion under 53) {
|
|
||||||
// // This was accidentally visible from the reader settings sheet, but should always
|
|
||||||
// // be disabled in release builds.
|
|
||||||
// if (isReleaseBuildType) {
|
|
||||||
// readerPreferences.longStripSplitWebtoon().set(false)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
if (oldVersion under 56) {
|
|
||||||
val pref = libraryPreferences.autoUpdateDeviceRestrictions()
|
|
||||||
if (pref.isSet() && "battery_not_low" in pref.get()) {
|
|
||||||
pref.getAndSet { it - "battery_not_low" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 57) {
|
|
||||||
val pref = preferenceStore.getInt("relative_time", 7)
|
|
||||||
if (pref.get() == 0) {
|
|
||||||
uiPreferences.relativeTime().set(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 58) {
|
|
||||||
pagePreviewCache.clear()
|
|
||||||
File(context.cacheDir, PagePreviewCache.PARAMETER_CACHE_DIRECTORY).listFiles()?.forEach {
|
|
||||||
if (it.name == "journal" || it.name.startsWith("journal.")) {
|
|
||||||
return@forEach
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
it.delete()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logcat(LogPriority.WARN, e) { "Failed to remove file from cache" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 59) {
|
|
||||||
val prefsToReplace = listOf(
|
|
||||||
"pref_download_only",
|
|
||||||
"incognito_mode",
|
|
||||||
"last_catalogue_source",
|
|
||||||
"trusted_signatures",
|
|
||||||
"last_app_closed",
|
|
||||||
"library_update_last_timestamp",
|
|
||||||
"library_unseen_updates_count",
|
|
||||||
"last_used_category",
|
|
||||||
"last_app_check",
|
|
||||||
"last_ext_check",
|
|
||||||
"last_version_code",
|
|
||||||
"skip_pre_migration",
|
|
||||||
"eh_auto_update_stats",
|
|
||||||
"storage_dir",
|
|
||||||
)
|
|
||||||
replacePreferences(
|
|
||||||
preferenceStore = preferenceStore,
|
|
||||||
filterPredicate = { it.key in prefsToReplace },
|
|
||||||
newKey = { Preference.appStateKey(it) },
|
|
||||||
)
|
|
||||||
|
|
||||||
val privatePrefsToReplace = listOf(
|
|
||||||
"sql_password",
|
|
||||||
"encrypt_database",
|
|
||||||
"cbz_password",
|
|
||||||
"password_protect_downloads",
|
|
||||||
"eh_ipb_member_id",
|
|
||||||
"enable_exhentai",
|
|
||||||
"eh_ipb_member_id",
|
|
||||||
"eh_ipb_pass_hash",
|
|
||||||
"eh_igneous",
|
|
||||||
"eh_ehSettingsProfile",
|
|
||||||
"eh_exhSettingsProfile",
|
|
||||||
"eh_settingsKey",
|
|
||||||
"eh_sessionCookie",
|
|
||||||
"eh_hathPerksCookie",
|
|
||||||
)
|
|
||||||
|
|
||||||
replacePreferences(
|
|
||||||
preferenceStore = preferenceStore,
|
|
||||||
filterPredicate = { it.key in privatePrefsToReplace },
|
|
||||||
newKey = { Preference.privateKey(it) },
|
|
||||||
)
|
|
||||||
|
|
||||||
// Deleting old download cache index files, but might as well clear it all out
|
|
||||||
context.cacheDir.deleteRecursively()
|
|
||||||
}
|
|
||||||
if (oldVersion under 60) {
|
|
||||||
sourcePreferences.extensionRepos().getAndSet {
|
|
||||||
it.map { "https://raw.githubusercontent.com/$it/repo" }.toSet()
|
|
||||||
}
|
|
||||||
replacePreferences(
|
|
||||||
preferenceStore = preferenceStore,
|
|
||||||
filterPredicate = { it.key.startsWith("pref_mangasync_") || it.key.startsWith("track_token_") },
|
|
||||||
newKey = { Preference.privateKey(it) },
|
|
||||||
)
|
|
||||||
prefs.edit {
|
|
||||||
remove(Preference.appStateKey("trusted_signatures"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldVersion under 66) {
|
|
||||||
val cacheImagesToDisk = prefs.getBoolean("cache_archive_manga_on_disk", false)
|
|
||||||
if (cacheImagesToDisk) {
|
|
||||||
readerPreferences.archiveReaderMode().set(ReaderPreferences.ArchiveReaderMode.CACHE_TO_DISK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldVersion under 66) {
|
|
||||||
if (prefs.getBoolean(Preference.privateKey("encrypt_database"), false)) {
|
|
||||||
context.toast(
|
|
||||||
"Restart the app to load your encrypted library",
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val appStatePrefsToReplace = listOf(
|
|
||||||
"__PRIVATE_sql_password",
|
|
||||||
"__PRIVATE_encrypt_database",
|
|
||||||
"__PRIVATE_cbz_password",
|
|
||||||
)
|
|
||||||
|
|
||||||
replacePreferences(
|
|
||||||
preferenceStore = preferenceStore,
|
|
||||||
filterPredicate = { it.key in appStatePrefsToReplace },
|
|
||||||
newKey = { Preference.appStateKey(it.replace("__PRIVATE_", "").trim()) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldVersion under 67) {
|
|
||||||
runBlocking {
|
|
||||||
for ((index, source) in sourcePreferences.extensionRepos().get().withIndex()) {
|
|
||||||
try {
|
|
||||||
extensionRepoRepository.upsertRepo(
|
|
||||||
source,
|
|
||||||
"Repo #${index + 1}",
|
|
||||||
null,
|
|
||||||
source,
|
|
||||||
"NOFINGERPRINT-${index + 1}",
|
|
||||||
)
|
|
||||||
} catch (e: SaveExtensionRepoException) {
|
|
||||||
logcat(LogPriority.ERROR, e) { "Error Migrating Extension Repo with baseUrl: $source" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sourcePreferences.extensionRepos().delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (oldVersion under 1) { } (1 is current release version)
|
|
||||||
// do stuff here when releasing changed crap
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
xLogE("Failed to migrate app from $oldVersion -> ${BuildConfig.VERSION_CODE}!", e)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun migrateBackupEntry(manga: Manga): Manga {
|
fun migrateBackupEntry(manga: Manga): Manga {
|
||||||
var newManga = manga
|
var newManga = manga
|
||||||
@ -769,102 +61,4 @@ object EXHMigrations {
|
|||||||
orig
|
orig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class UrlConfig(
|
|
||||||
@SerialName("s")
|
|
||||||
val source: Long,
|
|
||||||
@SerialName("u")
|
|
||||||
val url: String,
|
|
||||||
@SerialName("m")
|
|
||||||
val mangaUrl: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class MangaConfig(
|
|
||||||
@SerialName("c")
|
|
||||||
val children: List<MangaSource>,
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
fun readFromUrl(url: String): MangaConfig? {
|
|
||||||
return try {
|
|
||||||
Json.decodeFromString(url)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readMangaConfig(manga: Manga): MangaConfig? {
|
|
||||||
return MangaConfig.readFromUrl(manga.url)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class MangaSource(
|
|
||||||
@SerialName("s")
|
|
||||||
val source: Long,
|
|
||||||
@SerialName("u")
|
|
||||||
val url: String,
|
|
||||||
) {
|
|
||||||
fun load(): LoadedMangaSource? {
|
|
||||||
val manga = runBlocking { getManga.await(url, source) } ?: return null
|
|
||||||
val source = sourceManager.getOrStub(source)
|
|
||||||
return LoadedMangaSource(source, manga)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readUrlConfig(url: String): UrlConfig? {
|
|
||||||
return try {
|
|
||||||
Json.decodeFromString(url)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class LoadedMangaSource(val source: Source, val manga: Manga)
|
|
||||||
|
|
||||||
private fun updateSourceId(newId: Long, oldId: Long) {
|
|
||||||
runBlocking {
|
|
||||||
handler.await { ehQueries.migrateSource(newId, oldId) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
private fun replacePreferences(
|
|
||||||
preferenceStore: PreferenceStore,
|
|
||||||
filterPredicate: (Map.Entry<String, Any?>) -> Boolean,
|
|
||||||
newKey: (String) -> String,
|
|
||||||
) {
|
|
||||||
preferenceStore.getAll()
|
|
||||||
.filter(filterPredicate)
|
|
||||||
.forEach { (key, value) ->
|
|
||||||
when (value) {
|
|
||||||
is Int -> {
|
|
||||||
preferenceStore.getInt(newKey(key)).set(value)
|
|
||||||
preferenceStore.getInt(key).delete()
|
|
||||||
}
|
|
||||||
is Long -> {
|
|
||||||
preferenceStore.getLong(newKey(key)).set(value)
|
|
||||||
preferenceStore.getLong(key).delete()
|
|
||||||
}
|
|
||||||
is Float -> {
|
|
||||||
preferenceStore.getFloat(newKey(key)).set(value)
|
|
||||||
preferenceStore.getFloat(key).delete()
|
|
||||||
}
|
|
||||||
is String -> {
|
|
||||||
preferenceStore.getString(newKey(key)).set(value)
|
|
||||||
preferenceStore.getString(key).delete()
|
|
||||||
}
|
|
||||||
is Boolean -> {
|
|
||||||
preferenceStore.getBoolean(newKey(key)).set(value)
|
|
||||||
preferenceStore.getBoolean(key).delete()
|
|
||||||
}
|
|
||||||
is Set<*> -> (value as? Set<String>)?.let {
|
|
||||||
preferenceStore.getStringSet(newKey(key)).set(value)
|
|
||||||
preferenceStore.getStringSet(key).delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,15 @@
|
|||||||
package exh.debug
|
package exh.debug
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import eu.kanade.domain.base.BasePreferences
|
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.toSManga
|
import eu.kanade.domain.manga.model.toSManga
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
|
||||||
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||||
import eu.kanade.tachiyomi.data.cache.PagePreviewCache
|
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||||
import eu.kanade.tachiyomi.data.sync.SyncDataJob
|
import eu.kanade.tachiyomi.data.sync.SyncDataJob
|
||||||
import eu.kanade.tachiyomi.data.track.TrackerManager
|
|
||||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
|
||||||
import eu.kanade.tachiyomi.source.AndroidSourceManager
|
import eu.kanade.tachiyomi.source.AndroidSourceManager
|
||||||
import eu.kanade.tachiyomi.source.online.all.NHentai
|
import eu.kanade.tachiyomi.source.online.all.NHentai
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
|
||||||
import eu.kanade.tachiyomi.util.system.workManager
|
import eu.kanade.tachiyomi.util.system.workManager
|
||||||
import exh.EXHMigrations
|
|
||||||
import exh.eh.EHentaiThrottleManager
|
import exh.eh.EHentaiThrottleManager
|
||||||
import exh.eh.EHentaiUpdateWorker
|
import exh.eh.EHentaiUpdateWorker
|
||||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||||
@ -27,11 +19,9 @@ import exh.source.nHentaiSourceIds
|
|||||||
import exh.util.jobScheduler
|
import exh.util.jobScheduler
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.serialization.protobuf.schema.ProtoBufSchemaGenerator
|
import kotlinx.serialization.protobuf.schema.ProtoBufSchemaGenerator
|
||||||
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
import mihon.core.migration.Migrator
|
||||||
import tachiyomi.core.common.preference.PreferenceStore
|
import mihon.core.migration.migrations.migrations
|
||||||
import tachiyomi.data.DatabaseHandler
|
import tachiyomi.data.DatabaseHandler
|
||||||
import tachiyomi.domain.backup.service.BackupPreferences
|
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
|
||||||
import tachiyomi.domain.manga.interactor.GetAllManga
|
import tachiyomi.domain.manga.interactor.GetAllManga
|
||||||
import tachiyomi.domain.manga.interactor.GetExhFavoriteMangaWithMetadata
|
import tachiyomi.domain.manga.interactor.GetExhFavoriteMangaWithMetadata
|
||||||
import tachiyomi.domain.manga.interactor.GetFavorites
|
import tachiyomi.domain.manga.interactor.GetFavorites
|
||||||
@ -48,16 +38,6 @@ import java.util.UUID
|
|||||||
object DebugFunctions {
|
object DebugFunctions {
|
||||||
private val app: Application by injectLazy()
|
private val app: Application by injectLazy()
|
||||||
private val handler: DatabaseHandler by injectLazy()
|
private val handler: DatabaseHandler by injectLazy()
|
||||||
private val prefsStore: PreferenceStore by injectLazy()
|
|
||||||
private val basePrefs: BasePreferences by injectLazy()
|
|
||||||
private val uiPrefs: UiPreferences by injectLazy()
|
|
||||||
private val networkPrefs: NetworkPreferences by injectLazy()
|
|
||||||
private val sourcePrefs: SourcePreferences by injectLazy()
|
|
||||||
private val securityPrefs: SecurityPreferences by injectLazy()
|
|
||||||
private val libraryPrefs: LibraryPreferences by injectLazy()
|
|
||||||
private val readerPrefs: ReaderPreferences by injectLazy()
|
|
||||||
private val backupPrefs: BackupPreferences by injectLazy()
|
|
||||||
private val trackerManager: TrackerManager by injectLazy()
|
|
||||||
private val sourceManager: SourceManager by injectLazy()
|
private val sourceManager: SourceManager by injectLazy()
|
||||||
private val updateManga: UpdateManga by injectLazy()
|
private val updateManga: UpdateManga by injectLazy()
|
||||||
private val getFavorites: GetFavorites by injectLazy()
|
private val getFavorites: GetFavorites by injectLazy()
|
||||||
@ -66,46 +46,22 @@ object DebugFunctions {
|
|||||||
private val getExhFavoriteMangaWithMetadata: GetExhFavoriteMangaWithMetadata by injectLazy()
|
private val getExhFavoriteMangaWithMetadata: GetExhFavoriteMangaWithMetadata by injectLazy()
|
||||||
private val getSearchMetadata: GetSearchMetadata by injectLazy()
|
private val getSearchMetadata: GetSearchMetadata by injectLazy()
|
||||||
private val getAllManga: GetAllManga by injectLazy()
|
private val getAllManga: GetAllManga by injectLazy()
|
||||||
private val pagePreviewCache: PagePreviewCache by injectLazy()
|
|
||||||
private val extensionRepoRepository: ExtensionRepoRepository by injectLazy()
|
|
||||||
|
|
||||||
fun forceUpgradeMigration() {
|
fun forceUpgradeMigration() {
|
||||||
val lastVersionCode = prefsStore.getInt("eh_last_version_code", 0)
|
Migrator.migrate(
|
||||||
lastVersionCode.set(1)
|
old = 1,
|
||||||
EXHMigrations.upgrade(
|
new = BuildConfig.VERSION_CODE,
|
||||||
context = app,
|
migrations = migrations,
|
||||||
preferenceStore = prefsStore,
|
onMigrationComplete = {}
|
||||||
basePreferences = basePrefs,
|
|
||||||
uiPreferences = uiPrefs,
|
|
||||||
networkPreferences = networkPrefs,
|
|
||||||
sourcePreferences = sourcePrefs,
|
|
||||||
securityPreferences = securityPrefs,
|
|
||||||
libraryPreferences = libraryPrefs,
|
|
||||||
readerPreferences = readerPrefs,
|
|
||||||
backupPreferences = backupPrefs,
|
|
||||||
trackerManager = trackerManager,
|
|
||||||
pagePreviewCache = pagePreviewCache,
|
|
||||||
extensionRepoRepository = extensionRepoRepository,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun forceSetupJobs() {
|
fun forceSetupJobs() {
|
||||||
val lastVersionCode = prefsStore.getInt("eh_last_version_code", 0)
|
Migrator.migrate(
|
||||||
lastVersionCode.set(0)
|
old = 0,
|
||||||
EXHMigrations.upgrade(
|
new = BuildConfig.VERSION_CODE,
|
||||||
context = app,
|
migrations = migrations,
|
||||||
preferenceStore = prefsStore,
|
onMigrationComplete = {}
|
||||||
basePreferences = basePrefs,
|
|
||||||
uiPreferences = uiPrefs,
|
|
||||||
networkPreferences = networkPrefs,
|
|
||||||
sourcePreferences = sourcePrefs,
|
|
||||||
securityPreferences = securityPrefs,
|
|
||||||
libraryPreferences = libraryPrefs,
|
|
||||||
readerPreferences = readerPrefs,
|
|
||||||
backupPreferences = backupPrefs,
|
|
||||||
trackerManager = trackerManager,
|
|
||||||
pagePreviewCache = pagePreviewCache,
|
|
||||||
extensionRepoRepository = extensionRepoRepository,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
53
app/src/main/java/mihon/core/migration/MigrateUtils.kt
Normal file
53
app/src/main/java/mihon/core/migration/MigrateUtils.kt
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package mihon.core.migration
|
||||||
|
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
|
import tachiyomi.data.DatabaseHandler
|
||||||
|
|
||||||
|
object MigrateUtils {
|
||||||
|
fun updateSourceId(migrationContext: MigrationContext, newId: Long, oldId: Long) {
|
||||||
|
val handler = migrationContext.get<DatabaseHandler>() ?: return
|
||||||
|
runBlocking {
|
||||||
|
handler.await { ehQueries.migrateSource(newId, oldId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun replacePreferences(
|
||||||
|
preferenceStore: PreferenceStore,
|
||||||
|
filterPredicate: (Map.Entry<String, Any?>) -> Boolean,
|
||||||
|
newKey: (String) -> String,
|
||||||
|
) {
|
||||||
|
preferenceStore.getAll()
|
||||||
|
.filter(filterPredicate)
|
||||||
|
.forEach { (key, value) ->
|
||||||
|
when (value) {
|
||||||
|
is Int -> {
|
||||||
|
preferenceStore.getInt(newKey(key)).set(value)
|
||||||
|
preferenceStore.getInt(key).delete()
|
||||||
|
}
|
||||||
|
is Long -> {
|
||||||
|
preferenceStore.getLong(newKey(key)).set(value)
|
||||||
|
preferenceStore.getLong(key).delete()
|
||||||
|
}
|
||||||
|
is Float -> {
|
||||||
|
preferenceStore.getFloat(newKey(key)).set(value)
|
||||||
|
preferenceStore.getFloat(key).delete()
|
||||||
|
}
|
||||||
|
is String -> {
|
||||||
|
preferenceStore.getString(newKey(key)).set(value)
|
||||||
|
preferenceStore.getString(key).delete()
|
||||||
|
}
|
||||||
|
is Boolean -> {
|
||||||
|
preferenceStore.getBoolean(newKey(key)).set(value)
|
||||||
|
preferenceStore.getBoolean(key).delete()
|
||||||
|
}
|
||||||
|
is Set<*> -> (value as? Set<String>)?.let {
|
||||||
|
preferenceStore.getStringSet(newKey(key)).set(value)
|
||||||
|
preferenceStore.getStringSet(key).delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
19
app/src/main/java/mihon/core/migration/Migration.kt
Normal file
19
app/src/main/java/mihon/core/migration/Migration.kt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package mihon.core.migration
|
||||||
|
|
||||||
|
interface Migration {
|
||||||
|
val version: Float
|
||||||
|
|
||||||
|
suspend operator fun invoke(migrationContext: MigrationContext): Boolean
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ALWAYS = -1f
|
||||||
|
|
||||||
|
fun of(version: Float, action: suspend (MigrationContext) -> Boolean): Migration = object : Migration {
|
||||||
|
override val version: Float = version
|
||||||
|
|
||||||
|
override suspend operator fun invoke(migrationContext: MigrationContext): Boolean {
|
||||||
|
return action(migrationContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
app/src/main/java/mihon/core/migration/MigrationContext.kt
Normal file
10
app/src/main/java/mihon/core/migration/MigrationContext.kt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package mihon.core.migration
|
||||||
|
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
|
||||||
|
class MigrationContext {
|
||||||
|
|
||||||
|
inline fun <reified T> get(): T? {
|
||||||
|
return Injekt.getInstanceOrNull(T::class.java)
|
||||||
|
}
|
||||||
|
}
|
53
app/src/main/java/mihon/core/migration/Migrator.kt
Normal file
53
app/src/main/java/mihon/core/migration/Migrator.kt
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package mihon.core.migration
|
||||||
|
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import tachiyomi.core.common.util.system.logcat
|
||||||
|
|
||||||
|
object Migrator {
|
||||||
|
|
||||||
|
@SuppressWarnings("ReturnCount")
|
||||||
|
fun migrate(
|
||||||
|
old: Int,
|
||||||
|
new: Int,
|
||||||
|
migrations: List<Migration>,
|
||||||
|
dryrun: Boolean = false,
|
||||||
|
onMigrationComplete: () -> Unit
|
||||||
|
): Boolean {
|
||||||
|
val migrationContext = MigrationContext()
|
||||||
|
|
||||||
|
if (old == 0) {
|
||||||
|
return migrationContext.migrate(
|
||||||
|
migrations = migrations.filter { it.isAlways() },
|
||||||
|
dryrun = dryrun
|
||||||
|
)
|
||||||
|
.also { onMigrationComplete() }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old >= new) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return migrationContext.migrate(
|
||||||
|
migrations = migrations.filter { it.isAlways() || it.version.toInt() in (old + 1)..new },
|
||||||
|
dryrun = dryrun
|
||||||
|
)
|
||||||
|
.also { onMigrationComplete() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Migration.isAlways() = version == Migration.ALWAYS
|
||||||
|
|
||||||
|
@SuppressWarnings("MaxLineLength")
|
||||||
|
private fun MigrationContext.migrate(migrations: List<Migration>, dryrun: Boolean): Boolean {
|
||||||
|
return migrations.sortedBy { it.version }
|
||||||
|
.map { migration ->
|
||||||
|
if (!dryrun) {
|
||||||
|
logcat { "Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" }
|
||||||
|
runBlocking { migration(this@migrate) }
|
||||||
|
} else {
|
||||||
|
logcat { "(Dry-run) Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" }
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.reduce { acc, b -> acc || b }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.domain.backup.service.BackupPreferences
|
||||||
|
|
||||||
|
class AlwaysBackupMigration : Migration {
|
||||||
|
override val version: Float = 40f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val backupPreferences = migrationContext.get<BackupPreferences>() ?: return@withIOContext false
|
||||||
|
if (backupPreferences.backupInterval().get() == 0) {
|
||||||
|
backupPreferences.backupInterval().set(12)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.domain.base.BasePreferences
|
||||||
|
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class ChangeMiuiExtensionInstallerMigration : Migration {
|
||||||
|
override val version: Float = 27f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val basePreferences = migrationContext.get<BasePreferences>() ?: return@withIOContext false
|
||||||
|
if (
|
||||||
|
DeviceUtil.isMiui &&
|
||||||
|
basePreferences.extensionInstaller().get() == BasePreferences.ExtensionInstaller
|
||||||
|
.PACKAGEINSTALLER
|
||||||
|
) {
|
||||||
|
basePreferences.extensionInstaller().set(BasePreferences.ExtensionInstaller.LEGACY)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class ChangeThemeModeToUppercaseMigration : Migration {
|
||||||
|
override val version: Float = 42f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val uiPreferences = migrationContext.get<UiPreferences>() ?: return@withIOContext false
|
||||||
|
if (uiPreferences.themeMode().isSet()) {
|
||||||
|
prefs.edit {
|
||||||
|
val themeMode = prefs.getString(uiPreferences.themeMode().key(), null) ?: return@edit
|
||||||
|
putString(uiPreferences.themeMode().key(), themeMode.uppercase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class ChangeTrackingQueueTypeMigration : Migration {
|
||||||
|
override val version: Float = 44f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val trackingQueuePref = context.getSharedPreferences("tracking_queue", Context.MODE_PRIVATE)
|
||||||
|
trackingQueuePref.all.forEach {
|
||||||
|
val (_, lastChapterRead) = it.value.toString().split(":")
|
||||||
|
trackingQueuePref.edit {
|
||||||
|
remove(it.key)
|
||||||
|
putFloat(it.key, lastChapterRead.toFloat())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.data.cache.PagePreviewCache
|
||||||
|
import logcat.LogPriority
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.core.common.util.system.logcat
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class ClearBrokenPagePreviewCacheMigration : Migration {
|
||||||
|
override val version: Float = 58f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val pagePreviewCache = migrationContext.get<PagePreviewCache>() ?: return@withIOContext false
|
||||||
|
pagePreviewCache.clear()
|
||||||
|
File(context.cacheDir, PagePreviewCache.PARAMETER_CACHE_DIRECTORY).listFiles()?.forEach {
|
||||||
|
if (it.name == "journal" || it.name.startsWith("journal.")) {
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
it.delete()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.WARN, e) { "Failed to remove file from cache" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
|
import exh.source.HBROWSE_SOURCE_ID
|
||||||
|
import mihon.core.migration.MigrateUtils
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.domain.manga.interactor.GetMangaBySource
|
||||||
|
import tachiyomi.domain.manga.model.MangaUpdate
|
||||||
|
|
||||||
|
class DelegateHBrowseMigration : Migration {
|
||||||
|
override val version: Float = 4f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val getMangaBySource = migrationContext.get<GetMangaBySource>() ?: return@withIOContext false
|
||||||
|
val updateManga = migrationContext.get<UpdateManga>() ?: return@withIOContext false
|
||||||
|
MigrateUtils.updateSourceId(migrationContext, HBROWSE_SOURCE_ID, 6912)
|
||||||
|
|
||||||
|
// Migrate BHrowse URLs
|
||||||
|
val hBrowseManga = getMangaBySource.await(HBROWSE_SOURCE_ID)
|
||||||
|
val mangaUpdates = hBrowseManga.map {
|
||||||
|
MangaUpdate(it.id, url = it.url + "/c00001/")
|
||||||
|
}
|
||||||
|
updateManga.awaitAll(mangaUpdates)
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.online.all.NHentai
|
||||||
|
import mihon.core.migration.MigrateUtils
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class DelegateNHentaiMigration : Migration {
|
||||||
|
override val version: Float = 6f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
MigrateUtils.updateSourceId(migrationContext, NHentai.otherId, 6907)
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import exh.log.xLogE
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class DeleteOldEhFavoritesDatabaseMigration : Migration {
|
||||||
|
override val version: Float = 24f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
try {
|
||||||
|
sequenceOf(
|
||||||
|
"fav-sync",
|
||||||
|
"fav-sync.management",
|
||||||
|
"fav-sync.lock",
|
||||||
|
"fav-sync.note",
|
||||||
|
).map {
|
||||||
|
File(context.filesDir, it)
|
||||||
|
}.filter(File::exists).forEach {
|
||||||
|
if (it.isDirectory) {
|
||||||
|
it.deleteRecursively()
|
||||||
|
} else {
|
||||||
|
it.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
xLogE("Failed to delete old favorites database", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.data.DatabaseHandler
|
||||||
|
|
||||||
|
class DeleteOldMangaDexTracksMigration : Migration {
|
||||||
|
override val version: Float = 17f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val handler = migrationContext.get<DatabaseHandler>() ?: return@withIOContext false
|
||||||
|
// Delete old mangadex trackers
|
||||||
|
handler.await { ehQueries.deleteBySyncId(6) }
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class LogoutFromMALMigration : Migration {
|
||||||
|
override val version: Float = 12f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
// Force MAL log out due to login flow change
|
||||||
|
migrationContext.get<TrackerManager>()?.myAnimeList?.logout()
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class LogoutFromMangaDexMigration : Migration {
|
||||||
|
override val version: Float = 45f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
// Force MangaDex log out due to login flow change
|
||||||
|
migrationContext.get<TrackerManager>()?.mdList?.logout()
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,191 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
|
import eu.kanade.tachiyomi.source.Source
|
||||||
|
import exh.source.MERGED_SOURCE_ID
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.data.DatabaseHandler
|
||||||
|
import tachiyomi.data.chapter.ChapterMapper
|
||||||
|
import tachiyomi.domain.chapter.interactor.DeleteChapters
|
||||||
|
import tachiyomi.domain.chapter.interactor.UpdateChapter
|
||||||
|
import tachiyomi.domain.chapter.model.ChapterUpdate
|
||||||
|
import tachiyomi.domain.manga.interactor.GetManga
|
||||||
|
import tachiyomi.domain.manga.interactor.GetMangaBySource
|
||||||
|
import tachiyomi.domain.manga.interactor.InsertMergedReference
|
||||||
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
import tachiyomi.domain.manga.model.MangaUpdate
|
||||||
|
import tachiyomi.domain.manga.model.MergedMangaReference
|
||||||
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
|
|
||||||
|
class MergedMangaRewriteMigration : Migration {
|
||||||
|
override val version: Float = 7f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val handler = migrationContext.get<DatabaseHandler>() ?: return@withIOContext false
|
||||||
|
val getMangaBySource = migrationContext.get<GetMangaBySource>() ?: return@withIOContext false
|
||||||
|
val getManga = migrationContext.get<GetManga>() ?: return@withIOContext false
|
||||||
|
val updateManga = migrationContext.get<UpdateManga>() ?: return@withIOContext false
|
||||||
|
val insertMergedReference = migrationContext.get<InsertMergedReference>() ?: return@withIOContext false
|
||||||
|
val sourceManager = migrationContext.get<SourceManager>() ?: return@withIOContext false
|
||||||
|
val deleteChapters = migrationContext.get<DeleteChapters>() ?: return@withIOContext false
|
||||||
|
val updateChapter = migrationContext.get<UpdateChapter>() ?: return@withIOContext false
|
||||||
|
val mergedMangas = getMangaBySource.await(MERGED_SOURCE_ID)
|
||||||
|
|
||||||
|
if (mergedMangas.isNotEmpty()) {
|
||||||
|
val mangaConfigs = mergedMangas.mapNotNull { mergedManga ->
|
||||||
|
readMangaConfig(mergedManga)?.let { mergedManga to it }
|
||||||
|
}
|
||||||
|
if (mangaConfigs.isNotEmpty()) {
|
||||||
|
val mangaToUpdate = mutableListOf<MangaUpdate>()
|
||||||
|
val mergedMangaReferences = mutableListOf<MergedMangaReference>()
|
||||||
|
mangaConfigs.onEach { mergedManga ->
|
||||||
|
val newFirst = mergedManga.second.children.firstOrNull()?.url?.let {
|
||||||
|
if (getManga.await(it, MERGED_SOURCE_ID) != null) return@onEach
|
||||||
|
mangaToUpdate += MangaUpdate(id = mergedManga.first.id, url = it)
|
||||||
|
mergedManga.first.copy(url = it)
|
||||||
|
} ?: mergedManga.first
|
||||||
|
mergedMangaReferences += MergedMangaReference(
|
||||||
|
id = -1,
|
||||||
|
isInfoManga = false,
|
||||||
|
getChapterUpdates = false,
|
||||||
|
chapterSortMode = 0,
|
||||||
|
chapterPriority = 0,
|
||||||
|
downloadChapters = false,
|
||||||
|
mergeId = newFirst.id,
|
||||||
|
mergeUrl = newFirst.url,
|
||||||
|
mangaId = newFirst.id,
|
||||||
|
mangaUrl = newFirst.url,
|
||||||
|
mangaSourceId = MERGED_SOURCE_ID,
|
||||||
|
)
|
||||||
|
mergedManga.second.children.distinct().forEachIndexed { index, mangaSource ->
|
||||||
|
val load = mangaSource.load(getManga, sourceManager) ?: return@forEachIndexed
|
||||||
|
mergedMangaReferences += MergedMangaReference(
|
||||||
|
id = -1,
|
||||||
|
isInfoManga = index == 0,
|
||||||
|
getChapterUpdates = true,
|
||||||
|
chapterSortMode = 0,
|
||||||
|
chapterPriority = 0,
|
||||||
|
downloadChapters = true,
|
||||||
|
mergeId = newFirst.id,
|
||||||
|
mergeUrl = newFirst.url,
|
||||||
|
mangaId = load.manga.id,
|
||||||
|
mangaUrl = load.manga.url,
|
||||||
|
mangaSourceId = load.source.id,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateManga.awaitAll(mangaToUpdate)
|
||||||
|
insertMergedReference.awaitAll(mergedMangaReferences)
|
||||||
|
|
||||||
|
val loadedMangaList = mangaConfigs
|
||||||
|
.map { it.second.children }
|
||||||
|
.flatten()
|
||||||
|
.mapNotNull { it.load(getManga, sourceManager) }
|
||||||
|
.distinct()
|
||||||
|
val chapters =
|
||||||
|
handler.awaitList {
|
||||||
|
ehQueries.getChaptersByMangaIds(
|
||||||
|
mergedMangas.map { it.id },
|
||||||
|
ChapterMapper::mapChapter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val mergedMangaChapters =
|
||||||
|
handler.awaitList {
|
||||||
|
ehQueries.getChaptersByMangaIds(
|
||||||
|
loadedMangaList.map { it.manga.id },
|
||||||
|
ChapterMapper::mapChapter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val mergedMangaChaptersMatched = mergedMangaChapters.mapNotNull { chapter ->
|
||||||
|
loadedMangaList.firstOrNull {
|
||||||
|
it.manga.id == chapter.id
|
||||||
|
}?.let { it to chapter }
|
||||||
|
}
|
||||||
|
val parsedChapters = chapters.filter {
|
||||||
|
it.read || it.lastPageRead != 0L
|
||||||
|
}.mapNotNull { chapter -> readUrlConfig(chapter.url)?.let { chapter to it } }
|
||||||
|
val chaptersToUpdate = mutableListOf<ChapterUpdate>()
|
||||||
|
parsedChapters.forEach { parsedChapter ->
|
||||||
|
mergedMangaChaptersMatched.firstOrNull {
|
||||||
|
it.second.url == parsedChapter.second.url &&
|
||||||
|
it.first.source.id == parsedChapter.second.source &&
|
||||||
|
it.first.manga.url == parsedChapter.second.mangaUrl
|
||||||
|
}?.let {
|
||||||
|
chaptersToUpdate += ChapterUpdate(
|
||||||
|
it.second.id,
|
||||||
|
read = parsedChapter.first.read,
|
||||||
|
lastPageRead = parsedChapter.first.lastPageRead,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteChapters.await(mergedMangaChapters.map { it.id })
|
||||||
|
updateChapter.awaitAll(chaptersToUpdate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class UrlConfig(
|
||||||
|
@SerialName("s")
|
||||||
|
val source: Long,
|
||||||
|
@SerialName("u")
|
||||||
|
val url: String,
|
||||||
|
@SerialName("m")
|
||||||
|
val mangaUrl: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class MangaConfig(
|
||||||
|
@SerialName("c")
|
||||||
|
val children: List<MangaSource>,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun readFromUrl(url: String): MangaConfig? {
|
||||||
|
return try {
|
||||||
|
Json.decodeFromString(url)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readMangaConfig(manga: Manga): MangaConfig? {
|
||||||
|
return MangaConfig.readFromUrl(manga.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class MangaSource(
|
||||||
|
@SerialName("s")
|
||||||
|
val source: Long,
|
||||||
|
@SerialName("u")
|
||||||
|
val url: String,
|
||||||
|
) {
|
||||||
|
suspend fun load(getManga: GetManga, sourceManager: SourceManager): LoadedMangaSource? {
|
||||||
|
val manga = getManga.await(url, source) ?: return null
|
||||||
|
val source = sourceManager.getOrStub(source)
|
||||||
|
return LoadedMangaSource(source, manga)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readUrlConfig(url: String): UrlConfig? {
|
||||||
|
return try {
|
||||||
|
Json.decodeFromString(url)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class LoadedMangaSource(val source: Source, val manga: Manga)
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
|
||||||
|
val migrations: List<Migration>
|
||||||
|
get() = listOf(
|
||||||
|
SetupBackupCreateMigration(),
|
||||||
|
SetupLibraryUpdateMigration(),
|
||||||
|
SetupEHentaiUpdateMigration(),
|
||||||
|
SetupSyncDataMigration(),
|
||||||
|
DelegateHBrowseMigration(),
|
||||||
|
DelegateNHentaiMigration(),
|
||||||
|
MergedMangaRewriteMigration(),
|
||||||
|
LogoutFromMALMigration(),
|
||||||
|
MoveDOHSettingMigration(),
|
||||||
|
ResetRotationSettingMigration(),
|
||||||
|
ResetReaderSettingsMigration(),
|
||||||
|
DeleteOldMangaDexTracksMigration(),
|
||||||
|
RemoveOldReaderThemeMigration(),
|
||||||
|
RemoveShorterLibraryUpdatesMigration(),
|
||||||
|
MoveLibrarySortingSettingsMigration(),
|
||||||
|
RemoveShortLibraryUpdatesMigration(),
|
||||||
|
MoveLibraryNonCompleteSettingMigration(),
|
||||||
|
DeleteOldEhFavoritesDatabaseMigration(),
|
||||||
|
MoveSecureScreenSettingMigration(),
|
||||||
|
ChangeMiuiExtensionInstallerMigration(),
|
||||||
|
MoveCoverOnlyGridSettingMigration(),
|
||||||
|
MoveCatalogueCoverOnlyGridSettingMigration(),
|
||||||
|
MoveLatestToFeedMigration(),
|
||||||
|
MoveReaderTapSettingMigration(),
|
||||||
|
MoveSortingModeSettingsMigration(),
|
||||||
|
MoveSortingModeSettingMigration(),
|
||||||
|
AlwaysBackupMigration(),
|
||||||
|
ResetFilterAndSortSettingsMigration(),
|
||||||
|
ChangeThemeModeToUppercaseMigration(),
|
||||||
|
MoveReadingButtonSettingMigration(),
|
||||||
|
ChangeTrackingQueueTypeMigration(),
|
||||||
|
LogoutFromMangaDexMigration(),
|
||||||
|
RemoveUpdateCheckerJobsMigration(),
|
||||||
|
RemoveBatteryNotLowRestrictionMigration(),
|
||||||
|
MoveRelativeTimeSettingMigration(),
|
||||||
|
ClearBrokenPagePreviewCacheMigration(),
|
||||||
|
MoveSettingsToPrivateOrAppStateMigration(),
|
||||||
|
MoveExtensionRepoSettingsMigration(),
|
||||||
|
MoveCacheToDiskSettingMigration(),
|
||||||
|
MoveEncryptionSettingsToAppStateMigration(),
|
||||||
|
TrustExtensionRepositoryMigration(),
|
||||||
|
)
|
@ -0,0 +1,24 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class MoveCacheToDiskSettingMigration : Migration {
|
||||||
|
override val version: Float = 66f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val readerPreferences = migrationContext.get<ReaderPreferences>() ?: return@withIOContext false
|
||||||
|
val cacheImagesToDisk = prefs.getBoolean("cache_archive_manga_on_disk", false)
|
||||||
|
if (cacheImagesToDisk) {
|
||||||
|
readerPreferences.archiveReaderMode().set(ReaderPreferences.ArchiveReaderMode.CACHE_TO_DISK)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class MoveCatalogueCoverOnlyGridSettingMigration : Migration {
|
||||||
|
override val version: Float = 29f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
if (prefs.getString("pref_display_mode_catalogue", null) == "NO_TITLE_GRID") {
|
||||||
|
prefs.edit(commit = true) {
|
||||||
|
putString("pref_display_mode_catalogue", "COMPACT_GRID")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class MoveCoverOnlyGridSettingMigration : Migration {
|
||||||
|
override val version: Float = 28f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
if (prefs.getString("pref_display_mode_library", null) == "NO_TITLE_GRID") {
|
||||||
|
prefs.edit(commit = true) {
|
||||||
|
putString("pref_display_mode_library", "COVER_ONLY_GRID")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.network.NetworkPreferences
|
||||||
|
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class MoveDOHSettingMigration : Migration {
|
||||||
|
override val version: Float = 14f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val networkPreferences = migrationContext.get<NetworkPreferences>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
// Migrate DNS over HTTPS setting
|
||||||
|
val wasDohEnabled = prefs.getBoolean("enable_doh", false)
|
||||||
|
if (wasDohEnabled) {
|
||||||
|
prefs.edit {
|
||||||
|
putInt(networkPreferences.dohProvider().key(), PREF_DOH_CLOUDFLARE)
|
||||||
|
remove("enable_doh")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import mihon.core.migration.MigrateUtils
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.preference.Preference
|
||||||
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.core.common.util.lang.withUIContext
|
||||||
|
|
||||||
|
class MoveEncryptionSettingsToAppStateMigration : Migration {
|
||||||
|
override val version: Float = 66f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val preferenceStore = migrationContext.get<PreferenceStore>() ?: return@withIOContext false
|
||||||
|
if (prefs.getBoolean(Preference.privateKey("encrypt_database"), false)) {
|
||||||
|
withUIContext {
|
||||||
|
context.toast(
|
||||||
|
"Restart the app to load your encrypted library",
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val appStatePrefsToReplace = listOf(
|
||||||
|
"__PRIVATE_sql_password",
|
||||||
|
"__PRIVATE_encrypt_database",
|
||||||
|
"__PRIVATE_cbz_password",
|
||||||
|
)
|
||||||
|
|
||||||
|
MigrateUtils.replacePreferences(
|
||||||
|
preferenceStore = preferenceStore,
|
||||||
|
filterPredicate = { it.key in appStatePrefsToReplace },
|
||||||
|
newKey = { Preference.appStateKey(it.replace("__PRIVATE_", "").trim()) },
|
||||||
|
)
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.MigrateUtils
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.preference.Preference
|
||||||
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class MoveExtensionRepoSettingsMigration : Migration {
|
||||||
|
override val version: Float = 60f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val preferenceStore = migrationContext.get<PreferenceStore>() ?: return@withIOContext false
|
||||||
|
val sourcePreferences = migrationContext.get<SourcePreferences>() ?: return@withIOContext false
|
||||||
|
sourcePreferences.extensionRepos().getAndSet {
|
||||||
|
it.map { "https://raw.githubusercontent.com/$it/repo" }.toSet()
|
||||||
|
}
|
||||||
|
MigrateUtils.replacePreferences(
|
||||||
|
preferenceStore = preferenceStore,
|
||||||
|
filterPredicate = { it.key.startsWith("pref_mangasync_") || it.key.startsWith("track_token_") },
|
||||||
|
newKey = { Preference.privateKey(it) },
|
||||||
|
)
|
||||||
|
prefs.edit {
|
||||||
|
remove(Preference.appStateKey("trusted_signatures"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import exh.util.nullIfBlank
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
import kotlinx.serialization.json.contentOrNull
|
||||||
|
import kotlinx.serialization.json.jsonArray
|
||||||
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.domain.source.interactor.InsertFeedSavedSearch
|
||||||
|
import tachiyomi.domain.source.interactor.InsertSavedSearch
|
||||||
|
import tachiyomi.domain.source.model.FeedSavedSearch
|
||||||
|
import tachiyomi.domain.source.model.SavedSearch
|
||||||
|
|
||||||
|
class MoveLatestToFeedMigration : Migration {
|
||||||
|
override val version: Float = 31f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val insertSavedSearch = migrationContext.get<InsertSavedSearch>() ?: return@withIOContext false
|
||||||
|
val insertFeedSavedSearch = migrationContext.get<InsertFeedSavedSearch>() ?: return@withIOContext false
|
||||||
|
val savedSearch = prefs.getStringSet("eh_saved_searches", emptySet())?.mapNotNull {
|
||||||
|
runCatching {
|
||||||
|
val content = Json.decodeFromString<JsonObject>(it.substringAfter(':'))
|
||||||
|
SavedSearch(
|
||||||
|
id = -1,
|
||||||
|
source = it.substringBefore(':').toLongOrNull()
|
||||||
|
?: return@runCatching null,
|
||||||
|
name = content["name"]!!.jsonPrimitive.content,
|
||||||
|
query = content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
|
||||||
|
filtersJson = Json.encodeToString(content["filters"]!!.jsonArray),
|
||||||
|
)
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
|
if (!savedSearch.isNullOrEmpty()) {
|
||||||
|
insertSavedSearch.awaitAll(savedSearch)
|
||||||
|
}
|
||||||
|
val feedSavedSearch = prefs.getStringSet("latest_tab_sources", emptySet())?.map {
|
||||||
|
FeedSavedSearch(
|
||||||
|
id = -1,
|
||||||
|
source = it.toLong(),
|
||||||
|
savedSearch = null,
|
||||||
|
global = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!feedSavedSearch.isNullOrEmpty()) {
|
||||||
|
insertFeedSavedSearch.awaitAll(feedSavedSearch)
|
||||||
|
}
|
||||||
|
prefs.edit(commit = true) {
|
||||||
|
remove("eh_saved_searches")
|
||||||
|
remove("latest_tab_sources")
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.preference.minusAssign
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
|
||||||
|
class MoveLibraryNonCompleteSettingMigration : Migration {
|
||||||
|
override val version: Float = 23f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||||
|
val oldUpdateOngoingOnly = prefs.getBoolean("pref_update_only_non_completed_key", true)
|
||||||
|
if (!oldUpdateOngoingOnly) {
|
||||||
|
libraryPreferences.autoUpdateMangaRestrictions() -= LibraryPreferences.MANGA_NON_COMPLETED
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.core.common.util.system.logcat
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
|
||||||
|
class MoveLibrarySortingSettingsMigration : Migration {
|
||||||
|
override val version: Float = 20f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||||
|
try {
|
||||||
|
val oldSortingMode = prefs.getInt(libraryPreferences.sortingMode().key(), 0 /* ALPHABETICAL */)
|
||||||
|
val oldSortingDirection = prefs.getBoolean("library_sorting_ascending", true)
|
||||||
|
|
||||||
|
val newSortingMode = when (oldSortingMode) {
|
||||||
|
0 -> "ALPHABETICAL"
|
||||||
|
1 -> "LAST_READ"
|
||||||
|
2 -> "LAST_MANGA_UPDATE"
|
||||||
|
3 -> "UNREAD_COUNT"
|
||||||
|
4 -> "TOTAL_CHAPTERS"
|
||||||
|
6 -> "LATEST_CHAPTER"
|
||||||
|
7 -> "DRAG_AND_DROP"
|
||||||
|
8 -> "DATE_ADDED"
|
||||||
|
9 -> "TAG_LIST"
|
||||||
|
10 -> "CHAPTER_FETCH_DATE"
|
||||||
|
else -> "ALPHABETICAL"
|
||||||
|
}
|
||||||
|
|
||||||
|
val newSortingDirection = when (oldSortingDirection) {
|
||||||
|
true -> "ASCENDING"
|
||||||
|
else -> "DESCENDING"
|
||||||
|
}
|
||||||
|
|
||||||
|
prefs.edit(commit = true) {
|
||||||
|
remove(libraryPreferences.sortingMode().key())
|
||||||
|
remove("library_sorting_ascending")
|
||||||
|
}
|
||||||
|
|
||||||
|
prefs.edit {
|
||||||
|
putString(libraryPreferences.sortingMode().key(), newSortingMode)
|
||||||
|
putString("library_sorting_ascending", newSortingDirection)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(throwable = e) { "Already done migration" }
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class MoveReaderTapSettingMigration : Migration {
|
||||||
|
override val version: Float = 32f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val readerPreferences = migrationContext.get<ReaderPreferences>() ?: return@withIOContext false
|
||||||
|
val oldReaderTap = prefs.getBoolean("reader_tap", false)
|
||||||
|
if (!oldReaderTap) {
|
||||||
|
readerPreferences.navigationModePager().set(5)
|
||||||
|
readerPreferences.navigationModeWebtoon().set(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
|
||||||
|
class MoveReadingButtonSettingMigration : Migration {
|
||||||
|
override val version: Float = 43f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||||
|
if (prefs.getBoolean("start_reading_button", false)) {
|
||||||
|
libraryPreferences.showContinueReadingButton().set(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class MoveRelativeTimeSettingMigration : Migration {
|
||||||
|
override val version: Float = 57f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val preferenceStore = migrationContext.get<PreferenceStore>() ?: return@withIOContext false
|
||||||
|
val uiPreferences = migrationContext.get<UiPreferences>() ?: return@withIOContext false
|
||||||
|
val pref = preferenceStore.getInt("relative_time", 7)
|
||||||
|
if (pref.get() == 0) {
|
||||||
|
uiPreferences.relativeTime().set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class MoveSecureScreenSettingMigration : Migration {
|
||||||
|
override val version: Float = 27f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val securityPreferences = migrationContext.get<SecurityPreferences>() ?: return@withIOContext false
|
||||||
|
val oldSecureScreen = prefs.getBoolean("secure_screen", false)
|
||||||
|
if (oldSecureScreen) {
|
||||||
|
securityPreferences.secureScreen().set(SecurityPreferences.SecureScreenMode.ALWAYS)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.MigrateUtils
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.preference.Preference
|
||||||
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class MoveSettingsToPrivateOrAppStateMigration : Migration {
|
||||||
|
override val version: Float = 59f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val preferenceStore = migrationContext.get<PreferenceStore>() ?: return@withIOContext false
|
||||||
|
val prefsToReplace = listOf(
|
||||||
|
"pref_download_only",
|
||||||
|
"incognito_mode",
|
||||||
|
"last_catalogue_source",
|
||||||
|
"trusted_signatures",
|
||||||
|
"last_app_closed",
|
||||||
|
"library_update_last_timestamp",
|
||||||
|
"library_unseen_updates_count",
|
||||||
|
"last_used_category",
|
||||||
|
"last_app_check",
|
||||||
|
"last_ext_check",
|
||||||
|
"last_version_code",
|
||||||
|
"skip_pre_migration",
|
||||||
|
"eh_auto_update_stats",
|
||||||
|
"storage_dir",
|
||||||
|
)
|
||||||
|
MigrateUtils.replacePreferences(
|
||||||
|
preferenceStore = preferenceStore,
|
||||||
|
filterPredicate = { it.key in prefsToReplace },
|
||||||
|
newKey = { Preference.appStateKey(it) },
|
||||||
|
)
|
||||||
|
|
||||||
|
val privatePrefsToReplace = listOf(
|
||||||
|
"sql_password",
|
||||||
|
"encrypt_database",
|
||||||
|
"cbz_password",
|
||||||
|
"password_protect_downloads",
|
||||||
|
"eh_ipb_member_id",
|
||||||
|
"enable_exhentai",
|
||||||
|
"eh_ipb_member_id",
|
||||||
|
"eh_ipb_pass_hash",
|
||||||
|
"eh_igneous",
|
||||||
|
"eh_ehSettingsProfile",
|
||||||
|
"eh_exhSettingsProfile",
|
||||||
|
"eh_settingsKey",
|
||||||
|
"eh_sessionCookie",
|
||||||
|
"eh_hathPerksCookie",
|
||||||
|
)
|
||||||
|
|
||||||
|
MigrateUtils.replacePreferences(
|
||||||
|
preferenceStore = preferenceStore,
|
||||||
|
filterPredicate = { it.key in privatePrefsToReplace },
|
||||||
|
newKey = { Preference.privateKey(it) },
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deleting old download cache index files, but might as well clear it all out
|
||||||
|
context.cacheDir.deleteRecursively()
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
|
||||||
|
class MoveSortingModeSettingMigration : Migration {
|
||||||
|
override val version: Float = 39f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
prefs.edit {
|
||||||
|
val sort = prefs.getString(libraryPreferences.sortingMode().key(), null) ?: return@edit
|
||||||
|
val direction = prefs.getString("library_sorting_ascending", "ASCENDING")!!
|
||||||
|
putString(libraryPreferences.sortingMode().key(), "$sort,$direction")
|
||||||
|
remove("library_sorting_ascending")
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.data.DatabaseHandler
|
||||||
|
import tachiyomi.data.category.CategoryMapper
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
|
||||||
|
class MoveSortingModeSettingsMigration : Migration {
|
||||||
|
override val version: Float = 38f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||||
|
val handler = migrationContext.get<DatabaseHandler>() ?: return@withIOContext false
|
||||||
|
// Handle renamed enum values
|
||||||
|
val newSortingMode = when (
|
||||||
|
val oldSortingMode = prefs.getString(libraryPreferences.sortingMode().key(), "ALPHABETICAL")
|
||||||
|
) {
|
||||||
|
"LAST_CHECKED" -> "LAST_MANGA_UPDATE"
|
||||||
|
"UNREAD" -> "UNREAD_COUNT"
|
||||||
|
"DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
|
||||||
|
"DRAG_AND_DROP" -> "ALPHABETICAL"
|
||||||
|
else -> oldSortingMode
|
||||||
|
}
|
||||||
|
prefs.edit {
|
||||||
|
putString(libraryPreferences.sortingMode().key(), newSortingMode)
|
||||||
|
}
|
||||||
|
handler.await(true) {
|
||||||
|
categoriesQueries.getCategories(CategoryMapper::mapCategory).executeAsList()
|
||||||
|
.filter { (it.flags and 0b00111100L) == 0b00100000L }
|
||||||
|
.forEach {
|
||||||
|
categoriesQueries.update(
|
||||||
|
categoryId = it.id,
|
||||||
|
flags = it.flags and 0b00111100L.inv(),
|
||||||
|
name = null,
|
||||||
|
order = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
|
||||||
|
class RemoveBatteryNotLowRestrictionMigration : Migration {
|
||||||
|
override val version: Float = 56f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||||
|
val pref = libraryPreferences.autoUpdateDeviceRestrictions()
|
||||||
|
if (pref.isSet() && "battery_not_low" in pref.get()) {
|
||||||
|
pref.getAndSet { it - "battery_not_low" }
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class RemoveOldReaderThemeMigration : Migration {
|
||||||
|
override val version: Float = 18f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val readerPreferences = migrationContext.get<ReaderPreferences>() ?: return@withIOContext false
|
||||||
|
val readerTheme = readerPreferences.readerTheme().get()
|
||||||
|
if (readerTheme == 4) {
|
||||||
|
readerPreferences.readerTheme().set(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
|
||||||
|
class RemoveShortLibraryUpdatesMigration : Migration {
|
||||||
|
override val version: Float = 22f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||||
|
val updateInterval = libraryPreferences.autoUpdateInterval().get()
|
||||||
|
if (updateInterval in listOf(3, 4, 6, 8)) {
|
||||||
|
libraryPreferences.autoUpdateInterval().set(12)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
|
||||||
|
class RemoveShorterLibraryUpdatesMigration : Migration {
|
||||||
|
override val version: Float = 18f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||||
|
val updateInterval = libraryPreferences.autoUpdateInterval().get()
|
||||||
|
if (updateInterval == 1 || updateInterval == 2) {
|
||||||
|
libraryPreferences.autoUpdateInterval().set(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||||
|
import eu.kanade.tachiyomi.util.system.workManager
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
|
import tachiyomi.core.common.preference.TriState
|
||||||
|
import tachiyomi.core.common.preference.getEnum
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class RemoveUpdateCheckerJobsMigration : Migration {
|
||||||
|
override val version: Float = 52f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val preferenceStore = migrationContext.get<PreferenceStore>() ?: return@withIOContext false
|
||||||
|
val trackerManager = migrationContext.get<TrackerManager>() ?: return@withIOContext false
|
||||||
|
// Removed background jobs
|
||||||
|
context.workManager.cancelAllWorkByTag("UpdateChecker")
|
||||||
|
context.workManager.cancelAllWorkByTag("ExtensionUpdate")
|
||||||
|
prefs.edit {
|
||||||
|
remove("automatic_ext_updates")
|
||||||
|
}
|
||||||
|
val prefKeys = listOf(
|
||||||
|
"pref_filter_library_downloaded",
|
||||||
|
"pref_filter_library_unread",
|
||||||
|
"pref_filter_library_started",
|
||||||
|
"pref_filter_library_bookmarked",
|
||||||
|
"pref_filter_library_completed",
|
||||||
|
"pref_filter_library_lewd",
|
||||||
|
) + trackerManager.trackers.map { "pref_filter_library_tracked_${it.id}" }
|
||||||
|
|
||||||
|
prefKeys.forEach { key ->
|
||||||
|
val pref = prefs.getInt(key, 0)
|
||||||
|
prefs.edit {
|
||||||
|
remove(key)
|
||||||
|
|
||||||
|
val newValue = when (pref) {
|
||||||
|
1 -> TriState.ENABLED_IS
|
||||||
|
2 -> TriState.ENABLED_NOT
|
||||||
|
else -> TriState.DISABLED
|
||||||
|
}
|
||||||
|
|
||||||
|
preferenceStore.getEnum("${key}_v2", TriState.DISABLED).set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
|
||||||
|
class ResetFilterAndSortSettingsMigration : Migration {
|
||||||
|
override val version: Float = 41f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val libraryPreferences = migrationContext.get<LibraryPreferences>() ?: return@withIOContext false
|
||||||
|
val preferences = listOf(
|
||||||
|
libraryPreferences.filterChapterByRead(),
|
||||||
|
libraryPreferences.filterChapterByDownloaded(),
|
||||||
|
libraryPreferences.filterChapterByBookmarked(),
|
||||||
|
libraryPreferences.sortChapterBySourceOrNumber(),
|
||||||
|
libraryPreferences.displayChapterByNameOrNumber(),
|
||||||
|
libraryPreferences.sortChapterByAscendingOrDescending(),
|
||||||
|
)
|
||||||
|
|
||||||
|
prefs.edit {
|
||||||
|
preferences.forEach { preference ->
|
||||||
|
val key = preference.key()
|
||||||
|
val value = prefs.getInt(key, Int.MIN_VALUE)
|
||||||
|
if (value == Int.MIN_VALUE) return@forEach
|
||||||
|
remove(key)
|
||||||
|
putLong(key, value.toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class ResetReaderSettingsMigration : Migration {
|
||||||
|
override val version: Float = 17f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
// Migrate Rotation and Viewer values to default values for viewer_flags
|
||||||
|
val newOrientation = when (prefs.getInt("pref_rotation_type_key", 1)) {
|
||||||
|
1 -> ReaderOrientation.FREE.flagValue
|
||||||
|
2 -> ReaderOrientation.PORTRAIT.flagValue
|
||||||
|
3 -> ReaderOrientation.LANDSCAPE.flagValue
|
||||||
|
4 -> ReaderOrientation.LOCKED_PORTRAIT.flagValue
|
||||||
|
5 -> ReaderOrientation.LOCKED_LANDSCAPE.flagValue
|
||||||
|
else -> ReaderOrientation.FREE.flagValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reading mode flag and prefValue is the same value
|
||||||
|
val newReadingMode = prefs.getInt("pref_default_viewer_key", 1)
|
||||||
|
|
||||||
|
prefs.edit {
|
||||||
|
putInt("pref_default_orientation_type_key", newOrientation)
|
||||||
|
remove("pref_rotation_type_key")
|
||||||
|
putInt("pref_default_reading_mode_key", newReadingMode)
|
||||||
|
remove("pref_default_viewer_key")
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|
||||||
|
class ResetRotationSettingMigration : Migration {
|
||||||
|
override val version: Float = 16f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val context = migrationContext.get<App>() ?: return@withIOContext false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
// Reset rotation to Free after replacing Lock
|
||||||
|
if (prefs.contains("pref_rotation_type_key")) {
|
||||||
|
prefs.edit {
|
||||||
|
putInt("pref_rotation_type_key", 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
|
||||||
|
class SetupBackupCreateMigration : Migration {
|
||||||
|
override val version: Float = Migration.ALWAYS
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean {
|
||||||
|
val context = migrationContext.get<App>() ?: return false
|
||||||
|
BackupCreateJob.setupTask(context)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import exh.eh.EHentaiUpdateWorker
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
|
||||||
|
class SetupEHentaiUpdateMigration : Migration {
|
||||||
|
override val version: Float = Migration.ALWAYS
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean {
|
||||||
|
val context = migrationContext.get<App>() ?: return false
|
||||||
|
EHentaiUpdateWorker.scheduleBackground(context)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
|
||||||
|
class SetupLibraryUpdateMigration : Migration {
|
||||||
|
override val version: Float = Migration.ALWAYS
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean {
|
||||||
|
val context = migrationContext.get<App>() ?: return false
|
||||||
|
LibraryUpdateJob.setupTask(context)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.data.sync.SyncDataJob
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
|
||||||
|
class SetupSyncDataMigration : Migration {
|
||||||
|
override val version: Float = Migration.ALWAYS
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean {
|
||||||
|
val context = migrationContext.get<App>() ?: return false
|
||||||
|
SyncDataJob.setupTask(context)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package mihon.core.migration.migrations
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
|
import logcat.LogPriority
|
||||||
|
import mihon.core.migration.Migration
|
||||||
|
import mihon.core.migration.MigrationContext
|
||||||
|
import mihon.domain.extensionrepo.exception.SaveExtensionRepoException
|
||||||
|
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.core.common.util.system.logcat
|
||||||
|
|
||||||
|
class TrustExtensionRepositoryMigration : Migration {
|
||||||
|
override val version: Float = 67f
|
||||||
|
|
||||||
|
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
|
||||||
|
val sourcePreferences = migrationContext.get<SourcePreferences>() ?: return@withIOContext false
|
||||||
|
val extensionRepositoryRepository =
|
||||||
|
migrationContext.get<ExtensionRepoRepository>() ?: return@withIOContext false
|
||||||
|
for ((index, source) in sourcePreferences.extensionRepos().get().withIndex()) {
|
||||||
|
try {
|
||||||
|
extensionRepositoryRepository.upsertRepo(
|
||||||
|
source,
|
||||||
|
"Repo #${index + 1}",
|
||||||
|
null,
|
||||||
|
source,
|
||||||
|
"NOFINGERPRINT-${index + 1}",
|
||||||
|
)
|
||||||
|
} catch (e: SaveExtensionRepoException) {
|
||||||
|
logcat(LogPriority.ERROR, e) { "Error Migrating Extension Repo with baseUrl: $source" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sourcePreferences.extensionRepos().delete()
|
||||||
|
return@withIOContext true
|
||||||
|
}
|
||||||
|
}
|
96
app/src/test/java/mihon/core/migration/MigratorTest.kt
Normal file
96
app/src/test/java/mihon/core/migration/MigratorTest.kt
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package mihon.core.migration
|
||||||
|
|
||||||
|
import io.mockk.Called
|
||||||
|
import io.mockk.spyk
|
||||||
|
import io.mockk.verify
|
||||||
|
import org.junit.jupiter.api.Assertions
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class MigratorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun initialVersion() {
|
||||||
|
val onMigrationComplete: () -> Unit = {}
|
||||||
|
val onMigrationCompleteSpy = spyk(onMigrationComplete)
|
||||||
|
val didMigration = Migrator.migrate(
|
||||||
|
old = 0,
|
||||||
|
new = 1,
|
||||||
|
migrations = listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { false }),
|
||||||
|
onMigrationComplete = onMigrationCompleteSpy
|
||||||
|
)
|
||||||
|
verify { onMigrationCompleteSpy() }
|
||||||
|
Assertions.assertTrue(didMigration)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun sameVersion() {
|
||||||
|
val onMigrationComplete: () -> Unit = {}
|
||||||
|
val onMigrationCompleteSpy = spyk(onMigrationComplete)
|
||||||
|
val didMigration = Migrator.migrate(
|
||||||
|
old = 1,
|
||||||
|
new = 1,
|
||||||
|
migrations = listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { true }),
|
||||||
|
onMigrationComplete = onMigrationCompleteSpy
|
||||||
|
)
|
||||||
|
verify { onMigrationCompleteSpy wasNot Called }
|
||||||
|
Assertions.assertFalse(didMigration)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun smallMigration() {
|
||||||
|
val onMigrationComplete: () -> Unit = {}
|
||||||
|
val onMigrationCompleteSpy = spyk(onMigrationComplete)
|
||||||
|
val didMigration = Migrator.migrate(
|
||||||
|
old = 1,
|
||||||
|
new = 2,
|
||||||
|
migrations = listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { true }),
|
||||||
|
onMigrationComplete = onMigrationCompleteSpy
|
||||||
|
)
|
||||||
|
verify { onMigrationCompleteSpy() }
|
||||||
|
Assertions.assertTrue(didMigration)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun largeMigration() {
|
||||||
|
val onMigrationComplete: () -> Unit = {}
|
||||||
|
val onMigrationCompleteSpy = spyk(onMigrationComplete)
|
||||||
|
val input = listOf(
|
||||||
|
Migration.of(Migration.ALWAYS) { true },
|
||||||
|
Migration.of(2f) { true },
|
||||||
|
Migration.of(3f) { true },
|
||||||
|
Migration.of(4f) { true },
|
||||||
|
Migration.of(5f) { true },
|
||||||
|
Migration.of(6f) { true },
|
||||||
|
Migration.of(7f) { true },
|
||||||
|
Migration.of(8f) { true },
|
||||||
|
Migration.of(9f) { true },
|
||||||
|
Migration.of(10f) { true },
|
||||||
|
)
|
||||||
|
val didMigration = Migrator.migrate(
|
||||||
|
old = 1,
|
||||||
|
new = 10,
|
||||||
|
migrations = input,
|
||||||
|
onMigrationComplete = onMigrationCompleteSpy
|
||||||
|
)
|
||||||
|
verify { onMigrationCompleteSpy() }
|
||||||
|
Assertions.assertTrue(didMigration)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun withinRangeMigration() {
|
||||||
|
val onMigrationComplete: () -> Unit = {}
|
||||||
|
val onMigrationCompleteSpy = spyk(onMigrationComplete)
|
||||||
|
val didMigration = Migrator.migrate(
|
||||||
|
old = 1,
|
||||||
|
new = 2,
|
||||||
|
migrations = listOf(
|
||||||
|
Migration.of(Migration.ALWAYS) { true },
|
||||||
|
Migration.of(2f) { true },
|
||||||
|
Migration.of(3f) { false }
|
||||||
|
),
|
||||||
|
onMigrationComplete = onMigrationCompleteSpy
|
||||||
|
)
|
||||||
|
verify { onMigrationCompleteSpy() }
|
||||||
|
Assertions.assertTrue(didMigration)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user