mirror of
https://git.mihon.tech/mihonapp/mihon
synced 2024-11-26 15:16:04 +03:00
More backup/restore code cleanup
This commit is contained in:
parent
19eb4aaac9
commit
9f0052eceb
10 changed files with 270 additions and 415 deletions
|
@ -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 com.hippo.unifile.UniFile
|
||||||
import data.Manga_sync
|
import data.Manga_sync
|
||||||
import data.Mangas
|
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.category.model.Category
|
||||||
import eu.kanade.domain.history.model.HistoryUpdate
|
import eu.kanade.domain.history.model.HistoryUpdate
|
||||||
|
import eu.kanade.domain.manga.interactor.GetFavorites
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY
|
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_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.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.database.models.Track
|
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 eu.kanade.tachiyomi.util.system.logcat
|
||||||
import kotlinx.serialization.protobuf.ProtoBuf
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.gzip
|
import okio.gzip
|
||||||
import okio.sink
|
import okio.sink
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import eu.kanade.domain.manga.model.Manga as DomainManga
|
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 uri path of Uri
|
||||||
* @param isAutoBackup backup called from scheduled backup job
|
* @param isAutoBackup backup called from scheduled backup job
|
||||||
*/
|
*/
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
override suspend fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String {
|
suspend fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String {
|
||||||
// Create root object
|
val databaseManga = getFavorites.await()
|
||||||
var backup: Backup? = null
|
val backup = Backup(
|
||||||
|
|
||||||
val databaseManga = getFavoriteManga()
|
|
||||||
|
|
||||||
backup = Backup(
|
|
||||||
backupMangas(databaseManga, flags),
|
backupMangas(databaseManga, flags),
|
||||||
backupCategories(flags),
|
backupCategories(flags),
|
||||||
emptyList(),
|
emptyList(),
|
||||||
|
@ -73,7 +84,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
|
||||||
dir = dir.createDirectory("automatic")
|
dir = dir.createDirectory("automatic")
|
||||||
|
|
||||||
// Delete older backups
|
// Delete older backups
|
||||||
val numberOfBackups = numberOfBackups()
|
val numberOfBackups = preferences.numberOfBackups().get()
|
||||||
val backupRegex = Regex("""tachiyomi_\d+-\d+-\d+_\d+-\d+.proto.gz""")
|
val backupRegex = Regex("""tachiyomi_\d+-\d+-\d+_\d+-\d+.proto.gz""")
|
||||||
dir.listFiles { _, filename -> backupRegex.matches(filename) }
|
dir.listFiles { _, filename -> backupRegex.matches(filename) }
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
|
@ -93,7 +104,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
|
||||||
throw IllegalStateException("Failed to get handle on file")
|
throw IllegalStateException("Failed to get handle on file")
|
||||||
}
|
}
|
||||||
|
|
||||||
val byteArray = parser.encodeToByteArray(BackupSerializer, backup!!)
|
val byteArray = parser.encodeToByteArray(BackupSerializer, backup)
|
||||||
if (byteArray.isEmpty()) {
|
if (byteArray.isEmpty()) {
|
||||||
throw IllegalStateException(context.getString(R.string.empty_backup_error))
|
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> {
|
private suspend fun backupCategories(options: Int): List<BackupCategory> {
|
||||||
// Check if user wants category information in backup
|
// Check if user wants category information in backup
|
||||||
return if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) {
|
return if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) {
|
||||||
handler.awaitList { categoriesQueries.getCategories(categoryMapper) }
|
getCategories.await()
|
||||||
.filterNot(Category::isSystemCategory)
|
.filterNot(Category::isSystemCategory)
|
||||||
.map(backupCategoryMapper)
|
.map(backupCategoryMapper)
|
||||||
} else {
|
} else {
|
||||||
|
@ -170,7 +181,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
|
||||||
// Check if user wants category information in backup
|
// Check if user wants category information in backup
|
||||||
if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) {
|
if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) {
|
||||||
// Backup categories for this manga
|
// Backup categories for this manga
|
||||||
val categoriesForManga = handler.awaitList { categoriesQueries.getCategoriesByMangaId(manga.id) }
|
val categoriesForManga = getCategories.await(manga.id)
|
||||||
if (categoriesForManga.isNotEmpty()) {
|
if (categoriesForManga.isNotEmpty()) {
|
||||||
mangaObject.categories = categoriesForManga.map { it.order }
|
mangaObject.categories = categoriesForManga.map { it.order }
|
||||||
}
|
}
|
||||||
|
@ -201,7 +212,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
|
||||||
return mangaObject
|
return mangaObject
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun restoreExistingManga(manga: Manga, dbManga: Mangas) {
|
internal suspend fun restoreExistingManga(manga: Manga, dbManga: Mangas) {
|
||||||
manga.id = dbManga._id
|
manga.id = dbManga._id
|
||||||
manga.copyFrom(dbManga)
|
manga.copyFrom(dbManga)
|
||||||
updateManga(manga)
|
updateManga(manga)
|
||||||
|
@ -213,7 +224,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
|
||||||
* @param manga manga that needs updating
|
* @param manga manga that needs updating
|
||||||
* @return Updated manga info.
|
* @return Updated manga info.
|
||||||
*/
|
*/
|
||||||
suspend fun restoreNewManga(manga: Manga): Manga {
|
internal suspend fun restoreNewManga(manga: Manga): Manga {
|
||||||
return manga.also {
|
return manga.also {
|
||||||
it.initialized = it.description != null
|
it.initialized = it.description != null
|
||||||
it.id = insertManga(it)
|
it.id = insertManga(it)
|
||||||
|
@ -227,7 +238,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
|
||||||
*/
|
*/
|
||||||
internal suspend fun restoreCategories(backupCategories: List<BackupCategory>) {
|
internal suspend fun restoreCategories(backupCategories: List<BackupCategory>) {
|
||||||
// Get categories from file and from db
|
// Get categories from file and from db
|
||||||
val dbCategories = handler.awaitList { categoriesQueries.getCategories(categoryMapper) }
|
val dbCategories = getCategories.await()
|
||||||
|
|
||||||
val categories = backupCategories.map {
|
val categories = backupCategories.map {
|
||||||
var category = it.getCategory()
|
var category = it.getCategory()
|
||||||
|
@ -267,7 +278,7 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
|
||||||
* @param categories the categories to restore.
|
* @param categories the categories to restore.
|
||||||
*/
|
*/
|
||||||
internal suspend fun restoreCategories(manga: Manga, categories: List<Int>, backupCategories: List<BackupCategory>) {
|
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>>()
|
val mangaCategoriesToUpdate = mutableListOf<Pair<Long, Long>>()
|
||||||
|
|
||||||
categories.forEach { backupCategoryOrder ->
|
categories.forEach { backupCategoryOrder ->
|
||||||
|
@ -353,7 +364,6 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
|
||||||
tracks.map { it.manga_id = manga.id!! }
|
tracks.map { it.manga_id = manga.id!! }
|
||||||
|
|
||||||
// Get tracks from database
|
// Get tracks from database
|
||||||
|
|
||||||
val dbTracks = handler.awaitList { manga_syncQueries.getTracksByMangaId(manga.id!!) }
|
val dbTracks = handler.awaitList { manga_syncQueries.getTracksByMangaId(manga.id!!) }
|
||||||
val toUpdate = mutableListOf<Manga_sync>()
|
val toUpdate = mutableListOf<Manga_sync>()
|
||||||
val toInsert = mutableListOf<Track>()
|
val toInsert = mutableListOf<Track>()
|
||||||
|
@ -452,4 +462,139 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
|
||||||
newChapters[true]?.let { updateKnownChapters(it) }
|
newChapters[true]?.let { updateKnownChapters(it) }
|
||||||
newChapters[false]?.let { insertChapters(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 wakeLock: PowerManager.WakeLock
|
||||||
|
|
||||||
private lateinit var ioScope: CoroutineScope
|
private lateinit var ioScope: CoroutineScope
|
||||||
private var restorer: AbstractBackupRestore<*>? = null
|
private var restorer: BackupRestorer? = null
|
||||||
private lateinit var notifier: BackupNotifier
|
private lateinit var notifier: BackupNotifier
|
||||||
|
|
||||||
override fun onCreate() {
|
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.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.database.models.Track
|
import eu.kanade.tachiyomi.data.database.models.Track
|
||||||
|
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.gzip
|
import okio.gzip
|
||||||
import okio.source
|
import okio.source
|
||||||
|
import java.io.File
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
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")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
override suspend fun performRestore(uri: Uri): Boolean {
|
private suspend fun performRestore(uri: Uri): Boolean {
|
||||||
backupManager = BackupManager(context)
|
|
||||||
|
|
||||||
val backupString = context.contentResolver.openInputStream(uri)!!.source().gzip().buffer().use { it.readByteArray() }
|
val backupString = context.contentResolver.openInputStream(uri)!!.source().gzip().buffer().use { it.readByteArray() }
|
||||||
val backup = backupManager.parser.decodeFromByteArray(BackupSerializer, backupString)
|
val backup = backupManager.parser.decodeFromByteArray(BackupSerializer, backupString)
|
||||||
|
|
||||||
|
@ -125,4 +182,15 @@ class BackupRestorer(context: Context, notifier: BackupNotifier) : AbstractBacku
|
||||||
backupManager.restoreHistory(history)
|
backupManager.restoreHistory(history)
|
||||||
backupManager.restoreTracking(manga, tracks)
|
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,
|
private val sourceId: Long,
|
||||||
searchQuery: String? = null,
|
searchQuery: String? = null,
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
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 coverCache: CoverCache = Injekt.get(),
|
||||||
private val getManga: GetManga = Injekt.get(),
|
private val getManga: GetManga = Injekt.get(),
|
||||||
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
|
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
|
||||||
|
@ -153,7 +153,7 @@ open class BrowseSourcePresenter(
|
||||||
pager = createPager(query, filters)
|
pager = createPager(query, filters)
|
||||||
|
|
||||||
val sourceId = source.id
|
val sourceId = source.id
|
||||||
val sourceDisplayMode = prefs.sourceDisplayMode()
|
val sourceDisplayMode = preferences.sourceDisplayMode()
|
||||||
|
|
||||||
pagerJob?.cancel()
|
pagerJob?.cancel()
|
||||||
pagerJob = presenterScope.launchIO {
|
pagerJob = presenterScope.launchIO {
|
||||||
|
|
|
@ -18,11 +18,11 @@ import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class MorePresenter(
|
class MorePresenter(
|
||||||
private val downloadManager: DownloadManager = Injekt.get(),
|
private val downloadManager: DownloadManager = Injekt.get(),
|
||||||
preferencesHelper: PreferencesHelper = Injekt.get(),
|
preferences: PreferencesHelper = Injekt.get(),
|
||||||
) : BasePresenter<MoreController>() {
|
) : BasePresenter<MoreController>() {
|
||||||
|
|
||||||
val downloadedOnly = preferencesHelper.downloadedOnly().asState()
|
val downloadedOnly = preferences.downloadedOnly().asState()
|
||||||
val incognitoMode = preferencesHelper.incognitoMode().asState()
|
val incognitoMode = preferences.incognitoMode().asState()
|
||||||
|
|
||||||
private var _state: MutableStateFlow<DownloadQueueState> = MutableStateFlow(DownloadQueueState.Stopped)
|
private var _state: MutableStateFlow<DownloadQueueState> = MutableStateFlow(DownloadQueueState.Stopped)
|
||||||
val downloadQueueState: StateFlow<DownloadQueueState> = _state.asStateFlow()
|
val downloadQueueState: StateFlow<DownloadQueueState> = _state.asStateFlow()
|
||||||
|
|
|
@ -50,17 +50,17 @@ fun Manga.removeCovers(coverCache: CoverCache = Injekt.get()): Int {
|
||||||
return coverCache.deleteFromCache(this, true)
|
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
|
if (!favorite) return false
|
||||||
|
|
||||||
val categories = dbCategories.ifEmpty { listOf(0L) }
|
val categories = dbCategories.ifEmpty { listOf(0L) }
|
||||||
|
|
||||||
// Boolean to determine if user wants to automatically download new chapters.
|
// 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
|
if (!downloadNewChapter) return false
|
||||||
|
|
||||||
val includedCategories = prefs.downloadNewChapterCategories().get().map { it.toLong() }
|
val includedCategories = preferences.downloadNewChapterCategories().get().map { it.toLong() }
|
||||||
val excludedCategories = prefs.downloadNewChapterCategoriesExclude().get().map { it.toLong() }
|
val excludedCategories = preferences.downloadNewChapterCategoriesExclude().get().map { it.toLong() }
|
||||||
|
|
||||||
// Default: Download from all categories
|
// Default: Download from all categories
|
||||||
if (includedCategories.isEmpty() && excludedCategories.isEmpty()) return true
|
if (includedCategories.isEmpty() && excludedCategories.isEmpty()) return true
|
||||||
|
|
|
@ -10,7 +10,7 @@ import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
object ChapterSettingsHelper {
|
object ChapterSettingsHelper {
|
||||||
|
|
||||||
private val prefs: PreferencesHelper by injectLazy()
|
private val preferences: PreferencesHelper by injectLazy()
|
||||||
private val getFavorites: GetFavorites by injectLazy()
|
private val getFavorites: GetFavorites by injectLazy()
|
||||||
private val setMangaChapterFlags: SetMangaChapterFlags by injectLazy()
|
private val setMangaChapterFlags: SetMangaChapterFlags by injectLazy()
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ object ChapterSettingsHelper {
|
||||||
* Updates the global Chapter Settings in Preferences.
|
* Updates the global Chapter Settings in Preferences.
|
||||||
*/
|
*/
|
||||||
fun setGlobalSettings(manga: Manga) {
|
fun setGlobalSettings(manga: Manga) {
|
||||||
prefs.setChapterSettingsDefault(manga.toDbManga())
|
preferences.setChapterSettingsDefault(manga.toDbManga())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,12 +28,12 @@ object ChapterSettingsHelper {
|
||||||
launchIO {
|
launchIO {
|
||||||
setMangaChapterFlags.awaitSetAllFlags(
|
setMangaChapterFlags.awaitSetAllFlags(
|
||||||
mangaId = manga.id,
|
mangaId = manga.id,
|
||||||
unreadFilter = prefs.filterChapterByRead().toLong(),
|
unreadFilter = preferences.filterChapterByRead().toLong(),
|
||||||
downloadedFilter = prefs.filterChapterByDownloaded().toLong(),
|
downloadedFilter = preferences.filterChapterByDownloaded().toLong(),
|
||||||
bookmarkedFilter = prefs.filterChapterByBookmarked().toLong(),
|
bookmarkedFilter = preferences.filterChapterByBookmarked().toLong(),
|
||||||
sortingMode = prefs.sortChapterBySourceOrNumber().toLong(),
|
sortingMode = preferences.sortChapterBySourceOrNumber().toLong(),
|
||||||
sortingDirection = prefs.sortChapterByAscendingOrDescending().toLong(),
|
sortingDirection = preferences.sortChapterByAscendingOrDescending().toLong(),
|
||||||
displayMode = prefs.displayChapterByNameOrNumber().toLong(),
|
displayMode = preferences.displayChapterByNameOrNumber().toLong(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,12 +41,12 @@ object ChapterSettingsHelper {
|
||||||
suspend fun applySettingDefaults(mangaId: Long) {
|
suspend fun applySettingDefaults(mangaId: Long) {
|
||||||
setMangaChapterFlags.awaitSetAllFlags(
|
setMangaChapterFlags.awaitSetAllFlags(
|
||||||
mangaId = mangaId,
|
mangaId = mangaId,
|
||||||
unreadFilter = prefs.filterChapterByRead().toLong(),
|
unreadFilter = preferences.filterChapterByRead().toLong(),
|
||||||
downloadedFilter = prefs.filterChapterByDownloaded().toLong(),
|
downloadedFilter = preferences.filterChapterByDownloaded().toLong(),
|
||||||
bookmarkedFilter = prefs.filterChapterByBookmarked().toLong(),
|
bookmarkedFilter = preferences.filterChapterByBookmarked().toLong(),
|
||||||
sortingMode = prefs.sortChapterBySourceOrNumber().toLong(),
|
sortingMode = preferences.sortChapterBySourceOrNumber().toLong(),
|
||||||
sortingDirection = prefs.sortChapterByAscendingOrDescending().toLong(),
|
sortingDirection = preferences.sortChapterByAscendingOrDescending().toLong(),
|
||||||
displayMode = prefs.displayChapterByNameOrNumber().toLong(),
|
displayMode = preferences.displayChapterByNameOrNumber().toLong(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,12 +59,12 @@ object ChapterSettingsHelper {
|
||||||
.map { manga ->
|
.map { manga ->
|
||||||
setMangaChapterFlags.awaitSetAllFlags(
|
setMangaChapterFlags.awaitSetAllFlags(
|
||||||
mangaId = manga.id,
|
mangaId = manga.id,
|
||||||
unreadFilter = prefs.filterChapterByRead().toLong(),
|
unreadFilter = preferences.filterChapterByRead().toLong(),
|
||||||
downloadedFilter = prefs.filterChapterByDownloaded().toLong(),
|
downloadedFilter = preferences.filterChapterByDownloaded().toLong(),
|
||||||
bookmarkedFilter = prefs.filterChapterByBookmarked().toLong(),
|
bookmarkedFilter = preferences.filterChapterByBookmarked().toLong(),
|
||||||
sortingMode = prefs.sortChapterBySourceOrNumber().toLong(),
|
sortingMode = preferences.sortChapterBySourceOrNumber().toLong(),
|
||||||
sortingDirection = prefs.sortChapterByAscendingOrDescending().toLong(),
|
sortingDirection = preferences.sortChapterByAscendingOrDescending().toLong(),
|
||||||
displayMode = prefs.displayChapterByNameOrNumber().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
|
* 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 {
|
fun Context.createReaderThemeContext(): Context {
|
||||||
val prefs = Injekt.get<PreferencesHelper>()
|
val preferences = Injekt.get<PreferencesHelper>()
|
||||||
val isDarkBackground = when (prefs.readerTheme().get()) {
|
val isDarkBackground = when (preferences.readerTheme().get()) {
|
||||||
1, 2 -> true // Black, Gray
|
1, 2 -> true // Black, Gray
|
||||||
3 -> applicationContext.isNightMode() // Automatic bg uses activity background by default
|
3 -> applicationContext.isNightMode() // Automatic bg uses activity background by default
|
||||||
else -> false // White
|
else -> false // White
|
||||||
|
@ -333,7 +333,7 @@ fun Context.createReaderThemeContext(): Context {
|
||||||
|
|
||||||
val wrappedContext = ContextThemeWrapper(this, R.style.Theme_Tachiyomi)
|
val wrappedContext = ContextThemeWrapper(this, R.style.Theme_Tachiyomi)
|
||||||
wrappedContext.applyOverrideConfiguration(overrideConf)
|
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) }
|
.forEach { wrappedContext.theme.applyStyle(it, true) }
|
||||||
return wrappedContext
|
return wrappedContext
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue