|
|
|
@ -4,19 +4,15 @@ import android.content.Context
|
|
|
|
import androidx.core.content.edit
|
|
|
|
import androidx.core.content.edit
|
|
|
|
import androidx.room.withTransaction
|
|
|
|
import androidx.room.withTransaction
|
|
|
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
|
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
|
|
|
|
import kotlinx.coroutines.flow.FlowCollector
|
|
|
|
import kotlinx.coroutines.flow.FlowCollector
|
|
|
|
import kotlinx.coroutines.runInterruptible
|
|
|
|
|
|
|
|
import kotlinx.coroutines.sync.Mutex
|
|
|
|
import kotlinx.coroutines.sync.Mutex
|
|
|
|
import kotlinx.coroutines.sync.withLock
|
|
|
|
import kotlinx.coroutines.sync.withLock
|
|
|
|
import org.koitharu.kotatsu.core.db.MangaDatabase
|
|
|
|
import org.koitharu.kotatsu.core.db.MangaDatabase
|
|
|
|
import org.koitharu.kotatsu.core.parser.MangaDataRepository
|
|
|
|
import org.koitharu.kotatsu.core.parser.MangaDataRepository
|
|
|
|
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
|
|
|
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
|
|
|
import org.koitharu.kotatsu.local.data.LocalMangaRepository
|
|
|
|
import org.koitharu.kotatsu.local.data.LocalMangaRepository
|
|
|
|
import org.koitharu.kotatsu.local.data.LocalStorageManager
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.local.data.input.LocalMangaInput
|
|
|
|
import org.koitharu.kotatsu.local.data.input.LocalMangaInput
|
|
|
|
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
|
|
|
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
|
|
|
import org.koitharu.kotatsu.parsers.model.MangaTag
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
|
|
|
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
|
|
|
import java.io.File
|
|
|
|
import java.io.File
|
|
|
|
import javax.inject.Inject
|
|
|
|
import javax.inject.Inject
|
|
|
|
@ -27,7 +23,6 @@ import javax.inject.Singleton
|
|
|
|
class LocalMangaIndex @Inject constructor(
|
|
|
|
class LocalMangaIndex @Inject constructor(
|
|
|
|
private val mangaDataRepository: MangaDataRepository,
|
|
|
|
private val mangaDataRepository: MangaDataRepository,
|
|
|
|
private val db: MangaDatabase,
|
|
|
|
private val db: MangaDatabase,
|
|
|
|
private val localStorageManager: LocalStorageManager,
|
|
|
|
|
|
|
|
@ApplicationContext context: Context,
|
|
|
|
@ApplicationContext context: Context,
|
|
|
|
private val localMangaRepositoryProvider: Provider<LocalMangaRepository>,
|
|
|
|
private val localMangaRepositoryProvider: Provider<LocalMangaRepository>,
|
|
|
|
) : FlowCollector<LocalManga?> {
|
|
|
|
) : FlowCollector<LocalManga?> {
|
|
|
|
@ -35,9 +30,9 @@ class LocalMangaIndex @Inject constructor(
|
|
|
|
private val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
|
|
|
private val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
|
|
|
private val mutex = Mutex()
|
|
|
|
private val mutex = Mutex()
|
|
|
|
|
|
|
|
|
|
|
|
private var previousHash: Long
|
|
|
|
private var currentVersion: Int
|
|
|
|
get() = prefs.getLong(KEY_HASH, 0L)
|
|
|
|
get() = prefs.getInt(KEY_VERSION, 0)
|
|
|
|
set(value) = prefs.edit { putLong(KEY_HASH, value) }
|
|
|
|
set(value) = prefs.edit { putInt(KEY_VERSION, value) }
|
|
|
|
|
|
|
|
|
|
|
|
override suspend fun emit(value: LocalManga?) {
|
|
|
|
override suspend fun emit(value: LocalManga?) {
|
|
|
|
if (value != null) {
|
|
|
|
if (value != null) {
|
|
|
|
@ -45,22 +40,25 @@ class LocalMangaIndex @Inject constructor(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
suspend fun update(): Boolean = mutex.withLock {
|
|
|
|
suspend fun update() = mutex.withLock {
|
|
|
|
val newHash = computeHash()
|
|
|
|
|
|
|
|
if (newHash == previousHash) {
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
db.withTransaction {
|
|
|
|
db.withTransaction {
|
|
|
|
val dao = db.getLocalMangaIndexDao()
|
|
|
|
val dao = db.getLocalMangaIndexDao()
|
|
|
|
dao.clear()
|
|
|
|
dao.clear()
|
|
|
|
localMangaRepositoryProvider.get().getRawListAsFlow()
|
|
|
|
localMangaRepositoryProvider.get()
|
|
|
|
.collect { dao.upsert(it.toEntity()) }
|
|
|
|
.getRawListAsFlow()
|
|
|
|
|
|
|
|
.collect { upsert(it) }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
currentVersion = VERSION
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
suspend fun updateIfRequired() {
|
|
|
|
|
|
|
|
if (isUpdateRequired()) {
|
|
|
|
|
|
|
|
update()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
previousHash = newHash
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
suspend fun get(mangaId: Long): LocalManga? {
|
|
|
|
suspend fun get(mangaId: Long): LocalManga? {
|
|
|
|
|
|
|
|
updateIfRequired()
|
|
|
|
var path = db.getLocalMangaIndexDao().findPath(mangaId)
|
|
|
|
var path = db.getLocalMangaIndexDao().findPath(mangaId)
|
|
|
|
if (path == null && mutex.isLocked) { // wait for updating complete
|
|
|
|
if (path == null && mutex.isLocked) { // wait for updating complete
|
|
|
|
path = mutex.withLock { db.getLocalMangaIndexDao().findPath(mangaId) }
|
|
|
|
path = mutex.withLock { db.getLocalMangaIndexDao().findPath(mangaId) }
|
|
|
|
@ -77,8 +75,7 @@ class LocalMangaIndex @Inject constructor(
|
|
|
|
|
|
|
|
|
|
|
|
suspend fun put(manga: LocalManga) = mutex.withLock {
|
|
|
|
suspend fun put(manga: LocalManga) = mutex.withLock {
|
|
|
|
db.withTransaction {
|
|
|
|
db.withTransaction {
|
|
|
|
mangaDataRepository.storeManga(manga.manga)
|
|
|
|
upsert(manga)
|
|
|
|
db.getLocalMangaIndexDao().upsert(manga.toEntity())
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -90,27 +87,22 @@ class LocalMangaIndex @Inject constructor(
|
|
|
|
return db.getLocalMangaIndexDao().findTags()
|
|
|
|
return db.getLocalMangaIndexDao().findTags()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private suspend fun upsert(manga: LocalManga) {
|
|
|
|
|
|
|
|
mangaDataRepository.storeManga(manga.manga)
|
|
|
|
|
|
|
|
db.getLocalMangaIndexDao().upsert(manga.toEntity())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun LocalManga.toEntity() = LocalMangaIndexEntity(
|
|
|
|
private fun LocalManga.toEntity() = LocalMangaIndexEntity(
|
|
|
|
mangaId = manga.id,
|
|
|
|
mangaId = manga.id,
|
|
|
|
path = file.path,
|
|
|
|
path = file.path,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
private suspend fun computeHash(): Long {
|
|
|
|
private fun isUpdateRequired() = currentVersion < VERSION
|
|
|
|
return runCatchingCancellable {
|
|
|
|
|
|
|
|
localStorageManager.getReadableDirs()
|
|
|
|
|
|
|
|
.fold(0L) { acc, file -> acc + file.computeHash() }
|
|
|
|
|
|
|
|
}.onFailure {
|
|
|
|
|
|
|
|
it.printStackTraceDebug()
|
|
|
|
|
|
|
|
}.getOrDefault(0L)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private suspend fun File.computeHash(): Long = runInterruptible(Dispatchers.IO) {
|
|
|
|
|
|
|
|
lastModified() // TODO size
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
companion object {
|
|
|
|
|
|
|
|
|
|
|
|
private const val PREF_NAME = "_local_index"
|
|
|
|
private const val PREF_NAME = "_local_index"
|
|
|
|
private const val KEY_HASH = "hash"
|
|
|
|
private const val KEY_VERSION = "ver"
|
|
|
|
|
|
|
|
private const val VERSION = 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|