mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	Add app settings to backups
This should be compatible with Aniyomi's implementation. Related to #1857 Co-authored-by: jmir1 <jmir1@users.noreply.github.com>
This commit is contained in:
		@@ -147,6 +147,7 @@ object SettingsBackupScreen : SearchableSettings {
 | 
			
		||||
                BackupConst.BACKUP_CHAPTER to R.string.chapters,
 | 
			
		||||
                BackupConst.BACKUP_TRACK to R.string.track,
 | 
			
		||||
                BackupConst.BACKUP_HISTORY to R.string.history,
 | 
			
		||||
                BackupConst.BACKUP_APP_PREFS to R.string.app_settings,
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        val flags = remember { choices.keys.toMutableStateList() }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,18 @@ package eu.kanade.tachiyomi.data.backup
 | 
			
		||||
internal object BackupConst {
 | 
			
		||||
    const val BACKUP_CATEGORY = 0x1
 | 
			
		||||
    const val BACKUP_CATEGORY_MASK = 0x1
 | 
			
		||||
 | 
			
		||||
    const val BACKUP_CHAPTER = 0x2
 | 
			
		||||
    const val BACKUP_CHAPTER_MASK = 0x2
 | 
			
		||||
 | 
			
		||||
    const val BACKUP_HISTORY = 0x4
 | 
			
		||||
    const val BACKUP_HISTORY_MASK = 0x4
 | 
			
		||||
 | 
			
		||||
    const val BACKUP_TRACK = 0x8
 | 
			
		||||
    const val BACKUP_TRACK_MASK = 0x8
 | 
			
		||||
    const val BACKUP_ALL = 0xF
 | 
			
		||||
 | 
			
		||||
    const val BACKUP_APP_PREFS = 0x10
 | 
			
		||||
    const val BACKUP_APP_PREFS_MASK = 0x10
 | 
			
		||||
 | 
			
		||||
    const val BACKUP_ALL = 0x1F
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@ import android.net.Uri
 | 
			
		||||
import com.hippo.unifile.UniFile
 | 
			
		||||
import eu.kanade.domain.chapter.model.copyFrom
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_APP_PREFS
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_APP_PREFS_MASK
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY_MASK
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER
 | 
			
		||||
@@ -18,8 +20,15 @@ import eu.kanade.tachiyomi.data.backup.models.Backup
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BackupSource
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.backupCategoryMapper
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.backupChapterMapper
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.backupTrackMapper
 | 
			
		||||
@@ -30,6 +39,7 @@ import logcat.LogPriority
 | 
			
		||||
import okio.buffer
 | 
			
		||||
import okio.gzip
 | 
			
		||||
import okio.sink
 | 
			
		||||
import tachiyomi.core.preference.PreferenceStore
 | 
			
		||||
import tachiyomi.core.util.system.logcat
 | 
			
		||||
import tachiyomi.data.DatabaseHandler
 | 
			
		||||
import tachiyomi.data.Manga_sync
 | 
			
		||||
@@ -61,6 +71,7 @@ class BackupManager(
 | 
			
		||||
    private val getCategories: GetCategories = Injekt.get()
 | 
			
		||||
    private val getFavorites: GetFavorites = Injekt.get()
 | 
			
		||||
    private val getHistory: GetHistory = Injekt.get()
 | 
			
		||||
    private val preferenceStore: PreferenceStore = Injekt.get()
 | 
			
		||||
 | 
			
		||||
    internal val parser = ProtoBuf
 | 
			
		||||
 | 
			
		||||
@@ -81,6 +92,7 @@ class BackupManager(
 | 
			
		||||
            backupCategories(flags),
 | 
			
		||||
            emptyList(),
 | 
			
		||||
            prepExtensionInfoForSync(databaseManga),
 | 
			
		||||
            backupAppPreferences(flags),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        var file: UniFile? = null
 | 
			
		||||
@@ -133,7 +145,7 @@ class BackupManager(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun prepExtensionInfoForSync(mangas: List<Manga>): List<BackupSource> {
 | 
			
		||||
    private fun prepExtensionInfoForSync(mangas: List<Manga>): List<BackupSource> {
 | 
			
		||||
        return mangas
 | 
			
		||||
            .asSequence()
 | 
			
		||||
            .map(Manga::source)
 | 
			
		||||
@@ -148,7 +160,7 @@ class BackupManager(
 | 
			
		||||
     *
 | 
			
		||||
     * @return list of [BackupCategory] to be backed up
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun backupCategories(options: Int): List<BackupCategory> {
 | 
			
		||||
    private suspend fun backupCategories(options: Int): List<BackupCategory> {
 | 
			
		||||
        // Check if user wants category information in backup
 | 
			
		||||
        return if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) {
 | 
			
		||||
            getCategories.await()
 | 
			
		||||
@@ -159,7 +171,7 @@ class BackupManager(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun backupMangas(mangas: List<Manga>, flags: Int): List<BackupManga> {
 | 
			
		||||
    private suspend fun backupMangas(mangas: List<Manga>, flags: Int): List<BackupManga> {
 | 
			
		||||
        return mangas.map {
 | 
			
		||||
            backupManga(it, flags)
 | 
			
		||||
        }
 | 
			
		||||
@@ -219,6 +231,25 @@ class BackupManager(
 | 
			
		||||
        return mangaObject
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Suppress("UNCHECKED_CAST")
 | 
			
		||||
    private fun backupAppPreferences(flags: Int): List<BackupPreference> {
 | 
			
		||||
        if (flags and BACKUP_APP_PREFS_MASK != BACKUP_APP_PREFS) return emptyList()
 | 
			
		||||
 | 
			
		||||
        return preferenceStore.getAll().mapNotNull { (key, value) ->
 | 
			
		||||
            when (value) {
 | 
			
		||||
                is Int -> BackupPreference(key, IntPreferenceValue(value))
 | 
			
		||||
                is Long -> BackupPreference(key, LongPreferenceValue(value))
 | 
			
		||||
                is Float -> BackupPreference(key, FloatPreferenceValue(value))
 | 
			
		||||
                is String -> BackupPreference(key, StringPreferenceValue(value))
 | 
			
		||||
                is Boolean -> BackupPreference(key, BooleanPreferenceValue(value))
 | 
			
		||||
                is Set<*> -> (value as? Set<String>)?.let {
 | 
			
		||||
                    BackupPreference(key, StringSetPreferenceValue(it))
 | 
			
		||||
                }
 | 
			
		||||
                else -> null
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal suspend fun restoreExistingManga(manga: Manga, dbManga: Mangas): Manga {
 | 
			
		||||
        var updatedManga = manga.copy(id = dbManga._id)
 | 
			
		||||
        updatedManga = updatedManga.copyFrom(dbManga)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,13 +7,20 @@ import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BackupSource
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
 | 
			
		||||
import eu.kanade.tachiyomi.util.BackupUtil
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
 | 
			
		||||
import kotlinx.coroutines.coroutineScope
 | 
			
		||||
import kotlinx.coroutines.isActive
 | 
			
		||||
import tachiyomi.core.preference.PreferenceStore
 | 
			
		||||
import tachiyomi.domain.chapter.model.Chapter
 | 
			
		||||
import tachiyomi.domain.chapter.repository.ChapterRepository
 | 
			
		||||
import tachiyomi.domain.manga.interactor.FetchInterval
 | 
			
		||||
import tachiyomi.domain.manga.model.Manga
 | 
			
		||||
import tachiyomi.domain.track.model.Track
 | 
			
		||||
@@ -30,8 +37,8 @@ class BackupRestorer(
 | 
			
		||||
    private val notifier: BackupNotifier,
 | 
			
		||||
) {
 | 
			
		||||
    private val updateManga: UpdateManga = Injekt.get()
 | 
			
		||||
    private val chapterRepository: ChapterRepository = Injekt.get()
 | 
			
		||||
    private val fetchInterval: FetchInterval = Injekt.get()
 | 
			
		||||
    private val preferenceStore: PreferenceStore = Injekt.get()
 | 
			
		||||
 | 
			
		||||
    private var now = ZonedDateTime.now()
 | 
			
		||||
    private var currentFetchWindow = fetchInterval.getWindow(now)
 | 
			
		||||
@@ -106,6 +113,8 @@ class BackupRestorer(
 | 
			
		||||
        currentFetchWindow = fetchInterval.getWindow(now)
 | 
			
		||||
 | 
			
		||||
        return coroutineScope {
 | 
			
		||||
            restoreAppPreferences(backup.backupPreferences)
 | 
			
		||||
 | 
			
		||||
            // Restore individual manga
 | 
			
		||||
            backup.backupManga.forEach {
 | 
			
		||||
                if (!isActive) {
 | 
			
		||||
@@ -115,6 +124,7 @@ class BackupRestorer(
 | 
			
		||||
                restoreManga(it, backup.backupCategories, sync)
 | 
			
		||||
            }
 | 
			
		||||
            // TODO: optionally trigger online library + tracker update
 | 
			
		||||
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -200,6 +210,45 @@ class BackupRestorer(
 | 
			
		||||
        backupManager.restoreTracking(manga, tracks)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun restoreAppPreferences(preferences: List<BackupPreference>) {
 | 
			
		||||
        val prefs = preferenceStore.getAll()
 | 
			
		||||
 | 
			
		||||
        preferences.forEach { (key, value) ->
 | 
			
		||||
            when (value) {
 | 
			
		||||
                is IntPreferenceValue -> {
 | 
			
		||||
                    if (prefs[key] is Int?) {
 | 
			
		||||
                        preferenceStore.getInt(key).set(value.value)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                is LongPreferenceValue -> {
 | 
			
		||||
                    if (prefs[key] is Long?) {
 | 
			
		||||
                        preferenceStore.getLong(key).set(value.value)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                is FloatPreferenceValue -> {
 | 
			
		||||
                    if (prefs[key] is Float?) {
 | 
			
		||||
                        preferenceStore.getFloat(key).set(value.value)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                is StringPreferenceValue -> {
 | 
			
		||||
                    if (prefs[key] is String?) {
 | 
			
		||||
                        preferenceStore.getString(key).set(value.value)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                is BooleanPreferenceValue -> {
 | 
			
		||||
                    if (prefs[key] is Boolean?) {
 | 
			
		||||
                        preferenceStore.getBoolean(key).set(value.value)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                is StringSetPreferenceValue -> {
 | 
			
		||||
                    if (prefs[key] is Set<*>?) {
 | 
			
		||||
                        preferenceStore.getStringSet(key).set(value.value)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called to update dialog in [BackupConst]
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -11,9 +11,9 @@ import java.util.Locale
 | 
			
		||||
data class Backup(
 | 
			
		||||
    @ProtoNumber(1) val backupManga: List<BackupManga>,
 | 
			
		||||
    @ProtoNumber(2) var backupCategories: List<BackupCategory> = emptyList(),
 | 
			
		||||
    // Bump by 100 to specify this is a 0.x value
 | 
			
		||||
    @ProtoNumber(100) var backupBrokenSources: List<BrokenBackupSource> = emptyList(),
 | 
			
		||||
    @ProtoNumber(101) var backupSources: List<BackupSource> = emptyList(),
 | 
			
		||||
    @ProtoNumber(104) var backupPreferences: List<BackupPreference> = emptyList(),
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ class BackupCategory(
 | 
			
		||||
    @ProtoNumber(1) var name: String,
 | 
			
		||||
    @ProtoNumber(2) var order: Long = 0,
 | 
			
		||||
    // @ProtoNumber(3) val updateInterval: Int = 0, 1.x value not used in 0.x
 | 
			
		||||
    // Bump by 100 to specify this is a 0.x value
 | 
			
		||||
    @ProtoNumber(100) var flags: Long = 0,
 | 
			
		||||
) {
 | 
			
		||||
    fun getCategory(): Category {
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,31 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.backup.models
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
import kotlinx.serialization.protobuf.ProtoNumber
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class BackupPreference(
 | 
			
		||||
    @ProtoNumber(1) val key: String,
 | 
			
		||||
    @ProtoNumber(2) val value: PreferenceValue,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
sealed class PreferenceValue
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class IntPreferenceValue(val value: Int) : PreferenceValue()
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class LongPreferenceValue(val value: Long) : PreferenceValue()
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class FloatPreferenceValue(val value: Float) : PreferenceValue()
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class StringPreferenceValue(val value: String) : PreferenceValue()
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class BooleanPreferenceValue(val value: Boolean) : PreferenceValue()
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class StringSetPreferenceValue(val value: Set<String>) : PreferenceValue()
 | 
			
		||||
		Reference in New Issue
	
	Block a user