mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	More backup/restore code cleanup
This commit is contained in:
		@@ -1,205 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.backup
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import eu.kanade.data.DatabaseHandler
 | 
			
		||||
import eu.kanade.data.toLong
 | 
			
		||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
 | 
			
		||||
import eu.kanade.domain.chapter.model.toDbChapter
 | 
			
		||||
import eu.kanade.domain.manga.interactor.GetFavorites
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.TrackManager
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import eu.kanade.tachiyomi.source.SourceManager
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.toSChapter
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
import data.Mangas as DbManga
 | 
			
		||||
import eu.kanade.domain.manga.model.Manga as DomainManga
 | 
			
		||||
 | 
			
		||||
abstract class AbstractBackupManager(protected val context: Context) {
 | 
			
		||||
 | 
			
		||||
    protected val handler: DatabaseHandler = Injekt.get()
 | 
			
		||||
 | 
			
		||||
    internal val sourceManager: SourceManager = Injekt.get()
 | 
			
		||||
    internal val trackManager: TrackManager = Injekt.get()
 | 
			
		||||
    protected val preferences: PreferencesHelper = Injekt.get()
 | 
			
		||||
    private val getFavorites: GetFavorites = Injekt.get()
 | 
			
		||||
    private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get()
 | 
			
		||||
 | 
			
		||||
    abstract suspend fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns manga
 | 
			
		||||
     *
 | 
			
		||||
     * @return [Manga], null if not found
 | 
			
		||||
     */
 | 
			
