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:
Andreas
2022-04-21 21:45:56 +02:00
committed by GitHub
parent 6c1565a7d4
commit b1f46ed830
62 changed files with 1069 additions and 570 deletions

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

View File

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

View File

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

View File

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