Move SQLDelight to data module (#8954)

And use tachiyomi instead of eu.kanade.tachiyomi for package names in the module

(cherry picked from commit 823749fc1ed16dbf52d43839888a70de089d65b1)

# Conflicts:
#	app/build.gradle.kts
#	app/src/main/java/eu/kanade/data/history/HistoryRepositoryImpl.kt
#	app/src/main/java/eu/kanade/data/manga/MangaRepositoryImpl.kt
#	app/src/main/java/eu/kanade/data/updates/UpdatesRepositoryImpl.kt
#	app/src/main/java/eu/kanade/tachiyomi/App.kt
#	app/src/main/java/eu/kanade/tachiyomi/AppModule.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt
#	app/src/main/java/eu/kanade/tachiyomi/source/model/SMangaExtensions.kt
#	data/src/main/java/tachiyomi/data/AndroidDatabaseHandler.kt
#	data/src/main/sqldelight/tachiyomi/data/eh.sq
#	data/src/main/sqldelight/tachiyomi/data/eh_favorites.sq
#	data/src/main/sqldelight/tachiyomi/data/feed_saved_search.sq
#	data/src/main/sqldelight/tachiyomi/data/merged.sq
#	data/src/main/sqldelight/tachiyomi/data/saved_search.sq
#	data/src/main/sqldelight/tachiyomi/data/search_metadata.sq
#	data/src/main/sqldelight/tachiyomi/data/search_tags.sq
#	data/src/main/sqldelight/tachiyomi/data/search_titles.sq
#	data/src/main/sqldelight/tachiyomi/migrations/16.sqm
#	data/src/main/sqldelight/tachiyomi/migrations/24.sqm
This commit is contained in:
Andreas
2023-01-21 16:37:07 +01:00
committed by Jobobby04
parent 888d1f0983
commit 64d829d304
84 changed files with 206 additions and 119 deletions
@@ -1,98 +0,0 @@
package eu.kanade.data
import androidx.paging.PagingSource
import com.squareup.sqldelight.Query
import com.squareup.sqldelight.db.SqlDriver
import com.squareup.sqldelight.runtime.coroutines.asFlow
import com.squareup.sqldelight.runtime.coroutines.mapToList
import com.squareup.sqldelight.runtime.coroutines.mapToOne
import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull
import eu.kanade.data.manga.LibraryQuery
import eu.kanade.data.updates.UpdatesQuery
import eu.kanade.tachiyomi.Database
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
class AndroidDatabaseHandler(
val db: Database,
private val driver: SqlDriver,
val queryDispatcher: CoroutineDispatcher = Dispatchers.IO,
val transactionDispatcher: CoroutineDispatcher = queryDispatcher,
) : DatabaseHandler {
val suspendingTransactionId = ThreadLocal<Int>()
override suspend fun <T> await(inTransaction: Boolean, block: suspend Database.() -> T): T {
return dispatch(inTransaction, block)
}
override suspend fun <T : Any> awaitList(
inTransaction: Boolean,
block: suspend Database.() -> Query<T>,
): List<T> {
return dispatch(inTransaction) { block(db).executeAsList() }
}
override suspend fun <T : Any> awaitOne(
inTransaction: Boolean,
block: suspend Database.() -> Query<T>,
): T {
return dispatch(inTransaction) { block(db).executeAsOne() }
}
override suspend fun <T : Any> awaitOneOrNull(
inTransaction: Boolean,
block: suspend Database.() -> Query<T>,
): T? {
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
}
override fun <T : Any> subscribeToList(block: Database.() -> Query<T>): Flow<List<T>> {
return block(db).asFlow().mapToList(queryDispatcher)
}
override fun <T : Any> subscribeToOne(block: Database.() -> Query<T>): Flow<T> {
return block(db).asFlow().mapToOne(queryDispatcher)
}
override fun <T : Any> subscribeToOneOrNull(block: Database.() -> Query<T>): Flow<T?> {
return block(db).asFlow().mapToOneOrNull(queryDispatcher)
}
override fun <T : Any> subscribeToPagingSource(
countQuery: Database.() -> Query<Long>,
queryProvider: Database.(Long, Long) -> Query<T>,
): PagingSource<Long, T> {
return QueryPagingSource(
handler = this,
countQuery = countQuery,
queryProvider = { limit, offset ->
queryProvider.invoke(db, limit, offset)
},
)
}
private suspend fun <T> dispatch(inTransaction: Boolean, block: suspend Database.() -> T): T {
// Create a transaction if needed and run the calling block inside it.
if (inTransaction) {
return withTransaction { block(db) }
}
// If we're currently in the transaction thread, there's no need to dispatch our query.
if (driver.currentTransaction() != null) {
return block(db)
}
// Get the current database context and run the calling block.
val context = getCurrentDatabaseContext()
return withContext(context) { block(db) }
}
// SY -->
fun getLibraryQuery() = LibraryQuery(driver)
fun getUpdatesQuery(after: Long) = UpdatesQuery(driver, after)
// SY <--
}
@@ -1,54 +0,0 @@
package eu.kanade.data
import com.squareup.sqldelight.ColumnAdapter
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import java.util.Date
val dateAdapter = object : ColumnAdapter<Date, Long> {
override fun decode(databaseValue: Long): Date = Date(databaseValue)
override fun encode(value: Date): Long = value.time
}
private const val listOfStringsSeparator = ", "
val listOfStringsAdapter = object : ColumnAdapter<List<String>, String> {
override fun decode(databaseValue: String) =
if (databaseValue.isEmpty()) {
emptyList()
} else {
databaseValue.split(listOfStringsSeparator)
}
override fun encode(value: List<String>) = value.joinToString(separator = listOfStringsSeparator)
}
val updateStrategyAdapter = object : ColumnAdapter<UpdateStrategy, Long> {
private val enumValues by lazy { UpdateStrategy.values() }
override fun decode(databaseValue: Long): UpdateStrategy =
enumValues.getOrElse(databaseValue.toInt()) { UpdateStrategy.ALWAYS_UPDATE }
override fun encode(value: UpdateStrategy): Long = value.ordinal.toLong()
}
// SY -->
private const val listOfStringsAndSeparator = " & "
val listOfStringsAndAdapter = object : ColumnAdapter<List<String>, String> {
override fun decode(databaseValue: String) =
if (databaseValue.isEmpty()) {
emptyList()
} else {
databaseValue.split(listOfStringsAndSeparator)
}
override fun encode(value: List<String>) = value.joinToString(separator = listOfStringsAndSeparator)
}
private const val listOfLongsSeparator = "/"
val listOfLongsAdapter = object : ColumnAdapter<List<Long>, String> {
override fun decode(databaseValue: String) =
if (databaseValue.isEmpty()) {
emptyList()
} else {
databaseValue.split(listOfLongsSeparator).mapNotNull { it.toLongOrNull() }
}
override fun encode(value: List<Long>) = value.joinToString(separator = listOfLongsSeparator)
}
// SY <--
@@ -1,37 +0,0 @@
package eu.kanade.data
import androidx.paging.PagingSource
import com.squareup.sqldelight.Query
import eu.kanade.tachiyomi.Database
import kotlinx.coroutines.flow.Flow
interface DatabaseHandler {
suspend fun <T> await(inTransaction: Boolean = false, block: suspend Database.() -> T): T
suspend fun <T : Any> awaitList(
inTransaction: Boolean = false,
block: suspend Database.() -> Query<T>,
): List<T>
suspend fun <T : Any> awaitOne(
inTransaction: Boolean = false,
block: suspend Database.() -> Query<T>,
): T
suspend fun <T : Any> awaitOneOrNull(
inTransaction: Boolean = false,
block: suspend Database.() -> Query<T>,
): T?
fun <T : Any> subscribeToList(block: Database.() -> Query<T>): Flow<List<T>>
fun <T : Any> subscribeToOne(block: Database.() -> Query<T>): Flow<T>
fun <T : Any> subscribeToOneOrNull(block: Database.() -> Query<T>): Flow<T?>
fun <T : Any> subscribeToPagingSource(
countQuery: Database.() -> Query<Long>,
queryProvider: Database.(Long, Long) -> Query<T>,
): PagingSource<Long, T>
}
@@ -1,72 +0,0 @@
package eu.kanade.data
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.squareup.sqldelight.Query
import eu.kanade.tachiyomi.Database
import kotlin.properties.Delegates
class QueryPagingSource<RowType : Any>(
val handler: DatabaseHandler,
val countQuery: Database.() -> Query<Long>,
val queryProvider: Database.(Long, Long) -> Query<RowType>,
) : PagingSource<Long, RowType>(), Query.Listener {
override val jumpingSupported: Boolean = true
private var currentQuery: Query<RowType>? by Delegates.observable(null) { _, old, new ->
old?.removeListener(this)
new?.addListener(this)
}
init {
registerInvalidatedCallback {
currentQuery?.removeListener(this)
currentQuery = null
}
}
override suspend fun load(params: LoadParams<Long>): LoadResult<Long, RowType> {
try {
val key = params.key ?: 0L
val loadSize = params.loadSize
val count = handler.awaitOne { countQuery() }
val (offset, limit) = when (params) {
is LoadParams.Prepend -> key - loadSize to loadSize.toLong()
else -> key to loadSize.toLong()
}
val data = handler.awaitList {
queryProvider(limit, offset)
.also { currentQuery = it }
}
val (prevKey, nextKey) = when (params) {
is LoadParams.Append -> { offset - loadSize to offset + loadSize }
else -> { offset to offset + loadSize }
}
return LoadResult.Page(
data = data,
prevKey = if (offset <= 0L || prevKey < 0L) null else prevKey,
nextKey = if (offset + loadSize >= count) null else nextKey,
itemsBefore = maxOf(0L, offset).toInt(),
itemsAfter = maxOf(0L, count - (offset + loadSize)).toInt(),
)
} catch (e: Exception) {
return LoadResult.Error(throwable = e)
}
}
override fun getRefreshKey(state: PagingState<Long, RowType>): Long? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey ?: anchorPage?.nextKey
}
}
override fun queryResultsChanged() {
invalidate()
}
}
@@ -1,161 +0,0 @@
package eu.kanade.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Job
import kotlinx.coroutines.asContextElement
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import java.util.concurrent.RejectedExecutionException
import java.util.concurrent.atomic.AtomicInteger
import kotlin.coroutines.ContinuationInterceptor
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.coroutineContext
import kotlin.coroutines.resume
/**
* Returns the transaction dispatcher if we are on a transaction, or the database dispatchers.
*/
internal suspend fun AndroidDatabaseHandler.getCurrentDatabaseContext(): CoroutineContext {
return coroutineContext[TransactionElement]?.transactionDispatcher ?: queryDispatcher
}
/**
* Calls the specified suspending [block] in a database transaction. The transaction will be
* marked as successful unless an exception is thrown in the suspending [block] or the coroutine
* is cancelled.
*
* SQLDelight will only perform at most one transaction at a time, additional transactions are queued
* and executed on a first come, first serve order.
*
* Performing blocking database operations is not permitted in a coroutine scope other than the
* one received by the suspending block. It is recommended that all [Dao] function invoked within
* the [block] be suspending functions.
*
* The dispatcher used to execute the given [block] will utilize threads from SQLDelight's query executor.
*/
internal suspend fun <T> AndroidDatabaseHandler.withTransaction(block: suspend () -> T): T {
// Use inherited transaction context if available, this allows nested suspending transactions.
val transactionContext =
coroutineContext[TransactionElement]?.transactionDispatcher ?: createTransactionContext()
return withContext(transactionContext) {
val transactionElement = coroutineContext[TransactionElement]!!
transactionElement.acquire()
try {
db.transactionWithResult {
runBlocking(transactionContext) {
block()
}
}
} finally {
transactionElement.release()
}
}
}
/**
* Creates a [CoroutineContext] for performing database operations within a coroutine transaction.
*
* The context is a combination of a dispatcher, a [TransactionElement] and a thread local element.
*
* * The dispatcher will dispatch coroutines to a single thread that is taken over from the SQLDelight
* query executor. If the coroutine context is switched, suspending DAO functions will be able to
* dispatch to the transaction thread.
*
* * The [TransactionElement] serves as an indicator for inherited context, meaning, if there is a
* switch of context, suspending DAO methods will be able to use the indicator to dispatch the
* database operation to the transaction thread.
*
* * The thread local element serves as a second indicator and marks threads that are used to
* execute coroutines within the coroutine transaction, more specifically it allows us to identify
* if a blocking DAO method is invoked within the transaction coroutine. Never assign meaning to
* this value, for now all we care is if its present or not.
*/
private suspend fun AndroidDatabaseHandler.createTransactionContext(): CoroutineContext {
val controlJob = Job()
// make sure to tie the control job to this context to avoid blocking the transaction if
// context get cancelled before we can even start using this job. Otherwise, the acquired
// transaction thread will forever wait for the controlJob to be cancelled.
// see b/148181325
coroutineContext[Job]?.invokeOnCompletion {
controlJob.cancel()
}
val dispatcher = transactionDispatcher.acquireTransactionThread(controlJob)
val transactionElement = TransactionElement(controlJob, dispatcher)
val threadLocalElement =
suspendingTransactionId.asContextElement(System.identityHashCode(controlJob))
return dispatcher + transactionElement + threadLocalElement
}
/**
* Acquires a thread from the executor and returns a [ContinuationInterceptor] to dispatch
* coroutines to the acquired thread. The [controlJob] is used to control the release of the
* thread by cancelling the job.
*/
private suspend fun CoroutineDispatcher.acquireTransactionThread(
controlJob: Job,
): ContinuationInterceptor {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
// We got cancelled while waiting to acquire a thread, we can't stop our attempt to
// acquire a thread, but we can cancel the controlling job so once it gets acquired it
// is quickly released.
controlJob.cancel()
}
try {
dispatch(EmptyCoroutineContext) {
runBlocking {
// Thread acquired, resume coroutine
continuation.resume(coroutineContext[ContinuationInterceptor]!!)
controlJob.join()
}
}
} catch (ex: RejectedExecutionException) {
// Couldn't acquire a thread, cancel coroutine
continuation.cancel(
IllegalStateException(
"Unable to acquire a thread to perform the database transaction",
ex,
),
)
}
}
}
/**
* A [CoroutineContext.Element] that indicates there is an on-going database transaction.
*/
private class TransactionElement(
private val transactionThreadControlJob: Job,
val transactionDispatcher: ContinuationInterceptor,
) : CoroutineContext.Element {
companion object Key : CoroutineContext.Key<TransactionElement>
override val key: CoroutineContext.Key<TransactionElement>
get() = TransactionElement
/**
* Number of transactions (including nested ones) started with this element.
* Call [acquire] to increase the count and [release] to decrease it. If the count reaches zero
* when [release] is invoked then the transaction job is cancelled and the transaction thread
* is released.
*/
private val referenceCount = AtomicInteger(0)
fun acquire() {
referenceCount.incrementAndGet()
}
fun release() {
val count = referenceCount.decrementAndGet()
if (count < 0) {
throw IllegalStateException("Transaction was never started or was already released")
} else if (count == 0) {
// Cancel the job that controls the transaction thread, causing it to be released.
transactionThreadControlJob.cancel()
}
}
}
@@ -1,11 +1,11 @@
package eu.kanade.data.category
import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.category.model.Category
import eu.kanade.domain.category.model.CategoryUpdate
import eu.kanade.domain.category.repository.CategoryRepository
import eu.kanade.tachiyomi.Database
import kotlinx.coroutines.flow.Flow
import tachiyomi.data.Database
import tachiyomi.data.DatabaseHandler
class CategoryRepositoryImpl(
private val handler: DatabaseHandler,
@@ -1,6 +1,5 @@
package eu.kanade.data.chapter
import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.model.ChapterUpdate
import eu.kanade.domain.chapter.repository.ChapterRepository
@@ -8,6 +7,7 @@ import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toLong
import kotlinx.coroutines.flow.Flow
import logcat.LogPriority
import tachiyomi.data.DatabaseHandler
class ChapterRepositoryImpl(
private val handler: DatabaseHandler,
@@ -1,6 +1,5 @@
package eu.kanade.data.history
import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.history.model.History
import eu.kanade.domain.history.model.HistoryUpdate
import eu.kanade.domain.history.model.HistoryWithRelations
@@ -8,6 +7,7 @@ import eu.kanade.domain.history.repository.HistoryRepository
import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.flow.Flow
import logcat.LogPriority
import tachiyomi.data.DatabaseHandler
class HistoryRepositoryImpl(
private val handler: DatabaseHandler,
@@ -1,9 +1,9 @@
package eu.kanade.data.manga
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.favoriteEntryMapper
import eu.kanade.domain.manga.repository.FavoritesEntryRepository
import exh.favorites.sql.models.FavoriteEntry
import tachiyomi.data.DatabaseHandler
class FavoritesEntryRepositoryImpl(
private val handler: DatabaseHandler,
@@ -1,123 +0,0 @@
package eu.kanade.data.manga
import com.squareup.sqldelight.Query
import com.squareup.sqldelight.db.SqlCursor
import com.squareup.sqldelight.db.SqlDriver
import com.squareup.sqldelight.internal.copyOnWriteList
import eu.kanade.data.listOfStringsAdapter
import eu.kanade.data.listOfStringsAndAdapter
import eu.kanade.data.updateStrategyAdapter
import eu.kanade.domain.library.model.LibraryManga
import exh.source.MERGED_SOURCE_ID
private val mapper = { cursor: SqlCursor ->
LibraryManga(
manga = mangaMapper(
cursor.getLong(0)!!,
cursor.getLong(1)!!,
cursor.getString(2)!!,
cursor.getString(3),
cursor.getString(4),
cursor.getString(5),
cursor.getString(6)?.let(listOfStringsAdapter::decode),
cursor.getString(7)!!,
cursor.getLong(8)!!,
cursor.getString(9),
cursor.getLong(10)!! == 1L,
cursor.getLong(11) ?: 0,
null,
cursor.getLong(13)!! == 1L,
cursor.getLong(14)!!,
cursor.getLong(15)!!,
cursor.getLong(16)!!,
cursor.getLong(17)!!,
cursor.getString(18)?.let(listOfStringsAndAdapter::decode),
updateStrategyAdapter.decode(cursor.getLong(19)!!),
),
totalChapters = cursor.getLong(20)!!,
readCount = cursor.getLong(21)!!,
latestUpload = cursor.getLong(22)!!,
chapterFetchedAt = cursor.getLong(23)!!,
lastRead = cursor.getLong(24)!!,
bookmarkCount = cursor.getLong(25)!!,
category = cursor.getLong(26)!!,
)
}
class LibraryQuery(val driver: SqlDriver) : Query<LibraryManga>(copyOnWriteList(), mapper) {
override fun execute(): SqlCursor {
return driver.executeQuery(
null,
"""
SELECT
M.*,
coalesce(C.total, 0) AS totalCount,
coalesce(C.readCount, 0) AS readCount,
coalesce(C.latestUpload, 0) AS latestUpload,
coalesce(C.fetchedAt, 0) AS chapterFetchedAt,
coalesce(C.lastRead, 0) AS lastRead,
coalesce(C.bookmarkCount, 0) AS bookmarkCount,
coalesce(MC.category_id, 0) AS category
FROM mangas M
LEFT JOIN(
SELECT
chapters.manga_id,
count(*) AS total,
sum(read) AS readCount,
coalesce(max(chapters.date_upload), 0) AS latestUpload,
coalesce(max(history.last_read), 0) AS lastRead,
coalesce(max(chapters.date_fetch), 0) AS fetchedAt,
sum(chapters.bookmark) AS bookmarkCount
FROM chapters
LEFT JOIN history
ON chapters._id = history.chapter_id
GROUP BY chapters.manga_id
) AS C
ON M._id = C.manga_id
LEFT JOIN mangas_categories AS MC
ON MC.manga_id = M._id
WHERE M.favorite = 1 AND M.source <> $MERGED_SOURCE_ID
UNION
SELECT
M.*,
coalesce(C.total, 0) AS totalCount,
coalesce(C.readCount, 0) AS readCount,
coalesce(C.latestUpload, 0) AS latestUpload,
coalesce(C.fetchedAt, 0) AS chapterFetchedAt,
coalesce(C.lastRead, 0) AS lastRead,
coalesce(C.bookmarkCount, 0) AS bookmarkCount,
coalesce(MC.category_id, 0) AS category
FROM mangas M
LEFT JOIN (
SELECT merged.manga_id,merged.merge_id
FROM merged
GROUP BY merged.merge_id
) as ME
ON ME.merge_id = M._id
LEFT JOIN(
SELECT
ME.merge_id,
count(*) AS total,
sum(read) AS readCount,
coalesce(max(chapters.date_upload), 0) AS latestUpload,
coalesce(max(history.last_read), 0) AS lastRead,
coalesce(max(chapters.date_fetch), 0) AS fetchedAt,
sum(chapters.bookmark) AS bookmarkCount
FROM chapters
LEFT JOIN history
ON chapters._id = history.chapter_id
LEFT JOIN merged as ME
ON ME.manga_id = chapters.manga_id
GROUP BY ME.merge_id
) AS C
ON ME.merge_id = C.merge_id
LEFT JOIN mangas_categories AS MC
ON MC.manga_id = M._id
WHERE M.favorite = 1 AND M.source = $MERGED_SOURCE_ID;
""".trimIndent(),
1,
)
}
override fun toString(): String = "LibraryQuery.sq:get"
}
@@ -3,6 +3,7 @@ package eu.kanade.data.manga
import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import tachiyomi.view.LibraryView
val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, List<String>?, UpdateStrategy) -> Manga =
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, _, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, filteredScanlators, updateStrategy ->
@@ -69,3 +70,36 @@ val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?,
lastRead = lastRead,
)
}
val libraryViewMapper: (LibraryView) -> LibraryManga = {
LibraryManga(
Manga(
id = it._id,
source = it.source,
favorite = it.favorite,
lastUpdate = it.last_update ?: 0,
dateAdded = it.date_added,
viewerFlags = it.viewer,
chapterFlags = it.chapter_flags,
coverLastModified = it.cover_last_modified,
url = it.url,
ogTitle = it.title,
ogArtist = it.artist,
ogAuthor = it.author,
ogDescription = it.description,
ogGenre = it.genre,
ogStatus = it.status,
thumbnailUrl = it.thumbnail_url,
updateStrategy = it.update_strategy,
initialized = it.initialized,
filteredScanlators = it.filtered_scanlators,
),
it.category,
it.totalCount,
it.readCount,
it.bookmarkCount,
it.latestUpload,
it.chapterFetchedAt,
it.lastRead,
)
}
@@ -1,6 +1,5 @@
package eu.kanade.data.manga
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.mergedMangaReferenceMapper
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MergeMangaSettingsUpdate
@@ -10,6 +9,7 @@ import eu.kanade.tachiyomi.util.system.toLong
import exh.merged.sql.models.MergedMangaReference
import kotlinx.coroutines.flow.Flow
import logcat.LogPriority
import tachiyomi.data.DatabaseHandler
class MangaMergeRepositoryImpl(
private val handler: DatabaseHandler,
@@ -1,6 +1,5 @@
package eu.kanade.data.manga
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.searchMetadataMapper
import eu.kanade.data.exh.searchTagMapper
import eu.kanade.data.exh.searchTitleMapper
@@ -13,6 +12,7 @@ import exh.metadata.sql.models.SearchTitle
import exh.source.EH_SOURCE_ID
import exh.source.EXH_SOURCE_ID
import kotlinx.coroutines.flow.Flow
import tachiyomi.data.DatabaseHandler
class MangaMetadataRepositoryImpl(
private val handler: DatabaseHandler,
@@ -1,10 +1,5 @@
package eu.kanade.data.manga
import eu.kanade.data.AndroidDatabaseHandler
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.listOfStringsAdapter
import eu.kanade.data.listOfStringsAndAdapter
import eu.kanade.data.updateStrategyAdapter
import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MangaUpdate
@@ -14,6 +9,11 @@ import eu.kanade.tachiyomi.util.system.toLong
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import logcat.LogPriority
import tachiyomi.data.AndroidDatabaseHandler
import tachiyomi.data.DatabaseHandler
import tachiyomi.data.listOfStringsAdapter
import tachiyomi.data.listOfStringsAndAdapter
import tachiyomi.data.updateStrategyAdapter
class MangaRepositoryImpl(
private val handler: DatabaseHandler,
@@ -40,7 +40,7 @@ class MangaRepositoryImpl(
}
override suspend fun getLibraryManga(): List<LibraryManga> {
return handler.awaitList { (handler as AndroidDatabaseHandler).getLibraryQuery() }
return handler.awaitList { (handler as AndroidDatabaseHandler).getLibraryQuery() }.map(libraryViewMapper)
// return handler.awaitList { libraryViewQueries.library(libraryManga) }
}
@@ -1,12 +1,12 @@
package eu.kanade.data.source
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.feedSavedSearchMapper
import eu.kanade.data.exh.savedSearchMapper
import eu.kanade.domain.source.repository.FeedSavedSearchRepository
import exh.savedsearches.models.FeedSavedSearch
import exh.savedsearches.models.SavedSearch
import kotlinx.coroutines.flow.Flow
import tachiyomi.data.DatabaseHandler
class FeedSavedSearchRepositoryImpl(
private val handler: DatabaseHandler,
@@ -1,10 +1,10 @@
package eu.kanade.data.source
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.savedSearchMapper
import eu.kanade.domain.source.repository.SavedSearchRepository
import exh.savedsearches.models.SavedSearch
import kotlinx.coroutines.flow.Flow
import tachiyomi.data.DatabaseHandler
class SavedSearchRepositoryImpl(
private val handler: DatabaseHandler,
@@ -1,9 +1,9 @@
package eu.kanade.data.source
import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.source.model.SourceData
import eu.kanade.domain.source.repository.SourceDataRepository
import kotlinx.coroutines.flow.Flow
import tachiyomi.data.DatabaseHandler
class SourceDataRepositoryImpl(
private val handler: DatabaseHandler,
@@ -1,6 +1,5 @@
package eu.kanade.data.source
import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.source.model.Source
import eu.kanade.domain.source.model.SourcePagingSourceType
import eu.kanade.domain.source.model.SourceWithCount
@@ -13,6 +12,7 @@ import eu.kanade.tachiyomi.source.online.all.EHentai
import exh.source.MERGED_SOURCE_ID
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import tachiyomi.data.DatabaseHandler
class SourceRepositoryImpl(
private val sourceManager: SourceManager,
@@ -1,9 +1,9 @@
package eu.kanade.data.track
import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.track.model.Track
import eu.kanade.domain.track.repository.TrackRepository
import kotlinx.coroutines.flow.Flow
import tachiyomi.data.DatabaseHandler
class TrackRepositoryImpl(
private val handler: DatabaseHandler,
@@ -2,6 +2,7 @@ package eu.kanade.data.updates
import eu.kanade.domain.manga.model.MangaCover
import eu.kanade.domain.updates.model.UpdatesWithRelations
import tachiyomi.view.UpdatesView
val updateWithRelationMapper: (Long, String, Long, String, String?, Boolean, Boolean, Long, Long, Boolean, String?, Long, Long, Long) -> UpdatesWithRelations = {
mangaId, mangaTitle, chapterId, chapterName, scanlator, read, bookmark, lastPageRead, sourceId, favorite, thumbnailUrl, coverLastModified, _, dateFetch ->
@@ -27,3 +28,25 @@ val updateWithRelationMapper: (Long, String, Long, String, String?, Boolean, Boo
),
)
}
val updatesViewMapper: (UpdatesView) -> UpdatesWithRelations = {
UpdatesWithRelations(
mangaId = it.mangaId,
ogMangaTitle = it.mangaTitle,
chapterId = it.chapterId,
chapterName = it.chapterName,
scanlator = it.scanlator,
read = it.read,
bookmark = it.bookmark,
lastPageRead = it.last_page_read,
sourceId = it.source,
dateFetch = it.datefetch,
coverData = MangaCover(
mangaId = it.mangaId,
sourceId = it.source,
isMangaFavorite = it.favorite,
url = it.thumbnailUrl,
lastModified = it.coverLastModified,
),
)
}
@@ -1,92 +0,0 @@
package eu.kanade.data.updates
import com.squareup.sqldelight.Query
import com.squareup.sqldelight.db.SqlCursor
import com.squareup.sqldelight.db.SqlDriver
import com.squareup.sqldelight.internal.copyOnWriteList
import eu.kanade.domain.updates.model.UpdatesWithRelations
import exh.source.MERGED_SOURCE_ID
private val mapper = { cursor: SqlCursor ->
updateWithRelationMapper(
cursor.getLong(0)!!,
cursor.getString(1)!!,
cursor.getLong(2)!!,
cursor.getString(3)!!,
cursor.getString(4),
cursor.getLong(5)!! == 1L,
cursor.getLong(6)!! == 1L,
cursor.getLong(7)!!,
cursor.getLong(8)!!,
cursor.getLong(9)!! == 1L,
cursor.getString(10),
cursor.getLong(11)!!,
cursor.getLong(12)!!,
cursor.getLong(13)!!,
)
}
class UpdatesQuery(val driver: SqlDriver, val after: Long) : Query<UpdatesWithRelations>(copyOnWriteList(), mapper) {
override fun execute(): SqlCursor {
return driver.executeQuery(
null,
"""
SELECT
mangas._id AS mangaId,
mangas.title AS mangaTitle,
chapters._id AS chapterId,
chapters.name AS chapterName,
chapters.scanlator,
chapters.read,
chapters.bookmark,
chapters.last_page_read,
mangas.source,
mangas.favorite,
mangas.thumbnail_url AS thumbnailUrl,
mangas.cover_last_modified AS coverLastModified,
chapters.date_upload AS dateUpload,
chapters.date_fetch AS datefetch
FROM mangas JOIN chapters
ON mangas._id = chapters.manga_id
WHERE favorite = 1 AND source <> $MERGED_SOURCE_ID
AND date_fetch > date_added
AND dateUpload > :after
UNION
SELECT
mangas._id AS mangaId,
mangas.title AS mangaTitle,
chapters._id AS chapterId,
chapters.name AS chapterName,
chapters.scanlator,
chapters.read,
chapters.bookmark,
chapters.last_page_read,
mangas.source,
mangas.favorite,
mangas.thumbnail_url AS thumbnailUrl,
mangas.cover_last_modified AS coverLastModified,
chapters.date_upload AS dateUpload,
chapters.date_fetch AS datefetch
FROM mangas
LEFT JOIN (
SELECT merged.manga_id,merged.merge_id
FROM merged
GROUP BY merged.merge_id
) as ME
ON ME.merge_id = mangas._id
JOIN chapters
ON ME.manga_id = chapters.manga_id
WHERE favorite = 1 AND source = $MERGED_SOURCE_ID
AND date_fetch > date_added
AND dateUpload > :after
ORDER BY datefetch DESC;
""".trimIndent(),
1,
binders = {
bindLong(1, after)
},
)
}
override fun toString(): String = "LibraryQuery.sq:get"
}
@@ -1,11 +1,11 @@
package eu.kanade.data.updates
import eu.kanade.data.AndroidDatabaseHandler
import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.updates.model.UpdatesWithRelations
import eu.kanade.domain.updates.repository.UpdatesRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import tachiyomi.data.AndroidDatabaseHandler
import tachiyomi.data.DatabaseHandler
class UpdatesRepositoryImpl(
val databaseHandler: DatabaseHandler,
@@ -16,6 +16,7 @@ class UpdatesRepositoryImpl(
updatesViewQueries.updates(after, updateWithRelationMapper)
}.map {
databaseHandler.awaitList { (databaseHandler as AndroidDatabaseHandler).getUpdatesQuery(after) }
.map(updatesViewMapper)
}
}
}
@@ -50,7 +50,6 @@ import eu.kanade.presentation.components.FastScrollLazyColumn
import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.util.selectedBackground
import eu.kanade.tachiyomi.Database
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI
@@ -58,6 +57,7 @@ import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.update
import tachiyomi.data.Database
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -74,10 +74,6 @@ import eu.kanade.tachiyomi.util.CrashLogUtil
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.isPackageInstalled
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
import eu.kanade.tachiyomi.util.system.isReleaseBuildType
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.powerManager
+1 -1
View File
@@ -35,7 +35,6 @@ import com.elvishew.xlog.printer.file.clean.FileLastModifiedCleanStrategy
import com.elvishew.xlog.printer.file.naming.DateFileNameGenerator
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.DomainModule
import eu.kanade.domain.SYDomainModule
import eu.kanade.domain.base.BasePreferences
@@ -74,6 +73,7 @@ import kotlinx.coroutines.flow.onEach
import logcat.LogPriority
import logcat.LogcatLogger
import org.conscrypt.Conscrypt
import tachiyomi.data.DatabaseHandler
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
@@ -7,16 +7,6 @@ import androidx.sqlite.db.SupportSQLiteDatabase
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
import com.squareup.sqldelight.android.AndroidSqliteDriver
import com.squareup.sqldelight.db.SqlDriver
import data.Categories
import data.History
import data.Mangas
import eu.kanade.data.AndroidDatabaseHandler
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.dateAdapter
import eu.kanade.data.listOfLongsAdapter
import eu.kanade.data.listOfStringsAdapter
import eu.kanade.data.listOfStringsAndAdapter
import eu.kanade.data.updateStrategyAdapter
import eu.kanade.domain.UnsortedPreferences
import eu.kanade.domain.backup.service.BackupPreferences
import eu.kanade.domain.base.BasePreferences
@@ -55,6 +45,17 @@ import nl.adaptivity.xmlutil.XmlDeclMode
import nl.adaptivity.xmlutil.core.XmlVersion
import nl.adaptivity.xmlutil.serialization.UnknownChildHandler
import nl.adaptivity.xmlutil.serialization.XML
import tachiyomi.data.AndroidDatabaseHandler
import tachiyomi.data.Categories
import tachiyomi.data.Database
import tachiyomi.data.DatabaseHandler
import tachiyomi.data.History
import tachiyomi.data.Mangas
import tachiyomi.data.dateAdapter
import tachiyomi.data.listOfLongsAdapter
import tachiyomi.data.listOfStringsAdapter
import tachiyomi.data.listOfStringsAndAdapter
import tachiyomi.data.updateStrategyAdapter
import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addSingleton
@@ -4,13 +4,8 @@ import android.Manifest
import android.content.Context
import android.net.Uri
import com.hippo.unifile.UniFile
import data.Manga_sync
import data.Mangas
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.mergedMangaReferenceMapper
import eu.kanade.data.listOfStringsAndAdapter
import eu.kanade.data.manga.mangaMapper
import eu.kanade.data.updateStrategyAdapter
import eu.kanade.domain.backup.service.BackupPreferences
import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.category.model.Category
@@ -65,6 +60,11 @@ import logcat.LogPriority
import okio.buffer
import okio.gzip
import okio.sink
import tachiyomi.data.DatabaseHandler
import tachiyomi.data.Manga_sync
import tachiyomi.data.Mangas
import tachiyomi.data.listOfStringsAndAdapter
import tachiyomi.data.updateStrategyAdapter
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.FileOutputStream
@@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.data.backup.models
import eu.kanade.data.listOfStringsAndAdapter
import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.data.database.models.ChapterImpl
import eu.kanade.tachiyomi.data.database.models.MangaImpl
@@ -10,6 +9,7 @@ import eu.kanade.tachiyomi.source.model.UpdateStrategy
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import tachiyomi.data.listOfStringsAndAdapter
@Suppress("DEPRECATION")
@Serializable
@@ -1,10 +1,10 @@
package eu.kanade.tachiyomi.data.database.models
import eu.kanade.data.listOfStringsAndAdapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import exh.util.nullIfEmpty
import tachiyomi.data.listOfStringsAndAdapter
import eu.kanade.domain.manga.model.Manga as DomainManga
interface Manga : SManga {
@@ -43,7 +43,6 @@ import coil.request.ImageRequest
import coil.size.Precision
import coil.size.Scale
import coil.transform.RoundedCornersTransformation
import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.manga.model.MangaCover
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.core.security.SecurityPreferences
@@ -52,10 +51,11 @@ import eu.kanade.tachiyomi.util.Constants
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.system.dpToPx
import kotlinx.coroutines.MainScope
import tachiyomi.data.DatabaseHandler
import tachiyomi.view.UpdatesView
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import view.UpdatesView
import java.util.Calendar
import java.util.Date
@@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.source.model
import data.Chapters
import tachiyomi.data.Chapters
fun SChapter.copyFrom(other: Chapters) {
name = other.name
@@ -1,8 +1,8 @@
package eu.kanade.tachiyomi.source.model
import data.Mangas
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.DownloadManager
import tachiyomi.data.Mangas
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -31,7 +31,7 @@ fun SManga.copyFrom(other: Mangas) {
}
if (other.genre != null) {
genre = other.genre.joinToString(separator = ", ")
genre = other.genre!!.joinToString(separator = ", ")
}
if (other.thumbnail_url != null) {