		||||
    internal suspend fun getMangaFromDatabase(url: String, source: Long): DbManga? {
 | 
			
		||||
        return handler.awaitOneOrNull { mangasQueries.getMangaByUrlAndSource(url, source) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetches chapter information.
 | 
			
		||||
     *
 | 
			
		||||
     * @param source source of manga
 | 
			
		||||
     * @param manga manga that needs updating
 | 
			
		||||
     * @param chapters list of chapters in the backup
 | 
			
		||||
     * @return Updated manga chapters.
 | 
			
		||||
     */
 | 
			
		||||
    internal suspend fun restoreChapters(source: Source, manga: Manga, chapters: List<Chapter>): Pair<List<Chapter>, List<Chapter>> {
 | 
			
		||||
        val fetchedChapters = source.getChapterList(manga.toMangaInfo())
 | 
			
		||||
            .map { it.toSChapter() }
 | 
			
		||||
        val syncedChapters = syncChaptersWithSource.await(fetchedChapters, manga.toDomainManga()!!, source)
 | 
			
		||||
        if (syncedChapters.first.isNotEmpty()) {
 | 
			
		||||
            chapters.forEach { it.manga_id = manga.id }
 | 
			
		||||
            updateChapters(chapters)
 | 
			
		||||
        }
 | 
			
		||||
        return syncedChapters.first.map { it.toDbChapter() } to syncedChapters.second.map { it.toDbChapter() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns list containing manga from library
 | 
			
		||||
     *
 | 
			
		||||
     * @return [Manga] from library
 | 
			
		||||
     */
 | 
			
		||||
    protected suspend fun getFavoriteManga(): List<DomainManga> {
 | 
			
		||||
        return getFavorites.await()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Inserts manga and returns id
 | 
			
		||||
     *
 | 
			
		||||
     * @return id of [Manga], null if not found
 | 
			
		||||
     */
 | 
			
		||||
    internal suspend fun insertManga(manga: Manga): Long {
 | 
			
		||||
        return handler.awaitOne(true) {
 | 
			
		||||
            mangasQueries.insert(
 | 
			
		||||
                source = manga.source,
 | 
			
		||||
                url = manga.url,
 | 
			
		||||
                artist = manga.artist,
 | 
			
		||||
                author = manga.author,
 | 
			
		||||
                description = manga.description,
 | 
			
		||||
                genre = manga.getGenres(),
 | 
			
		||||
                title = manga.title,
 | 
			
		||||
                status = manga.status.toLong(),
 | 
			
		||||
                thumbnailUrl = manga.thumbnail_url,
 | 
			
		||||
                favorite = manga.favorite,
 | 
			
		||||
                lastUpdate = manga.last_update,
 | 
			
		||||
                nextUpdate = 0L,
 | 
			
		||||
                initialized = manga.initialized,
 | 
			
		||||
                viewerFlags = manga.viewer_flags.toLong(),
 | 
			
		||||
                chapterFlags = manga.chapter_flags.toLong(),
 | 
			
		||||
                coverLastModified = manga.cover_last_modified,
 | 
			
		||||
                dateAdded = manga.date_added,
 | 
			
		||||
            )
 | 
			
		||||
            mangasQueries.selectLastInsertedRowId()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal suspend fun updateManga(manga: Manga): Long {
 | 
			
		||||
        handler.await(true) {
 | 
			
		||||
            mangasQueries.update(
 | 
			
		||||
                source = manga.source,
 | 
			
		||||
                url = manga.url,
 | 
			
		||||
                artist = manga.artist,
 | 
			
		||||
                author = manga.author,
 | 
			
		||||
                description = manga.description,
 | 
			
		||||
                genre = manga.genre,
 | 
			
		||||
                title = manga.title,
 | 
			
		||||
                status = manga.status.toLong(),
 | 
			
		||||
                thumbnailUrl = manga.thumbnail_url,
 | 
			
		||||
                favorite = manga.favorite.toLong(),
 | 
			
		||||
                lastUpdate = manga.last_update,
 | 
			
		||||
                initialized = manga.initialized.toLong(),
 | 
			
		||||
                viewer = manga.viewer_flags.toLong(),
 | 
			
		||||
                chapterFlags = manga.chapter_flags.toLong(),
 | 
			
		||||
                coverLastModified = manga.cover_last_modified,
 | 
			
		||||
                dateAdded = manga.date_added,
 | 
			
		||||
                mangaId = manga.id!!,
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        return manga.id!!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Inserts list of chapters
 | 
			
		||||
     */
 | 
			
		||||
    protected suspend fun insertChapters(chapters: List<Chapter>) {
 | 
			
		||||
        handler.await(true) {
 | 
			
		||||
            chapters.forEach { chapter ->
 | 
			
		||||
                chaptersQueries.insert(
 | 
			
		||||
                    chapter.manga_id!!,
 | 
			
		||||
                    chapter.url,
 | 
			
		||||
                    chapter.name,
 | 
			
		||||
                    chapter.scanlator,
 | 
			
		||||
                    chapter.read,
 | 
			
		||||
                    chapter.bookmark,
 | 
			
		||||
                    chapter.last_page_read.toLong(),
 | 
			
		||||
                    chapter.chapter_number,
 | 
			
		||||
                    chapter.source_order.toLong(),
 | 
			
		||||
                    chapter.date_fetch,
 | 
			
		||||
                    chapter.date_upload,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates a list of chapters
 | 
			
		||||
     */
 | 
			
		||||
    protected suspend fun updateChapters(chapters: List<Chapter>) {
 | 
			
		||||
        handler.await(true) {
 | 
			
		||||
            chapters.forEach { chapter ->
 | 
			
		||||
                chaptersQueries.update(
 | 
			
		||||
                    chapter.manga_id!!,
 | 
			
		||||
                    chapter.url,
 | 
			
		||||
                    chapter.name,
 | 
			
		||||
                    chapter.scanlator,
 | 
			
		||||
                    chapter.read.toLong(),
 | 
			
		||||
                    chapter.bookmark.toLong(),
 | 
			
		||||
                    chapter.last_page_read.toLong(),
 | 
			
		||||
                    chapter.chapter_number.toDouble(),
 | 
			
		||||
                    chapter.source_order.toLong(),
 | 
			
		||||
                    chapter.date_fetch,
 | 
			
		||||
                    chapter.date_upload,
 | 
			
		||||
                    chapter.id!!,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates a list of chapters with known database ids
 | 
			
		||||
     */
 | 
			
		||||
    protected suspend fun updateKnownChapters(chapters: List<Chapter>) {
 | 
			
		||||
        handler.await(true) {
 | 
			
		||||
            chapters.forEach { chapter ->
 | 
			
		||||
                chaptersQueries.update(
 | 
			
		||||
                    mangaId = null,
 | 
			
		||||
                    url = null,
 | 
			
		||||
                    name = null,
 | 
			
		||||
                    scanlator = null,
 | 
			
		||||
                    read = chapter.read.toLong(),
 | 
			
		||||
                    bookmark = chapter.bookmark.toLong(),
 | 
			
		||||
                    lastPageRead = chapter.last_page_read.toLong(),
 | 
			
		||||
                    chapterNumber = null,
 | 
			
		||||
                    sourceOrder = null,
 | 
			
		||||
                    dateFetch = null,
 | 
			
		||||
                    dateUpload = null,
 | 
			
		||||
                    chapterId = chapter.id!!,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return number of backups.
 | 
			
		||||
     *
 | 
			
		||||
     * @return number of backups selected by user
 | 
			
		||||
     */
 | 
			
		||||
    protected fun numberOfBackups(): Int = preferences.numberOfBackups().get()
 | 
			
		||||
}
 | 
			
		||||
@@ -1,153 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.backup
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import eu.kanade.data.DatabaseHandler
 | 
			
		||||
import eu.kanade.data.chapter.NoChaptersException
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Track
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.TrackManager
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.Date
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
 | 
			
		||||
abstract class AbstractBackupRestore<T : AbstractBackupManager>(protected val context: Context, protected val notifier: BackupNotifier) {
 | 
			
		||||
 | 
			
		||||
    protected val handler: DatabaseHandler by injectLazy()
 | 
			
		||||
    protected val trackManager: TrackManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    var job: Job? = null
 | 
			
		||||
 | 
			
		||||
    protected lateinit var backupManager: T
 | 
			
		||||
 | 
			
		||||
    protected var restoreAmount = 0
 | 
			
		||||
    protected var restoreProgress = 0
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mapping of source ID to source name from backup data
 | 
			
		||||
     */
 | 
			
		||||
    protected var sourceMapping: Map<Long, String> = emptyMap()
 | 
			
		||||
 | 
			
		||||
    protected val errors = mutableListOf<Pair<Date, String>>()
 | 
			
		||||
 | 
			
		||||
    abstract suspend fun performRestore(uri: Uri): Boolean
 | 
			
		||||
 | 
			
		||||
    suspend fun restoreBackup(uri: Uri): Boolean {
 | 
			
		||||
        val startTime = System.currentTimeMillis()
 | 
			
		||||
        restoreProgress = 0
 | 
			
		||||
        errors.clear()
 | 
			
		||||
 | 
			
		||||
        if (!performRestore(uri)) {
 | 
			
		||||
            return false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val endTime = System.currentTimeMillis()
 | 
			
		||||
        val time = endTime - startTime
 | 
			
		||||
 | 
			
		||||
        val logFile = writeErrorLog()
 | 
			
		||||
 | 
			
		||||
        notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name)
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetches chapter information.
 | 
			
		||||
     *
 | 
			
		||||
     * @param source source of manga
 | 
			
		||||
     * @param manga manga that needs updating
 | 
			
		||||
     * @return Updated manga chapters.
 | 
			
		||||
     */
 | 
			
		||||
    internal suspend fun updateChapters(source: Source, manga: Manga, chapters: List<Chapter>): Pair<List<Chapter>, List<Chapter>> {
 | 
			
		||||
        return try {
 | 
			
		||||
            backupManager.restoreChapters(source, manga, chapters)
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            // If there's any error, return empty update and continue.
 | 
			
		||||
            val errorMessage = if (e is NoChaptersException) {
 | 
			
		||||
                context.getString(R.string.no_chapters_error)
 | 
			
		||||
            } else {
 | 
			
		||||
                e.message
 | 
			
		||||
            }
 | 
			
		||||
            errors.add(Date() to "${manga.title} - $errorMessage")
 | 
			
		||||
            Pair(emptyList(), emptyList())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refreshes tracking information.
 | 
			
		||||
     *
 | 
			
		||||
     * @param manga manga that needs updating.
 | 
			
		||||
     * @param tracks list containing tracks from restore file.
 | 
			
		||||
     */
 | 
			
		||||
    internal suspend fun updateTracking(manga: Manga, tracks: List<Track>) {
 | 
			
		||||
        tracks.forEach { track ->
 | 
			
		||||
            val service = trackManager.getService(track.sync_id.toLong())
 | 
			
		||||
            if (service != null && service.isLogged) {
 | 
			
		||||
                try {
 | 
			
		||||
                    val updatedTrack = service.refresh(track)
 | 
			
		||||
                    handler.await {
 | 
			
		||||
                        manga_syncQueries.insert(
 | 
			
		||||
                            updatedTrack.manga_id,
 | 
			
		||||
                            updatedTrack.sync_id.toLong(),
 | 
			
		||||
                            updatedTrack.media_id,
 | 
			
		||||
                            updatedTrack.library_id,
 | 
			
		||||
                            updatedTrack.title,
 | 
			
		||||
                            updatedTrack.last_chapter_read.toDouble(),
 | 
			
		||||
                            updatedTrack.total_chapters.toLong(),
 | 
			
		||||
                            updatedTrack.status.toLong(),
 | 
			
		||||
                            updatedTrack.score,
 | 
			
		||||
                            updatedTrack.tracking_url,
 | 
			
		||||
                            updatedTrack.started_reading_date,
 | 
			
		||||
                            updatedTrack.finished_reading_date,
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (e: Exception) {
 | 
			
		||||
                    errors.add(Date() to "${manga.title} - ${e.message}")
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                val serviceName = service?.nameRes()?.let { context.getString(it) }
 | 
			
		||||
                errors.add(Date() to "${manga.title} - ${context.getString(R.string.tracker_not_logged_in, serviceName)}")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called to update dialog in [BackupConst]
 | 
			
		||||
     *
 | 
			
		||||
     * @param progress restore progress
 | 
			
		||||
     * @param amount total restoreAmount of manga
 | 
			
		||||
     * @param title title of restored manga
 | 
			
		||||
     */
 | 
			
		||||
    internal fun showRestoreProgress(
 | 
			
		||||
        progress: Int,
 | 
			
		||||
        amount: Int,
 | 
			
		||||
        title: String,
 | 
			
		||||
    ) {
 | 
			
		||||
        notifier.showRestoreProgress(title, progress, amount)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun writeErrorLog(): File {
 | 
			
		||||
        try {
 | 
			
		||||
            if (errors.isNotEmpty()) {
 | 
			
		||||
                val file = context.createFileInCacheDir("tachiyomi_restore.txt")
 | 
			
		||||
                val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
 | 
			
		||||
 | 
			
		||||
                file.bufferedWriter().use { out ->
 | 
			
		||||
                    errors.forEach { (date, message) ->
 | 
			
		||||
                        out.write("[${sdf.format(date)}] $message\n")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return file
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            // Empty
 | 
			
		||||
        }
 | 
			
		||||
        return File("")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,9 +5,12 @@ import android.net.Uri
 | 
			
		||||
import com.hippo.unifile.UniFile
 | 
			
		||||
import data.Manga_sync
 | 
			
		||||
import data.Mangas
 | 
			
		||||
import eu.kanade.data.category.categoryMapper
 | 
			
		||||
import eu.kanade.data.DatabaseHandler
 | 
			
		||||
import eu.kanade.data.toLong
 | 
			
		||||
import eu.kanade.domain.category.interactor.GetCategories
 | 
			
		||||
import eu.kanade.domain.category.model.Category
 | 
			
		||||
import eu.kanade.domain.history.model.HistoryUpdate
 | 
			
		||||
import eu.kanade.domain.manga.interactor.GetFavorites
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY_MASK
 | 
			
		||||
@@ -29,35 +32,43 @@ import eu.kanade.tachiyomi.data.backup.models.backupTrackMapper
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Track
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.source.SourceManager
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.logcat
 | 
			
		||||
import kotlinx.serialization.protobuf.ProtoBuf
 | 
			
		||||
import logcat.LogPriority
 | 
			
		||||
import okio.buffer
 | 
			
		||||
import okio.gzip
 | 
			
		||||
import okio.sink
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
import java.io.FileOutputStream
 | 
			
		||||
import java.util.Date
 | 
			
		||||
import kotlin.math.max
 | 
			
		||||
import eu.kanade.domain.manga.model.Manga as DomainManga
 | 
			
		||||
 | 
			
		||||
class BackupManager(context: Context) : AbstractBackupManager(context) {
 | 
			
		||||
class BackupManager(
 | 
			
		||||
    private val context: Context,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    val parser = ProtoBuf
 | 
			
		||||
    private val handler: DatabaseHandler = Injekt.get()
 | 
			
		||||
    private val sourceManager: SourceManager = Injekt.get()
 | 
			
		||||
    private val preferences: PreferencesHelper = Injekt.get()
 | 
			
		||||
    private val getCategories: GetCategories = Injekt.get()
 | 
			
		||||
    private val getFavorites: GetFavorites = Injekt.get()
 | 
			
		||||
 | 
			
		||||
    internal val parser = ProtoBuf
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create backup Json file from database
 | 
			
		||||
     * Create backup file from database
 | 
			
		||||
     *
 | 
			
		||||
     * @param uri path of Uri
 | 
			
		||||
     * @param isAutoBackup backup called from scheduled backup job
 | 
			
		||||
     */
 | 
			
		||||
    @Suppress("BlockingMethodInNonBlockingContext")
 | 
			
		||||
    override suspend fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String {
 | 
			
		||||
        // Create root object
 | 
			
		||||
        var backup: Backup? = null
 | 
			
		||||
 | 
			
		||||
        val databaseManga = getFavoriteManga()
 | 
			
		||||
 | 
			
		||||
        backup = Backup(
 | 
			
		||||
    suspend fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String {
 | 
			
		||||
        val databaseManga = getFavorites.await()
 | 
			
		||||
        val backup = Backup(
 | 
			
		||||
            backupMangas(databaseManga, flags),
 | 
			
		||||
            backupCategories(flags),
 | 
			
		||||
            emptyList(),
 | 
			
		||||
@@ -73,7 +84,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
 | 
			
		||||
                    dir = dir.createDirectory("automatic")
 | 
			
		||||
 | 
			
		||||
                    // Delete older backups
 | 
			
		||||
                    val numberOfBackups = numberOfBackups()
 | 
			
		||||
                    val numberOfBackups = preferences.numberOfBackups().get()
 | 
			
		||||
                    val backupRegex = Regex("""tachiyomi_\d+-\d+-\d+_\d+-\d+.proto.gz""")
 | 
			
		||||
                    dir.listFiles { _, filename -> backupRegex.matches(filename) }
 | 
			
		||||
                        .orEmpty()
 | 
			
		||||
@@ -93,7 +104,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
 | 
			
		||||
                throw IllegalStateException("Failed to get handle on file")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val byteArray = parser.encodeToByteArray(BackupSerializer, backup!!)
 | 
			
		||||
            val byteArray = parser.encodeToByteArray(BackupSerializer, backup)
 | 
			
		||||
            if (byteArray.isEmpty()) {
 | 
			
		||||
                throw IllegalStateException(context.getString(R.string.empty_backup_error))
 | 
			
		||||
            }
 | 
			
		||||
@@ -133,7 +144,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
 | 
			
		||||
    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) {
 | 
			
		||||
            handler.awaitList { categoriesQueries.getCategories(categoryMapper) }
 | 
			
		||||
            getCategories.await()
 | 
			
		||||
                .filterNot(Category::isSystemCategory)
 | 
			
		||||
                .map(backupCategoryMapper)
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -170,7 +181,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
 | 
			
		||||
        // Check if user wants category information in backup
 | 
			
		||||
        if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) {
 | 
			
		||||
            // Backup categories for this manga
 | 
			
		||||
            val categoriesForManga = handler.awaitList { categoriesQueries.getCategoriesByMangaId(manga.id) }
 | 
			
		||||
            val categoriesForManga = getCategories.await(manga.id)
 | 
			
		||||
            if (categoriesForManga.isNotEmpty()) {
 | 
			
		||||
                mangaObject.categories = categoriesForManga.map { it.order }
 | 
			
		||||
            }
 | 
			
		||||
@@ -201,7 +212,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
 | 
			
		||||
        return mangaObject
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun restoreExistingManga(manga: Manga, dbManga: Mangas) {
 | 
			
		||||
    internal suspend fun restoreExistingManga(manga: Manga, dbManga: Mangas) {
 | 
			
		||||
        manga.id = dbManga._id
 | 
			
		||||
        manga.copyFrom(dbManga)
 | 
			
		||||
        updateManga(manga)
 | 
			
		||||
@@ -213,7 +224,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
 | 
			
		||||
     * @param manga manga that needs updating
 | 
			
		||||
     * @return Updated manga info.
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun restoreNewManga(manga: Manga): Manga {
 | 
			
		||||
    internal suspend fun restoreNewManga(manga: Manga): Manga {
 | 
			
		||||
        return manga.also {
 | 
			
		||||
            it.initialized = it.description != null
 | 
			
		||||
            it.id = insertManga(it)
 | 
			
		||||
@@ -227,7 +238,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
 | 
			
		||||
     */
 | 
			
		||||
    internal suspend fun restoreCategories(backupCategories: List<BackupCategory>) {
 | 
			
		||||
        // Get categories from file and from db
 | 
			
		||||
        val dbCategories = handler.awaitList { categoriesQueries.getCategories(categoryMapper) }
 | 
			
		||||
        val dbCategories = getCategories.await()
 | 
			
		||||
 | 
			
		||||
        val categories = backupCategories.map {
 | 
			
		||||
            var category = it.getCategory()
 | 
			
		||||
@@ -267,7 +278,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
 | 
			
		||||
     * @param categories the categories to restore.
 | 
			
		||||
     */
 | 
			
		||||
    internal suspend fun restoreCategories(manga: Manga, categories: List<Int>, backupCategories: List<BackupCategory>) {
 | 
			
		||||
        val dbCategories = handler.awaitList { categoriesQueries.getCategories() }
 | 
			
		||||
        val dbCategories = getCategories.await()
 | 
			
		||||
        val mangaCategoriesToUpdate = mutableListOf<Pair<Long, Long>>()
 | 
			
		||||
 | 
			
		||||
        categories.forEach { backupCategoryOrder ->
 | 
			
		||||
@@ -353,7 +364,6 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
 | 
			
		||||
        tracks.map { it.manga_id = manga.id!! }
 | 
			
		||||
 | 
			
		||||
        // Get tracks from database
 | 
			
		||||
 | 
			
		||||
        val dbTracks = handler.awaitList { manga_syncQueries.getTracksByMangaId(manga.id!!) }
 | 
			
		||||
        val toUpdate = mutableListOf<Manga_sync>()
 | 
			
		||||
        val toInsert = mutableListOf<Track>()
 | 
			
		||||
@@ -452,4 +462,139 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
 | 
			
		||||
        newChapters[true]?.let { updateKnownChapters(it) }
 | 
			
		||||
        newChapters[false]?.let { insertChapters(it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns manga
 | 
			
		||||
     *
 | 
			
		||||
     * @return [Manga], null if not found
 | 
			
		||||
     */
 | 
			
		||||
    internal suspend fun getMangaFromDatabase(url: String, source: Long): Mangas? {
 | 
			
		||||
        return handler.awaitOneOrNull { mangasQueries.getMangaByUrlAndSource(url, source) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Inserts manga and returns id
 | 
			
		||||
     *
 | 
			
		||||
     * @return id of [Manga], null if not found
 | 
			
		||||
     */
 | 
			
		||||
    private suspend fun insertManga(manga: Manga): Long {
 | 
			
		||||
        return handler.awaitOne(true) {
 | 
			
		||||
            mangasQueries.insert(
 | 
			
		||||
                source = manga.source,
 | 
			
		||||
                url = manga.url,
 | 
			
		||||
                artist = manga.artist,
 | 
			
		||||
                author = manga.author,
 | 
			
		||||
                description = manga.description,
 | 
			
		||||
                genre = manga.getGenres(),
 | 
			
		||||
                title = manga.title,
 | 
			
		||||
                status = manga.status.toLong(),
 | 
			
		||||
                thumbnailUrl = manga.thumbnail_url,
 | 
			
		||||
                favorite = manga.favorite,
 | 
			
		||||
                lastUpdate = manga.last_update,
 | 
			
		||||
                nextUpdate = 0L,
 | 
			
		||||
                initialized = manga.initialized,
 | 
			
		||||
                viewerFlags = manga.viewer_flags.toLong(),
 | 
			
		||||
                chapterFlags = manga.chapter_flags.toLong(),
 | 
			
		||||
                coverLastModified = manga.cover_last_modified,
 | 
			
		||||
                dateAdded = manga.date_added,
 | 
			
		||||
            )
 | 
			
		||||
            mangasQueries.selectLastInsertedRowId()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun updateManga(manga: Manga): Long {
 | 
			
		||||
        handler.await(true) {
 | 
			
		||||
            mangasQueries.update(
 | 
			
		||||
                source = manga.source,
 | 
			
		||||
                url = manga.url,
 | 
			
		||||
                artist = manga.artist,
 | 
			
		||||
                author = manga.author,
 | 
			
		||||
                description = manga.description,
 | 
			
		||||
                genre = manga.genre,
 | 
			
		||||
                title = manga.title,
 | 
			
		||||
                status = manga.status.toLong(),
 | 
			
		||||
                thumbnailUrl = manga.thumbnail_url,
 | 
			
		||||
                favorite = manga.favorite.toLong(),
 | 
			
		||||
                lastUpdate = manga.last_update,
 | 
			
		||||
                initialized = manga.initialized.toLong(),
 | 
			
		||||
                viewer = manga.viewer_flags.toLong(),
 | 
			
		||||
                chapterFlags = manga.chapter_flags.toLong(),
 | 
			
		||||
                coverLastModified = manga.cover_last_modified,
 | 
			
		||||
                dateAdded = manga.date_added,
 | 
			
		||||
                mangaId = manga.id!!,
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        return manga.id!!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Inserts list of chapters
 | 
			
		||||
     */
 | 
			
		||||
    private suspend fun insertChapters(chapters: List<Chapter>) {
 | 
			
		||||
        handler.await(true) {
 | 
			
		||||
            chapters.forEach { chapter ->
 | 
			
		||||
                chaptersQueries.insert(
 | 
			
		||||
                    chapter.manga_id!!,
 | 
			
		||||
                    chapter.url,
 | 
			
		||||
                    chapter.name,
 | 
			
		||||
                    chapter.scanlator,
 | 
			
		||||
                    chapter.read,
 | 
			
		||||
                    chapter.bookmark,
 | 
			
		||||
                    chapter.last_page_read.toLong(),
 | 
			
		||||
                    chapter.chapter_number,
 | 
			
		||||
                    chapter.source_order.toLong(),
 | 
			
		||||
                    chapter.date_fetch,
 | 
			
		||||
                    chapter.date_upload,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates a list of chapters
 | 
			
		||||
     */
 | 
			
		||||
    private suspend fun updateChapters(chapters: List<Chapter>) {
 | 
			
		||||
        handler.await(true) {
 | 
			
		||||
            chapters.forEach { chapter ->
 | 
			
		||||
                chaptersQueries.update(
 | 
			
		||||
                    chapter.manga_id!!,
 | 
			
		||||
                    chapter.url,
 | 
			
		||||
                    chapter.name,
 | 
			
		||||
                    chapter.scanlator,
 | 
			
		||||
                    chapter.read.toLong(),
 | 
			
		||||
                    chapter.bookmark.toLong(),
 | 
			
		||||
                    chapter.last_page_read.toLong(),
 | 
			
		||||
                    chapter.chapter_number.toDouble(),
 | 
			
		||||
                    chapter.source_order.toLong(),
 | 
			
		||||
                    chapter.date_fetch,
 | 
			
		||||
                    chapter.date_upload,
 | 
			
		||||
                    chapter.id!!,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates a list of chapters with known database ids
 | 
			
		||||
     */
 | 
			
		||||
    private suspend fun updateKnownChapters(chapters: List<Chapter>) {
 | 
			
		||||
        handler.await(true) {
 | 
			
		||||
            chapters.forEach { chapter ->
 | 
			
		||||
                chaptersQueries.update(
 | 
			
		||||
                    mangaId = null,
 | 
			
		||||
                    url = null,
 | 
			
		||||
                    name = null,
 | 
			
		||||
                    scanlator = null,
 | 
			
		||||
                    read = chapter.read.toLong(),
 | 
			
		||||
                    bookmark = chapter.bookmark.toLong(),
 | 
			
		||||
                    lastPageRead = chapter.last_page_read.toLong(),
 | 
			
		||||
                    chapterNumber = null,
 | 
			
		||||
                    sourceOrder = null,
 | 
			
		||||
                    dateFetch = null,
 | 
			
		||||
                    dateUpload = null,
 | 
			
		||||
                    chapterId = chapter.id!!,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ class BackupRestoreService : Service() {
 | 
			
		||||
    private lateinit var wakeLock: PowerManager.WakeLock
 | 
			
		||||
 | 
			
		||||
    private lateinit var ioScope: CoroutineScope
 | 
			
		||||
    private var restorer: AbstractBackupRestore<*>? = null
 | 
			
		||||
    private var restorer: BackupRestorer? = null
 | 
			
		||||
    private lateinit var notifier: BackupNotifier
 | 
			
		||||
 | 
			
		||||
    override fun onCreate() {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,17 +11,74 @@ import eu.kanade.tachiyomi.data.backup.models.BackupSource
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Track
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import okio.buffer
 | 
			
		||||
import okio.gzip
 | 
			
		||||
import okio.source
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.Date
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
 | 
			
		||||
class BackupRestorer(context: Context, notifier: BackupNotifier) : AbstractBackupRestore<BackupManager>(context, notifier) {
 | 
			
		||||
class BackupRestorer(
 | 
			
		||||
    private val context: Context,
 | 
			
		||||
    private val notifier: BackupNotifier,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    var job: Job? = null
 | 
			
		||||
 | 
			
		||||
    private var backupManager = BackupManager(context)
 | 
			
		||||
 | 
			
		||||
    private var restoreAmount = 0
 | 
			
		||||
    private var restoreProgress = 0
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mapping of source ID to source name from backup data
 | 
			
		||||
     */
 | 
			
		||||
    private var sourceMapping: Map<Long, String> = emptyMap()
 | 
			
		||||
 | 
			
		||||
    private val errors = mutableListOf<Pair<Date, String>>()
 | 
			
		||||
 | 
			
		||||
    suspend fun restoreBackup(uri: Uri): Boolean {
 | 
			
		||||
        val startTime = System.currentTimeMillis()
 | 
			
		||||
        restoreProgress = 0
 | 
			
		||||
        errors.clear()
 | 
			
		||||
 | 
			
		||||
        if (!performRestore(uri)) {
 | 
			
		||||
            return false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val endTime = System.currentTimeMillis()
 | 
			
		||||
        val time = endTime - startTime
 | 
			
		||||
 | 
			
		||||
        val logFile = writeErrorLog()
 | 
			
		||||
 | 
			
		||||
        notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name)
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun writeErrorLog(): File {
 | 
			
		||||
        try {
 | 
			
		||||
            if (errors.isNotEmpty()) {
 | 
			
		||||
                val file = context.createFileInCacheDir("tachiyomi_restore.txt")
 | 
			
		||||
                val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
 | 
			
		||||
 | 
			
		||||
                file.bufferedWriter().use { out ->
 | 
			
		||||
                    errors.forEach { (date, message) ->
 | 
			
		||||
                        out.write("[${sdf.format(date)}] $message\n")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return file
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            // Empty
 | 
			
		||||
        }
 | 
			
		||||
        return File("")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Suppress("BlockingMethodInNonBlockingContext")
 | 
			
		||||
    override suspend fun performRestore(uri: Uri): Boolean {
 | 
			
		||||
        backupManager = BackupManager(context)
 | 
			
		||||
 | 
			
		||||
    private suspend fun performRestore(uri: Uri): Boolean {
 | 
			
		||||
        val backupString = context.contentResolver.openInputStream(uri)!!.source().gzip().buffer().use { it.readByteArray() }
 | 
			
		||||
        val backup = backupManager.parser.decodeFromByteArray(BackupSerializer, backupString)
 | 
			
		||||
 | 
			
		||||
@@ -125,4 +182,15 @@ class BackupRestorer(context: Context, notifier: BackupNotifier) : AbstractBacku
 | 
			
		||||
        backupManager.restoreHistory(history)
 | 
			
		||||
        backupManager.restoreTracking(manga, tracks)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called to update dialog in [BackupConst]
 | 
			
		||||
     *
 | 
			
		||||
     * @param progress restore progress
 | 
			
		||||
     * @param amount total restoreAmount of manga
 | 
			
		||||
     * @param title title of restored manga
 | 
			
		||||
     */
 | 
			
		||||
    private fun showRestoreProgress(progress: Int, amount: Int, title: String) {
 | 
			
		||||
        notifier.showRestoreProgress(title, progress, amount)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -68,7 +68,7 @@ open class BrowseSourcePresenter(
 | 
			
		||||
    private val sourceId: Long,
 | 
			
		||||
    searchQuery: String? = null,
 | 
			
		||||
    private val sourceManager: SourceManager = Injekt.get(),
 | 
			
		||||
    private val prefs: PreferencesHelper = Injekt.get(),
 | 
			
		||||
    private val preferences: PreferencesHelper = Injekt.get(),
 | 
			
		||||
    private val coverCache: CoverCache = Injekt.get(),
 | 
			
		||||
    private val getManga: GetManga = Injekt.get(),
 | 
			
		||||
    private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
 | 
			
		||||
@@ -153,7 +153,7 @@ open class BrowseSourcePresenter(
 | 
			
		||||
        pager = createPager(query, filters)
 | 
			
		||||
 | 
			
		||||
        val sourceId = source.id
 | 
			
		||||
        val sourceDisplayMode = prefs.sourceDisplayMode()
 | 
			
		||||
        val sourceDisplayMode = preferences.sourceDisplayMode()
 | 
			
		||||
 | 
			
		||||
        pagerJob?.cancel()
 | 
			
		||||
        pagerJob = presenterScope.launchIO {
 | 
			
		||||
 
 | 
			
		||||
@@ -18,11 +18,11 @@ import uy.kohesive.injekt.api.get
 | 
			
		||||
 | 
			
		||||
class MorePresenter(
 | 
			
		||||
    private val downloadManager: DownloadManager = Injekt.get(),
 | 
			
		||||
    preferencesHelper: PreferencesHelper = Injekt.get(),
 | 
			
		||||
    preferences: PreferencesHelper = Injekt.get(),
 | 
			
		||||
) : BasePresenter<MoreController>() {
 | 
			
		||||
 | 
			
		||||
    val downloadedOnly = preferencesHelper.downloadedOnly().asState()
 | 
			
		||||
    val incognitoMode = preferencesHelper.incognitoMode().asState()
 | 
			
		||||
    val downloadedOnly = preferences.downloadedOnly().asState()
 | 
			
		||||
    val incognitoMode = preferences.incognitoMode().asState()
 | 
			
		||||
 | 
			
		||||
    private var _state: MutableStateFlow<DownloadQueueState> = MutableStateFlow(DownloadQueueState.Stopped)
 | 
			
		||||
    val downloadQueueState: StateFlow<DownloadQueueState> = _state.asStateFlow()
 | 
			
		||||
 
 | 
			
		||||
@@ -50,17 +50,17 @@ fun Manga.removeCovers(coverCache: CoverCache = Injekt.get()): Int {
 | 
			
		||||
    return coverCache.deleteFromCache(this, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun DomainManga.shouldDownloadNewChapters(dbCategories: List<Long>, prefs: PreferencesHelper): Boolean {
 | 
			
		||||
fun DomainManga.shouldDownloadNewChapters(dbCategories: List<Long>, preferences: PreferencesHelper): Boolean {
 | 
			
		||||
    if (!favorite) return false
 | 
			
		||||
 | 
			
		||||
    val categories = dbCategories.ifEmpty { listOf(0L) }
 | 
			
		||||
 | 
			
		||||
    // Boolean to determine if user wants to automatically download new chapters.
 | 
			
		||||
    val downloadNewChapter = prefs.downloadNewChapter().get()
 | 
			
		||||
    val downloadNewChapter = preferences.downloadNewChapter().get()
 | 
			
		||||
    if (!downloadNewChapter) return false
 | 
			
		||||
 | 
			
		||||
    val includedCategories = prefs.downloadNewChapterCategories().get().map { it.toLong() }
 | 
			
		||||
    val excludedCategories = prefs.downloadNewChapterCategoriesExclude().get().map { it.toLong() }
 | 
			
		||||
    val includedCategories = preferences.downloadNewChapterCategories().get().map { it.toLong() }
 | 
			
		||||
    val excludedCategories = preferences.downloadNewChapterCategoriesExclude().get().map { it.toLong() }
 | 
			
		||||
 | 
			
		||||
    // Default: Download from all categories
 | 
			
		||||
    if (includedCategories.isEmpty() && excludedCategories.isEmpty()) return true
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
object ChapterSettingsHelper {
 | 
			
		||||
 | 
			
		||||
    private val prefs: PreferencesHelper by injectLazy()
 | 
			
		||||
    private val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
    private val getFavorites: GetFavorites by injectLazy()
 | 
			
		||||
    private val setMangaChapterFlags: SetMangaChapterFlags by injectLazy()
 | 
			
		||||
 | 
			
		||||
@@ -18,7 +18,7 @@ object ChapterSettingsHelper {
 | 
			
		||||
     * Updates the global Chapter Settings in Preferences.
 | 
			
		||||
     */
 | 
			
		||||
    fun setGlobalSettings(manga: Manga) {
 | 
			
		||||
        prefs.setChapterSettingsDefault(manga.toDbManga())
 | 
			
		||||
        preferences.setChapterSettingsDefault(manga.toDbManga())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -28,12 +28,12 @@ object ChapterSettingsHelper {
 | 
			
		||||
        launchIO {
 | 
			
		||||
            setMangaChapterFlags.awaitSetAllFlags(
 | 
			
		||||
                mangaId = manga.id,
 | 
			
		||||
                unreadFilter = prefs.filterChapterByRead().toLong(),
 | 
			
		||||
                downloadedFilter = prefs.filterChapterByDownloaded().toLong(),
 | 
			
		||||
                bookmarkedFilter = prefs.filterChapterByBookmarked().toLong(),
 | 
			
		||||
                sortingMode = prefs.sortChapterBySourceOrNumber().toLong(),
 | 
			
		||||
                sortingDirection = prefs.sortChapterByAscendingOrDescending().toLong(),
 | 
			
		||||
                displayMode = prefs.displayChapterByNameOrNumber().toLong(),
 | 
			
		||||
                unreadFilter = preferences.filterChapterByRead().toLong(),
 | 
			
		||||
                downloadedFilter = preferences.filterChapterByDownloaded().toLong(),
 | 
			
		||||
                bookmarkedFilter = preferences.filterChapterByBookmarked().toLong(),
 | 
			
		||||
                sortingMode = preferences.sortChapterBySourceOrNumber().toLong(),
 | 
			
		||||
                sortingDirection = preferences.sortChapterByAscendingOrDescending().toLong(),
 | 
			
		||||
                displayMode = preferences.displayChapterByNameOrNumber().toLong(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -41,12 +41,12 @@ object ChapterSettingsHelper {
 | 
			
		||||
    suspend fun applySettingDefaults(mangaId: Long) {
 | 
			
		||||
        setMangaChapterFlags.awaitSetAllFlags(
 | 
			
		||||
            mangaId = mangaId,
 | 
			
		||||
            unreadFilter = prefs.filterChapterByRead().toLong(),
 | 
			
		||||
            downloadedFilter = prefs.filterChapterByDownloaded().toLong(),
 | 
			
		||||
            bookmarkedFilter = prefs.filterChapterByBookmarked().toLong(),
 | 
			
		||||
            sortingMode = prefs.sortChapterBySourceOrNumber().toLong(),
 | 
			
		||||
            sortingDirection = prefs.sortChapterByAscendingOrDescending().toLong(),
 | 
			
		||||
            displayMode = prefs.displayChapterByNameOrNumber().toLong(),
 | 
			
		||||
            unreadFilter = preferences.filterChapterByRead().toLong(),
 | 
			
		||||
            downloadedFilter = preferences.filterChapterByDownloaded().toLong(),
 | 
			
		||||
            bookmarkedFilter = preferences.filterChapterByBookmarked().toLong(),
 | 
			
		||||
            sortingMode = preferences.sortChapterBySourceOrNumber().toLong(),
 | 
			
		||||
            sortingDirection = preferences.sortChapterByAscendingOrDescending().toLong(),
 | 
			
		||||
            displayMode = preferences.displayChapterByNameOrNumber().toLong(),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -59,12 +59,12 @@ object ChapterSettingsHelper {
 | 
			
		||||
                .map { manga ->
 | 
			
		||||
                    setMangaChapterFlags.awaitSetAllFlags(
 | 
			
		||||
                        mangaId = manga.id,
 | 
			
		||||
                        unreadFilter = prefs.filterChapterByRead().toLong(),
 | 
			
		||||
                        downloadedFilter = prefs.filterChapterByDownloaded().toLong(),
 | 
			
		||||
                        bookmarkedFilter = prefs.filterChapterByBookmarked().toLong(),
 | 
			
		||||
                        sortingMode = prefs.sortChapterBySourceOrNumber().toLong(),
 | 
			
		||||
                        sortingDirection = prefs.sortChapterByAscendingOrDescending().toLong(),
 | 
			
		||||
                        displayMode = prefs.displayChapterByNameOrNumber().toLong(),
 | 
			
		||||
                        unreadFilter = preferences.filterChapterByRead().toLong(),
 | 
			
		||||
                        downloadedFilter = preferences.filterChapterByDownloaded().toLong(),
 | 
			
		||||
                        bookmarkedFilter = preferences.filterChapterByBookmarked().toLong(),
 | 
			
		||||
                        sortingMode = preferences.sortChapterBySourceOrNumber().toLong(),
 | 
			
		||||
                        sortingDirection = preferences.sortChapterByAscendingOrDescending().toLong(),
 | 
			
		||||
                        displayMode = preferences.displayChapterByNameOrNumber().toLong(),
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -319,8 +319,8 @@ fun Context.isNightMode(): Boolean {
 | 
			
		||||
 * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java;l=348;drc=e28752c96fc3fb4d3354781469a1af3dbded4898
 | 
			
		||||
 */
 | 
			
		||||
fun Context.createReaderThemeContext(): Context {
 | 
			
		||||
    val prefs = Injekt.get<PreferencesHelper>()
 | 
			
		||||
    val isDarkBackground = when (prefs.readerTheme().get()) {
 | 
			
		||||
    val preferences = Injekt.get<PreferencesHelper>()
 | 
			
		||||
    val isDarkBackground = when (preferences.readerTheme().get()) {
 | 
			
		||||
        1, 2 -> true // Black, Gray
 | 
			
		||||
        3 -> applicationContext.isNightMode() // Automatic bg uses activity background by default
 | 
			
		||||
        else -> false // White
 | 
			
		||||
@@ -333,7 +333,7 @@ fun Context.createReaderThemeContext(): Context {
 | 
			
		||||
 | 
			
		||||
        val wrappedContext = ContextThemeWrapper(this, R.style.Theme_Tachiyomi)
 | 
			
		||||
        wrappedContext.applyOverrideConfiguration(overrideConf)
 | 
			
		||||
        ThemingDelegate.getThemeResIds(prefs.appTheme().get(), prefs.themeDarkAmoled().get())
 | 
			
		||||
        ThemingDelegate.getThemeResIds(preferences.appTheme().get(), preferences.themeDarkAmoled().get())
 | 
			
		||||
            .forEach { wrappedContext.theme.applyStyle(it, true) }
 | 
			
		||||
        return wrappedContext
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user