Some more code cleanup

This commit is contained in:
arkon 2020-11-21 15:30:04 -05:00
parent eed6db8e92
commit cf6c48744a
7 changed files with 115 additions and 190 deletions

View File

@ -7,10 +7,14 @@ 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.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import rx.Observable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
abstract class AbstractBackupManager(protected val context: Context) { abstract class AbstractBackupManager(protected val context: Context) {
internal val databaseHelper: DatabaseHelper by injectLazy() internal val databaseHelper: DatabaseHelper by injectLazy()
internal val sourceManager: SourceManager by injectLazy() internal val sourceManager: SourceManager by injectLazy()
internal val trackManager: TrackManager by injectLazy() internal val trackManager: TrackManager by injectLazy()
@ -26,6 +30,25 @@ abstract class AbstractBackupManager(protected val context: Context) {
internal fun getMangaFromDatabase(manga: Manga): Manga? = internal fun getMangaFromDatabase(manga: Manga): Manga? =
databaseHelper.getManga(manga.url, manga.source).executeAsBlocking() databaseHelper.getManga(manga.url, manga.source).executeAsBlocking()
/**
* [Observable] that fetches chapter information
*
* @param source source of manga
* @param manga manga that needs updating
* @param chapters list of chapters in the backup
* @return [Observable] that contains manga
*/
internal fun restoreChapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>): Observable<Pair<List<Chapter>, List<Chapter>>> {
return source.fetchChapterList(manga)
.map { syncChaptersWithSource(databaseHelper, it, manga, source) }
.doOnNext { pair ->
if (pair.first.isNotEmpty()) {
chapters.forEach { it.manga_id = manga.id }
updateChapters(chapters)
}
}
}
/** /**
* Returns list containing manga from library * Returns list containing manga from library
* *

View File

@ -2,20 +2,29 @@ package eu.kanade.tachiyomi.data.backup
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
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.data.track.TrackManager
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.chapter.NoChaptersException
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import rx.Observable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
abstract class AbstractBackupRestore(protected val context: Context, protected val notifier: BackupNotifier) { abstract class AbstractBackupRestore<T : AbstractBackupManager>(protected val context: Context, protected val notifier: BackupNotifier) {
protected val db: DatabaseHelper by injectLazy()
protected val db: DatabaseHelper by injectLazy()
protected val trackManager: TrackManager by injectLazy() protected val trackManager: TrackManager by injectLazy()
protected lateinit var backupManager: T
var job: Job? = null var job: Job? = null
/** /**
@ -43,7 +52,7 @@ abstract class AbstractBackupRestore(protected val context: Context, protected v
/** /**
* Write errors to error log * Write errors to error log
*/ */
fun writeErrorLog(): File { internal fun writeErrorLog(): File {
try { try {
if (errors.isNotEmpty()) { if (errors.isNotEmpty()) {
val destFile = File(context.externalCacheDir, "tachiyomi_restore.txt") val destFile = File(context.externalCacheDir, "tachiyomi_restore.txt")
@ -61,4 +70,64 @@ abstract class AbstractBackupRestore(protected val context: Context, protected v
} }
return File("") return File("")
} }
/**
* [Observable] that fetches chapter information
*
* @param source source of manga
* @param manga manga that needs updating
* @return [Observable] that contains manga
*/
internal fun chapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>): Observable<Pair<List<Chapter>, List<Chapter>>> {
return backupManager.restoreChapterFetchObservable(source, manga, chapters)
// If there's any error, return empty update and continue.
.onErrorReturn {
val errorMessage = if (it is NoChaptersException) {
context.getString(R.string.no_chapters_error)
} else {
it.message
}
errors.add(Date() to "${manga.title} - $errorMessage")
Pair(emptyList(), emptyList())
}
}
/**
* [Observable] that refreshes tracking information
* @param manga manga that needs updating.
* @param tracks list containing tracks from restore file.
* @return [Observable] that contains updated track item
*/
internal fun trackingFetchObservable(manga: Manga, tracks: List<Track>): Observable<Track> {
return Observable.from(tracks)
.flatMap { track ->
val service = trackManager.getService(track.sync_id)
if (service != null && service.isLogged) {
service.refresh(track)
.doOnNext { db.insertTrack(it).executeAsBlocking() }
.onErrorReturn {
errors.add(Date() to "${manga.title} - ${it.message}")
track
}
} else {
errors.add(Date() to "${manga.title} - ${context.getString(R.string.tracker_not_logged_in, service?.name)}")
Observable.empty()
}
}
}
/**
* 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)
}
} }

View File

@ -68,7 +68,7 @@ class BackupRestoreService : Service() {
*/ */
private lateinit var wakeLock: PowerManager.WakeLock private lateinit var wakeLock: PowerManager.WakeLock
private var backupRestore: AbstractBackupRestore? = null private var backupRestore: AbstractBackupRestore<*>? = null
private lateinit var notifier: BackupNotifier private lateinit var notifier: BackupNotifier
override fun onCreate() { override fun onCreate() {

View File

@ -27,7 +27,6 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import okio.buffer import okio.buffer
@ -209,25 +208,6 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
} }
} }
/**
* [Observable] that fetches chapter information
*
* @param source source of manga
* @param manga manga that needs updating
* @param chapters list of chapters in the backup
* @return [Observable] that contains manga
*/
fun restoreChapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>): Observable<Pair<List<Chapter>, List<Chapter>>> {
return source.fetchChapterList(manga)
.map { syncChaptersWithSource(databaseHelper, it, manga, source) }
.doOnNext { pair ->
if (pair.first.isNotEmpty()) {
chapters.forEach { it.manga_id = manga.id }
updateChapters(chapters)
}
}
}
/** /**
* Restore the categories from Json * Restore the categories from Json
* *

View File

@ -13,7 +13,6 @@ 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.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.chapter.NoChaptersException
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import okio.buffer import okio.buffer
import okio.gzip import okio.gzip
@ -22,8 +21,7 @@ import rx.Observable
import java.util.Date import java.util.Date
@OptIn(ExperimentalSerializationApi::class) @OptIn(ExperimentalSerializationApi::class)
class FullBackupRestore(context: Context, notifier: BackupNotifier, private val online: Boolean) : AbstractBackupRestore(context, notifier) { class FullBackupRestore(context: Context, notifier: BackupNotifier, private val online: Boolean) : AbstractBackupRestore<FullBackupManager>(context, notifier) {
private lateinit var fullBackupManager: FullBackupManager
/** /**
* Restores data from backup file. * Restores data from backup file.
@ -34,10 +32,10 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()
// Initialize manager // Initialize manager
fullBackupManager = FullBackupManager(context) backupManager = FullBackupManager(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 = fullBackupManager.parser.decodeFromByteArray(BackupSerializer, backupString) val backup = backupManager.parser.decodeFromByteArray(BackupSerializer, backupString)
restoreAmount = backup.backupManga.size + 1 // +1 for categories restoreAmount = backup.backupManga.size + 1 // +1 for categories
restoreProgress = 0 restoreProgress = 0
@ -71,7 +69,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
private fun restoreCategories(backupCategories: List<BackupCategory>) { private fun restoreCategories(backupCategories: List<BackupCategory>) {
db.inTransaction { db.inTransaction {
fullBackupManager.restoreCategories(backupCategories) backupManager.restoreCategories(backupCategories)
} }
restoreProgress += 1 restoreProgress += 1
@ -86,7 +84,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
val tracks = backupManga.getTrackingImpl() val tracks = backupManga.getTrackingImpl()
try { try {
val source = fullBackupManager.sourceManager.get(manga.source) val source = backupManager.sourceManager.get(manga.source)
if (source != null || !online) { if (source != null || !online) {
restoreMangaData(manga, source, chapters, categories, history, tracks, backupCategories, online) restoreMangaData(manga, source, chapters, categories, history, tracks, backupCategories, online)
} else { } else {
@ -121,7 +119,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
online: Boolean online: Boolean
) { ) {
val dbManga = fullBackupManager.getMangaFromDatabase(manga) val dbManga = backupManager.getMangaFromDatabase(manga)
db.inTransaction { db.inTransaction {
if (dbManga == null) { if (dbManga == null) {
@ -129,7 +127,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
restoreMangaFetch(source, manga, chapters, categories, history, tracks, backupCategories, online) restoreMangaFetch(source, manga, chapters, categories, history, tracks, backupCategories, online)
} else { // Manga in database } else { // Manga in database
// Copy information from manga already in database // Copy information from manga already in database
fullBackupManager.restoreMangaNoFetch(manga, dbManga) backupManager.restoreMangaNoFetch(manga, dbManga)
// Fetch rest of manga information // Fetch rest of manga information
restoreMangaNoFetch(source, manga, chapters, categories, history, tracks, backupCategories, online) restoreMangaNoFetch(source, manga, chapters, categories, history, tracks, backupCategories, online)
} }
@ -153,7 +151,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
online: Boolean online: Boolean
) { ) {
fullBackupManager.restoreMangaFetchObservable(source, manga, online) backupManager.restoreMangaFetchObservable(source, manga, online)
.doOnError { .doOnError {
errors.add(Date() to "${manga.title} - ${it.message}") errors.add(Date() to "${manga.title} - ${it.message}")
} }
@ -164,7 +162,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
// Convert to the manga that contains new chapters. // Convert to the manga that contains new chapters.
.map { manga } .map { manga }
} else { } else {
fullBackupManager.restoreChaptersForMangaOffline(it, chapters) backupManager.restoreChaptersForMangaOffline(it, chapters)
Observable.just(manga) Observable.just(manga)
} }
} }
@ -190,14 +188,14 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
Observable.just(backupManga) Observable.just(backupManga)
.flatMap { manga -> .flatMap { manga ->
if (online && source != null) { if (online && source != null) {
if (!fullBackupManager.restoreChaptersForManga(manga, chapters)) { if (!backupManager.restoreChaptersForManga(manga, chapters)) {
chapterFetchObservable(source, manga, chapters) chapterFetchObservable(source, manga, chapters)
.map { manga } .map { manga }
} else { } else {
Observable.just(manga) Observable.just(manga)
} }
} else { } else {
fullBackupManager.restoreChaptersForMangaOffline(manga, chapters) backupManager.restoreChaptersForMangaOffline(manga, chapters)
Observable.just(manga) Observable.just(manga)
} }
} }
@ -212,72 +210,12 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
private fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>) { private fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>) {
// Restore categories // Restore categories
fullBackupManager.restoreCategoriesForManga(manga, categories, backupCategories) backupManager.restoreCategoriesForManga(manga, categories, backupCategories)
// Restore history // Restore history
fullBackupManager.restoreHistoryForManga(history) backupManager.restoreHistoryForManga(history)
// Restore tracking // Restore tracking
fullBackupManager.restoreTrackForManga(manga, tracks) backupManager.restoreTrackForManga(manga, tracks)
}
/**
* [Observable] that fetches chapter information
*
* @param source source of manga
* @param manga manga that needs updating
* @return [Observable] that contains manga
*/
private fun chapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>): Observable<Pair<List<Chapter>, List<Chapter>>> {
return fullBackupManager.restoreChapterFetchObservable(source, manga, chapters)
// If there's any error, return empty update and continue.
.onErrorReturn {
val errorMessage = if (it is NoChaptersException) {
context.getString(R.string.no_chapters_error)
} else {
it.message
}
errors.add(Date() to "${manga.title} - $errorMessage")
Pair(emptyList(), emptyList())
}
}
/**
* [Observable] that refreshes tracking information
* @param manga manga that needs updating.
* @param tracks list containing tracks from restore file.
* @return [Observable] that contains updated track item
*/
private fun trackingFetchObservable(manga: Manga, tracks: List<Track>): Observable<Track> {
return Observable.from(tracks)
.flatMap { track ->
val service = trackManager.getService(track.sync_id)
if (service != null && service.isLogged) {
service.refresh(track)
.doOnNext { db.insertTrack(it).executeAsBlocking() }
.onErrorReturn {
errors.add(Date() to "${manga.title} - ${it.message}")
track
}
} else {
errors.add(Date() to "${manga.title} - ${context.getString(R.string.tracker_not_logged_in, service?.name)}")
Observable.empty()
}
}
}
/**
* 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

@ -46,16 +46,13 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.TrackImpl import eu.kanade.tachiyomi.data.database.models.TrackImpl
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import rx.Observable import rx.Observable
import timber.log.Timber import timber.log.Timber
import kotlin.math.max import kotlin.math.max
class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : AbstractBackupManager(context) { class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : AbstractBackupManager(context) {
/**
* Version of parser var parserVersion: Int = version
*/
var version: Int = version
private set private set
var parser: Gson = initParser() var parser: Gson = initParser()
@ -66,11 +63,11 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
* @param version version of parser * @param version version of parser
*/ */
internal fun setVersion(version: Int) { internal fun setVersion(version: Int) {
this.version = version this.parserVersion = version
parser = initParser() parser = initParser()
} }
private fun initParser(): Gson = when (version) { private fun initParser(): Gson = when (parserVersion) {
2 -> 2 ->
GsonBuilder() GsonBuilder()
.registerTypeAdapter<MangaImpl>(MangaTypeAdapter.build()) .registerTypeAdapter<MangaImpl>(MangaTypeAdapter.build())
@ -269,24 +266,6 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
} }
} }
/**
* [Observable] that fetches chapter information
*
* @param source source of manga
* @param manga manga that needs updating
* @return [Observable] that contains manga
*/
fun restoreChapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>): Observable<Pair<List<Chapter>, List<Chapter>>> {
return source.fetchChapterList(manga)
.map { syncChaptersWithSource(databaseHelper, it, manga, source) }
.doOnNext { pair ->
if (pair.first.isNotEmpty()) {
chapters.forEach { it.manga_id = manga.id }
updateChapters(chapters)
}
}
}
/** /**
* Restore the categories from Json * Restore the categories from Json
* *

View File

@ -10,7 +10,6 @@ import com.google.gson.JsonParser
import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonReader
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.AbstractBackupRestore import eu.kanade.tachiyomi.data.backup.AbstractBackupRestore
import eu.kanade.tachiyomi.data.backup.BackupConst
import eu.kanade.tachiyomi.data.backup.BackupNotifier import eu.kanade.tachiyomi.data.backup.BackupNotifier
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup import eu.kanade.tachiyomi.data.backup.legacy.models.Backup
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.MANGAS import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.MANGAS
@ -22,13 +21,10 @@ import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.TrackImpl import eu.kanade.tachiyomi.data.database.models.TrackImpl
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.chapter.NoChaptersException
import rx.Observable import rx.Observable
import java.util.Date import java.util.Date
class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore(context, notifier) { class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore<LegacyBackupManager>(context, notifier) {
private lateinit var backupManager: LegacyBackupManager
/** /**
* Restores data from backup file. * Restores data from backup file.
@ -229,64 +225,4 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
// Restore tracking // Restore tracking
backupManager.restoreTrackForManga(manga, tracks) backupManager.restoreTrackForManga(manga, tracks)
} }
/**
* [Observable] that fetches chapter information
*
* @param source source of manga
* @param manga manga that needs updating
* @return [Observable] that contains manga
*/
private fun chapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>): Observable<Pair<List<Chapter>, List<Chapter>>> {
return backupManager.restoreChapterFetchObservable(source, manga, chapters)
// If there's any error, return empty update and continue.
.onErrorReturn {
val errorMessage = if (it is NoChaptersException) {
context.getString(R.string.no_chapters_error)
} else {
it.message
}
errors.add(Date() to "${manga.title} - $errorMessage")
Pair(emptyList(), emptyList())
}
}
/**
* [Observable] that refreshes tracking information
* @param manga manga that needs updating.
* @param tracks list containing tracks from restore file.
* @return [Observable] that contains updated track item
*/
private fun trackingFetchObservable(manga: Manga, tracks: List<Track>): Observable<Track> {
return Observable.from(tracks)
.flatMap { track ->
val service = trackManager.getService(track.sync_id)
if (service != null && service.isLogged) {
service.refresh(track)
.doOnNext { db.insertTrack(it).executeAsBlocking() }
.onErrorReturn {
errors.add(Date() to "${manga.title} - ${it.message}")
track
}
} else {
errors.add(Date() to "${manga.title} - ${context.getString(R.string.tracker_not_logged_in, service?.name)}")
Observable.empty()
}
}
}
/**
* 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)
}
} }