More backup/restore code cleanup

This commit is contained in:
arkon 2022-08-06 15:27:25 -04:00
parent 19eb4aaac9
commit 9f0052eceb
10 changed files with 270 additions and 415 deletions

View file

@ -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()
}

View file

@ -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("")
}
}

View 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!!,
)
}
}
}
}

View file

@ -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() {

View file

@ -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)
}
}

View file

@ -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 {

View file

@ -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()

View file

@ -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

View file

@ -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(),
)
}
}

View file

@ -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
}