123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- package eu.kanade.tachiyomi
- import android.content.Context
- import androidx.core.content.edit
- import androidx.preference.PreferenceManager
- import eu.kanade.domain.base.BasePreferences
- import eu.kanade.domain.source.service.SourcePreferences
- import eu.kanade.domain.ui.UiPreferences
- import eu.kanade.tachiyomi.core.security.SecurityPreferences
- import eu.kanade.tachiyomi.data.backup.BackupCreateJob
- import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
- 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.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 tachiyomi.core.preference.Preference
- import tachiyomi.core.preference.PreferenceStore
- import tachiyomi.core.preference.TriState
- import tachiyomi.core.preference.getAndSet
- import tachiyomi.core.preference.getEnum
- import tachiyomi.core.preference.minusAssign
- import tachiyomi.core.preference.plusAssign
- import tachiyomi.domain.backup.service.BackupPreferences
- import tachiyomi.domain.library.service.LibraryPreferences
- import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED
- import tachiyomi.i18n.MR
- import java.io.File
- object Migrations {
- /**
- * 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,
- ): Boolean {
- val lastVersionCode = preferenceStore.getInt("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 prefs = PreferenceManager.getDefaultSharedPreferences(context)
- if (oldVersion < 14) {
- // Restore jobs after upgrading to Evernote's job scheduler.
- LibraryUpdateJob.setupTask(context)
- }
- if (oldVersion < 15) {
- // Delete internal chapter cache dir.
- File(context.cacheDir, "chapter_disk_cache").deleteRecursively()
- }
- if (oldVersion < 19) {
- // Move covers to external files dir.
- val oldDir = File(context.externalCacheDir, "cover_disk_cache")
- if (oldDir.exists()) {
- val destDir = context.getExternalFilesDir("covers")
- if (destDir != null) {
- oldDir.listFiles()?.forEach {
- it.renameTo(File(destDir, it.name))
- }
- }
- }
- }
- if (oldVersion < 26) {
- // Delete external chapter cache dir.
- val extCache = context.externalCacheDir
- if (extCache != null) {
- val chapterCache = File(extCache, "chapter_disk_cache")
- if (chapterCache.exists()) {
- chapterCache.deleteRecursively()
- }
- }
- }
- if (oldVersion < 43) {
- // Restore jobs after migrating from Evernote's job scheduler to WorkManager.
- LibraryUpdateJob.setupTask(context)
- BackupCreateJob.setupTask(context)
- }
- if (oldVersion < 44) {
- // Reset sorting preference if using removed sort by source
- val oldSortingMode = prefs.getInt(libraryPreferences.sortingMode().key(), 0)
- if (oldSortingMode == 5) { // SOURCE = 5
- prefs.edit {
- putInt(libraryPreferences.sortingMode().key(), 0) // ALPHABETICAL = 0
- }
- }
- }
- if (oldVersion < 52) {
- // Migrate library filters to tri-state versions
- fun convertBooleanPrefToTriState(key: String): Int {
- val oldPrefValue = prefs.getBoolean(key, false)
- return if (oldPrefValue) {
- 1
- } else {
- 0
- }
- }
- prefs.edit {
- putInt(
- libraryPreferences.filterDownloaded().key(),
- convertBooleanPrefToTriState("pref_filter_downloaded_key"),
- )
- remove("pref_filter_downloaded_key")
- putInt(
- libraryPreferences.filterUnread().key(),
- convertBooleanPrefToTriState("pref_filter_unread_key"),
- )
- remove("pref_filter_unread_key")
- putInt(
- libraryPreferences.filterCompleted().key(),
- convertBooleanPrefToTriState("pref_filter_completed_key"),
- )
- remove("pref_filter_completed_key")
- }
- }
- if (oldVersion < 54) {
- // Force MAL log out due to login flow change
- // v52: switched from scraping to WebView
- // v53: switched from WebView to OAuth
- if (trackerManager.myAnimeList.isLoggedIn) {
- trackerManager.myAnimeList.logout()
- context.toast(MR.strings.myanimelist_relogin)
- }
- }
- if (oldVersion < 57) {
- // 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 < 59) {
- // Reset rotation to Free after replacing Lock
- if (prefs.contains("pref_rotation_type_key")) {
- prefs.edit {
- putInt("pref_rotation_type_key", 1)
- }
- }
- }
- if (oldVersion < 60) {
- // 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")
- }
- }
- if (oldVersion < 61) {
- // Handle removed every 1 or 2 hour library updates
- val updateInterval = libraryPreferences.autoUpdateInterval().get()
- if (updateInterval == 1 || updateInterval == 2) {
- libraryPreferences.autoUpdateInterval().set(3)
- LibraryUpdateJob.setupTask(context, 3)
- }
- }
- if (oldVersion < 64) {
- val oldSortingMode = prefs.getInt(libraryPreferences.sortingMode().key(), 0)
- val oldSortingDirection = prefs.getBoolean("library_sorting_ascending", true)
- val newSortingMode = when (oldSortingMode) {
- 0 -> "ALPHABETICAL"
- 1 -> "LAST_READ"
- 2 -> "LAST_CHECKED"
- 3 -> "UNREAD"
- 4 -> "TOTAL_CHAPTERS"
- 6 -> "LATEST_CHAPTER"
- 8 -> "DATE_FETCHED"
- 7 -> "DATE_ADDED"
- 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)
- }
- }
- if (oldVersion < 70) {
- if (sourcePreferences.enabledLanguages().isSet()) {
- sourcePreferences.enabledLanguages() += "all"
- }
- }
- if (oldVersion < 71) {
- // 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)
- LibraryUpdateJob.setupTask(context, 12)
- }
- }
- if (oldVersion < 72) {
- val oldUpdateOngoingOnly = prefs.getBoolean("pref_update_only_non_completed_key", true)
- if (!oldUpdateOngoingOnly) {
- libraryPreferences.autoUpdateMangaRestrictions() -= MANGA_NON_COMPLETED
- }
- }
- if (oldVersion < 75) {
- 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 < 76) {
- BackupCreateJob.setupTask(context)
- }
- if (oldVersion < 77) {
- val oldReaderTap = prefs.getBoolean("reader_tap", false)
- if (!oldReaderTap) {
- readerPreferences.navigationModePager().set(5)
- readerPreferences.navigationModeWebtoon().set(5)
- }
- }
- if (oldVersion < 81) {
- // Handle renamed enum values
- prefs.edit {
- val newSortingMode = when (
- val oldSortingMode = prefs.getString(
- libraryPreferences.sortingMode().key(),
- "ALPHABETICAL",
- )
- ) {
- "LAST_CHECKED" -> "LAST_MANGA_UPDATE"
- "UNREAD" -> "UNREAD_COUNT"
- "DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
- else -> oldSortingMode
- }
- putString(libraryPreferences.sortingMode().key(), newSortingMode)
- }
- }
- if (oldVersion < 82) {
- 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 < 84) {
- if (backupPreferences.backupInterval().get() == 0) {
- backupPreferences.backupInterval().set(12)
- BackupCreateJob.setupTask(context)
- }
- }
- if (oldVersion < 85) {
- 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 < 86) {
- if (uiPreferences.themeMode().isSet()) {
- prefs.edit {
- val themeMode = prefs.getString(uiPreferences.themeMode().key(), null) ?: return@edit
- putString(uiPreferences.themeMode().key(), themeMode.uppercase())
- }
- }
- }
- if (oldVersion < 92) {
- 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 < 96) {
- LibraryUpdateJob.cancelAllWorks(context)
- LibraryUpdateJob.setupTask(context)
- }
- if (oldVersion < 97) {
- // Removed background jobs
- context.workManager.cancelAllWorkByTag("UpdateChecker")
- context.workManager.cancelAllWorkByTag("ExtensionUpdate")
- prefs.edit {
- remove("automatic_ext_updates")
- }
- }
- if (oldVersion < 99) {
- val prefKeys = listOf(
- "pref_filter_library_downloaded",
- "pref_filter_library_unread",
- "pref_filter_library_started",
- "pref_filter_library_bookmarked",
- "pref_filter_library_completed",
- ) + 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 < 100) {
- BackupCreateJob.setupTask(context)
- }
- if (oldVersion < 105) {
- val pref = libraryPreferences.autoUpdateDeviceRestrictions()
- if (pref.isSet() && "battery_not_low" in pref.get()) {
- pref.getAndSet { it - "battery_not_low" }
- }
- }
- if (oldVersion < 106) {
- val pref = preferenceStore.getInt("relative_time", 7)
- if (pref.get() == 0) {
- uiPreferences.relativeTime().set(false)
- }
- }
- if (oldVersion < 107) {
- replacePreferences(
- preferenceStore = preferenceStore,
- filterPredicate = { it.key.startsWith("pref_mangasync_") || it.key.startsWith("track_token_") },
- newKey = { Preference.privateKey(it) },
- )
- }
- if (oldVersion < 108) {
- 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",
- )
- replacePreferences(
- preferenceStore = preferenceStore,
- filterPredicate = { it.key in prefsToReplace },
- newKey = { Preference.appStateKey(it) },
- )
- }
- return true
- }
- return false
- }
- }
- @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()
- }
- }
- }
- }
|