Migrate History screen database calls to SQLDelight (#6933)
* Migrate History screen database call to SQLDelight - Move all migrations to SQLDelight - Move all tables to SQLDelight Co-authored-by: inorichi <3521738+inorichi@users.noreply.github.com> * Changes from review comments * Add adapters to database * Remove logging of database version in App * Change query name for paging source queries * Update migrations * Make SQLite Callback handle migration - To ensure it updates the database * Use SQLDelight Schema version for Callback database version Co-authored-by: inorichi <3521738+inorichi@users.noreply.github.com>
This commit is contained in:
26
app/src/main/java/eu/kanade/data/history/HistoryMapper.kt
Normal file
26
app/src/main/java/eu/kanade/data/history/HistoryMapper.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
package eu.kanade.data.history
|
||||
|
||||
import eu.kanade.domain.history.model.History
|
||||
import eu.kanade.domain.history.model.HistoryWithRelations
|
||||
import java.util.*
|
||||
|
||||
val historyMapper: (Long, Long, Date?, Date?) -> History = { id, chapterId, readAt, _ ->
|
||||
History(
|
||||
id = id,
|
||||
chapterId = chapterId,
|
||||
readAt = readAt,
|
||||
)
|
||||
}
|
||||
|
||||
val historyWithRelationsMapper: (Long, Long, Long, String, String?, Float, Date?) -> HistoryWithRelations = {
|
||||
historyId, mangaId, chapterId, title, thumbnailUrl, chapterNumber, readAt ->
|
||||
HistoryWithRelations(
|
||||
id = historyId,
|
||||
chapterId = chapterId,
|
||||
mangaId = mangaId,
|
||||
title = title,
|
||||
thumbnailUrl = thumbnailUrl ?: "",
|
||||
chapterNumber = chapterNumber,
|
||||
readAt = readAt
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package eu.kanade.data.history
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import eu.kanade.data.DatabaseHandler
|
||||
import eu.kanade.data.chapter.chapterMapper
|
||||
import eu.kanade.data.manga.mangaMapper
|
||||
import eu.kanade.domain.chapter.model.Chapter
|
||||
import eu.kanade.domain.history.model.HistoryWithRelations
|
||||
import eu.kanade.domain.history.repository.HistoryRepository
|
||||
import eu.kanade.domain.manga.model.Manga
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
|
||||
class HistoryRepositoryImpl(
|
||||
private val handler: DatabaseHandler
|
||||
) : HistoryRepository {
|
||||
|
||||
override fun getHistory(query: String): PagingSource<Long, HistoryWithRelations> {
|
||||
return handler.subscribeToPagingSource(
|
||||
countQuery = { historyViewQueries.countHistory(query) },
|
||||
transacter = { historyViewQueries },
|
||||
queryProvider = { limit, offset ->
|
||||
historyViewQueries.history(query, limit, offset, historyWithRelationsMapper)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getNextChapterForManga(mangaId: Long, chapterId: Long): Chapter? {
|
||||
val chapter = handler.awaitOne { chaptersQueries.getChapterById(chapterId, chapterMapper) }
|
||||
val manga = handler.awaitOne { mangasQueries.getMangaById(mangaId, mangaMapper) }
|
||||
|
||||
if (!chapter.read) {
|
||||
return chapter
|
||||
}
|
||||
|
||||
val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) {
|
||||
Manga.CHAPTER_SORTING_SOURCE -> { c1, c2 -> c2.sourceOrder.compareTo(c1.sourceOrder) }
|
||||
Manga.CHAPTER_SORTING_NUMBER -> { c1, c2 -> c1.chapterNumber.compareTo(c2.chapterNumber) }
|
||||
Manga.CHAPTER_SORTING_UPLOAD_DATE -> { c1, c2 -> c1.dateUpload.compareTo(c2.dateUpload) }
|
||||
else -> throw NotImplementedError("Unknown sorting method")
|
||||
}
|
||||
|
||||
val chapters = handler.awaitList { chaptersQueries.getChapterByMangaId(mangaId, chapterMapper) }
|
||||
.sortedWith(sortFunction)
|
||||
|
||||
val currChapterIndex = chapters.indexOfFirst { chapter.id == it.id }
|
||||
return when (manga.sorting) {
|
||||
Manga.CHAPTER_SORTING_SOURCE -> chapters.getOrNull(currChapterIndex + 1)
|
||||
Manga.CHAPTER_SORTING_NUMBER -> {
|
||||
val chapterNumber = chapter.chapterNumber
|
||||
|
||||
((currChapterIndex + 1) until chapters.size)
|
||||
.map { chapters[it] }
|
||||
.firstOrNull {
|
||||
it.chapterNumber > chapterNumber &&
|
||||
it.chapterNumber <= chapterNumber + 1
|
||||
}
|
||||
}
|
||||
Manga.CHAPTER_SORTING_UPLOAD_DATE -> {
|
||||
chapters.drop(currChapterIndex + 1)
|
||||
.firstOrNull { it.dateUpload >= chapter.dateUpload }
|
||||
}
|
||||
else -> throw NotImplementedError("Unknown sorting method")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun resetHistory(historyId: Long) {
|
||||
try {
|
||||
handler.await { historyQueries.resetHistoryById(historyId) }
|
||||
} catch (e: Exception) {
|
||||
logcat(throwable = e)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun resetHistoryByMangaId(mangaId: Long) {
|
||||
try {
|
||||
handler.await { historyQueries.resetHistoryByMangaId(mangaId) }
|
||||
} catch (e: Exception) {
|
||||
logcat(throwable = e)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteAllHistory(): Boolean {
|
||||
return try {
|
||||
handler.await { historyQueries.removeAllHistory() }
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
logcat(throwable = e)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package eu.kanade.data.history.local
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import eu.kanade.domain.history.repository.HistoryRepository
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
|
||||
import logcat.logcat
|
||||
|
||||
class HistoryPagingSource(
|
||||
private val repository: HistoryRepository,
|
||||
private val query: String
|
||||
) : PagingSource<Int, MangaChapterHistory>() {
|
||||
|
||||
override fun getRefreshKey(state: PagingState<Int, MangaChapterHistory>): Int? {
|
||||
return state.anchorPosition?.let { anchorPosition ->
|
||||
val anchorPage = state.closestPageToPosition(anchorPosition)
|
||||
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult.Page<Int, MangaChapterHistory> {
|
||||
val nextPageNumber = params.key ?: 0
|
||||
logcat { "Loading page $nextPageNumber" }
|
||||
|
||||
val response = repository.getHistory(PAGE_SIZE, nextPageNumber, query)
|
||||
|
||||
val nextKey = if (response.size == 25) {
|
||||
nextPageNumber + 1
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return LoadResult.Page(
|
||||
data = response,
|
||||
prevKey = null,
|
||||
nextKey = nextKey
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PAGE_SIZE = 25
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package eu.kanade.data.history.repository
|
||||
|
||||
import eu.kanade.data.history.local.HistoryPagingSource
|
||||
import eu.kanade.domain.history.repository.HistoryRepository
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.History
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.tables.HistoryTable
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.withContext
|
||||
import rx.Subscription
|
||||
import rx.schedulers.Schedulers
|
||||
import java.util.*
|
||||
|
||||
class HistoryRepositoryImpl(
|
||||
private val db: DatabaseHelper
|
||||
) : HistoryRepository {
|
||||
|
||||
/**
|
||||
* Used to observe changes in the History table
|
||||
* as RxJava isn't supported in Paging 3
|
||||
*/
|
||||
private var subscription: Subscription? = null
|
||||
|
||||
/**
|
||||
* Paging Source for history table
|
||||
*/
|
||||
override fun getHistory(query: String): HistoryPagingSource {
|
||||
subscription?.unsubscribe()
|
||||
val pagingSource = HistoryPagingSource(this, query)
|
||||
subscription = db.db
|
||||
.observeChangesInTable(HistoryTable.TABLE)
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe {
|
||||
pagingSource.invalidate()
|
||||
}
|
||||
return pagingSource
|
||||
}
|
||||
|
||||
override suspend fun getHistory(limit: Int, page: Int, query: String) = coroutineScope {
|
||||
withContext(Dispatchers.IO) {
|
||||
// Set date limit for recent manga
|
||||
val calendar = Calendar.getInstance().apply {
|
||||
time = Date()
|
||||
add(Calendar.YEAR, -50)
|
||||
}
|
||||
|
||||
db.getRecentManga(calendar.time, limit, page * limit, query)
|
||||
.executeAsBlocking()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getNextChapterForManga(manga: Manga, chapter: Chapter): Chapter? = coroutineScope {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (!chapter.read) {
|
||||
return@withContext chapter
|
||||
}
|
||||
|
||||
val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) {
|
||||
Manga.CHAPTER_SORTING_SOURCE -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) }
|
||||
Manga.CHAPTER_SORTING_NUMBER -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) }
|
||||
Manga.CHAPTER_SORTING_UPLOAD_DATE -> { c1, c2 -> c1.date_upload.compareTo(c2.date_upload) }
|
||||
else -> throw NotImplementedError("Unknown sorting method")
|
||||
}
|
||||
|
||||
val chapters = db.getChapters(manga)
|
||||
.executeAsBlocking()
|
||||
.sortedWith { c1, c2 -> sortFunction(c1, c2) }
|
||||
|
||||
val currChapterIndex = chapters.indexOfFirst { chapter.id == it.id }
|
||||
return@withContext when (manga.sorting) {
|
||||
Manga.CHAPTER_SORTING_SOURCE -> chapters.getOrNull(currChapterIndex + 1)
|
||||
Manga.CHAPTER_SORTING_NUMBER -> {
|
||||
val chapterNumber = chapter.chapter_number
|
||||
|
||||
((currChapterIndex + 1) until chapters.size)
|
||||
.map { chapters[it] }
|
||||
.firstOrNull {
|
||||
it.chapter_number > chapterNumber &&
|
||||
it.chapter_number <= chapterNumber + 1
|
||||
}
|
||||
}
|
||||
Manga.CHAPTER_SORTING_UPLOAD_DATE -> {
|
||||
chapters.drop(currChapterIndex + 1)
|
||||
.firstOrNull { it.date_upload >= chapter.date_upload }
|
||||
}
|
||||
else -> throw NotImplementedError("Unknown sorting method")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun resetHistory(history: History): Boolean = coroutineScope {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
history.last_read = 0
|
||||
db.upsertHistoryLastRead(history)
|
||||
.executeAsBlocking()
|
||||
true
|
||||
} catch (e: Throwable) {
|
||||
logcat(throwable = e)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun resetHistoryByMangaId(mangaId: Long): Boolean = coroutineScope {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val history = db.getHistoryByMangaId(mangaId)
|
||||
.executeAsBlocking()
|
||||
history.forEach { it.last_read = 0 }
|
||||
db.upsertHistoryLastRead(history)
|
||||
.executeAsBlocking()
|
||||
true
|
||||
} catch (e: Throwable) {
|
||||
logcat(throwable = e)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteAllHistory(): Boolean = coroutineScope {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
db.dropHistoryTable()
|
||||
.executeAsBlocking()
|
||||
true
|
||||
} catch (e: Throwable) {
|
||||
logcat(throwable = e)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user