Parcourir la source

Revert history Compose/SQLDelight changes

arkon il y a 2 ans
Parent
commit
96c894ce5b
91 fichiers modifiés avec 988 ajouts et 1808 suppressions
  1. 0 22
      app/build.gradle.kts
  2. 0 94
      app/src/main/java/eu/kanade/data/AndroidDatabaseHandler.kt
  3. 0 20
      app/src/main/java/eu/kanade/data/DatabaseAdapter.kt
  4. 0 39
      app/src/main/java/eu/kanade/data/DatabaseHandler.kt
  5. 0 160
      app/src/main/java/eu/kanade/data/TransactionContext.kt
  6. 0 21
      app/src/main/java/eu/kanade/data/chapter/ChapterMapper.kt
  7. 0 26
      app/src/main/java/eu/kanade/data/history/HistoryMapper.kt
  8. 0 91
      app/src/main/java/eu/kanade/data/history/HistoryRepositoryImpl.kt
  9. 0 26
      app/src/main/java/eu/kanade/data/manga/MangaMapper.kt
  10. 0 26
      app/src/main/java/eu/kanade/domain/DomainModule.kt
  11. 0 16
      app/src/main/java/eu/kanade/domain/chapter/model/Chapter.kt
  12. 0 12
      app/src/main/java/eu/kanade/domain/history/interactor/DeleteHistoryTable.kt
  13. 0 21
      app/src/main/java/eu/kanade/domain/history/interactor/GetHistory.kt
  14. 0 13
      app/src/main/java/eu/kanade/domain/history/interactor/GetNextChapterForManga.kt
  15. 0 13
      app/src/main/java/eu/kanade/domain/history/interactor/RemoveHistoryById.kt
  16. 0 12
      app/src/main/java/eu/kanade/domain/history/interactor/RemoveHistoryByMangaId.kt
  17. 0 9
      app/src/main/java/eu/kanade/domain/history/model/History.kt
  18. 0 13
      app/src/main/java/eu/kanade/domain/history/model/HistoryWithRelations.kt
  19. 0 18
      app/src/main/java/eu/kanade/domain/history/repository/HistoryRepository.kt
  20. 0 36
      app/src/main/java/eu/kanade/domain/manga/model/Manga.kt
  21. 0 49
      app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt
  22. 0 39
      app/src/main/java/eu/kanade/presentation/components/MangaCover.kt
  23. 0 297
      app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt
  24. 0 20
      app/src/main/java/eu/kanade/presentation/theme/TachiyomiTheme.kt
  25. 0 5
      app/src/main/java/eu/kanade/presentation/util/Constants.kt
  26. 0 5
      app/src/main/java/eu/kanade/presentation/util/LazyListState.kt
  27. 0 2
      app/src/main/java/eu/kanade/tachiyomi/App.kt
  28. 1 38
      app/src/main/java/eu/kanade/tachiyomi/AppModule.kt
  29. 1 1
      app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt
  30. 1 1
      app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt
  31. 2 5
      app/src/main/java/eu/kanade/tachiyomi/data/database/DatabaseHelper.kt
  32. 80 10
      app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt
  33. 34 6
      app/src/main/java/eu/kanade/tachiyomi/data/database/queries/HistoryQueries.kt
  34. 1 2
      app/src/main/java/eu/kanade/tachiyomi/data/database/queries/RawQueries.kt
  35. 1 1
      app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/HistoryLastReadPutResolver.kt
  36. 9 0
      app/src/main/java/eu/kanade/tachiyomi/data/database/tables/CategoryTable.kt
  37. 38 0
      app/src/main/java/eu/kanade/tachiyomi/data/database/tables/ChapterTable.kt
  38. 20 0
      app/src/main/java/eu/kanade/tachiyomi/data/database/tables/HistoryTable.kt
  39. 12 0
      app/src/main/java/eu/kanade/tachiyomi/data/database/tables/MangaCategoryTable.kt
  40. 49 0
      app/src/main/java/eu/kanade/tachiyomi/data/database/tables/MangaTable.kt
  41. 40 0
      app/src/main/java/eu/kanade/tachiyomi/data/database/tables/TrackTable.kt
  42. 1 1
      app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt
  43. 1 2
      app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt
  44. 0 26
      app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ComposeController.kt
  45. 0 3
      app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt
  46. 24 0
      app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaCoverImageView.kt
  47. 3 8
      app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
  48. 1 1
      app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt
  49. 0 21
      app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/ClearHistoryDialogController.kt
  50. 51 0
      app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryAdapter.kt
  51. 197 44
      app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryController.kt
  52. 71 0
      app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryHolder.kt
  53. 42 0
      app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryItem.kt
  54. 124 94
      app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryPresenter.kt
  55. 54 0
      app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/RemoveHistoryDialog.kt
  56. 1 2
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt
  57. 3 0
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/database/ClearDatabaseController.kt
  58. 1 3
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/database/ClearDatabasePresenter.kt
  59. 1 12
      app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiCoordinatorLayout.kt
  60. 1 1
      app/src/main/java/eu/kanade/tachiyomi/widget/preference/ThemesPreferenceAdapter.kt
  61. 0 4
      app/src/main/res/layout/compose_controller.xml
  62. 33 0
      app/src/main/res/layout/history_controller.xml
  63. 85 0
      app/src/main/res/layout/history_item.xml
  64. 0 6
      app/src/main/sqldelight/data/categories.sq
  65. 0 29
      app/src/main/sqldelight/data/chapters.sq
  66. 0 37
      app/src/main/sqldelight/data/history.sq
  67. 0 18
      app/src/main/sqldelight/data/manga_sync.sq
  68. 0 31
      app/src/main/sqldelight/data/mangas.sq
  69. 0 9
      app/src/main/sqldelight/data/mangas_categories.sq
  70. 0 6
      app/src/main/sqldelight/migrations/1.sqm
  71. 0 11
      app/src/main/sqldelight/migrations/10.sqm
  72. 0 2
      app/src/main/sqldelight/migrations/11.sqm
  73. 0 9
      app/src/main/sqldelight/migrations/12.sqm
  74. 0 3
      app/src/main/sqldelight/migrations/13.sqm
  75. 0 149
      app/src/main/sqldelight/migrations/14.sqm
  76. 0 10
      app/src/main/sqldelight/migrations/2.sqm
  77. 0 2
      app/src/main/sqldelight/migrations/3.sqm
  78. 0 2
      app/src/main/sqldelight/migrations/4.sqm
  79. 0 2
      app/src/main/sqldelight/migrations/5.sqm
  80. 0 2
      app/src/main/sqldelight/migrations/6.sqm
  81. 0 9
      app/src/main/sqldelight/migrations/7.sqm
  82. 0 5
      app/src/main/sqldelight/migrations/8.sqm
  83. 0 2
      app/src/main/sqldelight/migrations/9.sqm
  84. 0 46
      app/src/main/sqldelight/view/historyView.sq
  85. 1 1
      app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt
  86. 0 1
      build.gradle.kts
  87. 0 3
      gradle/androidx.versions.toml
  88. 0 9
      gradle/compose.versions.toml
  89. 1 1
      gradle/kotlinx.versions.toml
  90. 3 9
      gradle/libs.versions.toml
  91. 0 3
      settings.gradle.kts

+ 0 - 22
app/build.gradle.kts

@@ -6,7 +6,6 @@ plugins {
     kotlin("android")
     kotlin("plugin.serialization")
     id("com.github.zellius.shortcut-helper")
-    id("com.squareup.sqldelight")
 }
 
 if (gradle.startParameter.taskRequests.toString().contains("Standard")) {
@@ -110,7 +109,6 @@ android {
 
     buildFeatures {
         viewBinding = true
-        compose = true
 
         // Disable some unused things
         aidl = false
@@ -124,10 +122,6 @@ android {
         checkReleaseBuilds = false
     }
 
-    composeOptions {
-        kotlinCompilerExtensionVersion = compose.versions.compose.get()
-    }
-
     compileOptions {
         sourceCompatibility = JavaVersion.VERSION_1_8
         targetCompatibility = JavaVersion.VERSION_1_8
@@ -139,19 +133,6 @@ android {
 }
 
 dependencies {
-    implementation(compose.foundation)
-    implementation(compose.material3.core)
-    implementation(compose.material3.adapter)
-    implementation(compose.animation)
-    implementation(compose.ui.tooling)
-
-    implementation(androidx.paging.runtime)
-    implementation(androidx.paging.compose)
-
-    implementation(libs.sqldelight.android.driver)
-    implementation(libs.sqldelight.coroutines)
-    implementation(libs.sqldelight.android.paging)
-
     implementation(kotlinx.reflect)
 
     implementation(kotlinx.bundles.coroutines)
@@ -282,9 +263,6 @@ tasks {
             "-Xopt-in=kotlinx.coroutines.InternalCoroutinesApi",
             "-Xopt-in=kotlinx.serialization.ExperimentalSerializationApi",
             "-Xopt-in=coil.annotation.ExperimentalCoilApi",
-            "-Xopt-in=androidx.compose.material3.ExperimentalMaterial3Api",
-            "-Xopt-in=androidx.compose.ui.ExperimentalComposeUiApi",
-            "-Xopt-in=androidx.compose.foundation.ExperimentalFoundationApi"
         )
     }
 

+ 0 - 94
app/src/main/java/eu/kanade/data/AndroidDatabaseHandler.kt

@@ -1,94 +0,0 @@
-package eu.kanade.data
-
-import androidx.paging.PagingSource
-import com.squareup.sqldelight.Query
-import com.squareup.sqldelight.Transacter
-import com.squareup.sqldelight.android.paging3.QueryPagingSource
-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.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>,
-        transacter: Database.() -> Transacter,
-        queryProvider: Database.(Long, Long) -> Query<T>
-    ): PagingSource<Long, T> {
-        return QueryPagingSource(
-            countQuery = countQuery(db),
-            transacter = transacter(db),
-            dispatcher = queryDispatcher,
-            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) }
-    }
-}

+ 0 - 20
app/src/main/java/eu/kanade/data/DatabaseAdapter.kt

@@ -1,20 +0,0 @@
-package eu.kanade.data
-
-import com.squareup.sqldelight.ColumnAdapter
-import java.util.*
-
-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()) {
-            listOf()
-        } else {
-            databaseValue.split(listOfStringsSeparator)
-        }
-    override fun encode(value: List<String>) = value.joinToString(separator = listOfStringsSeparator)
-}

+ 0 - 39
app/src/main/java/eu/kanade/data/DatabaseHandler.kt

@@ -1,39 +0,0 @@
-package eu.kanade.data
-
-import androidx.paging.PagingSource
-import com.squareup.sqldelight.Query
-import com.squareup.sqldelight.Transacter
-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>,
-        transacter: Database.() -> Transacter,
-        queryProvider: Database.(Long, Long) -> Query<T>
-    ): PagingSource<Long, T>
-}

+ 0 - 160
app/src/main/java/eu/kanade/data/TransactionContext.kt

@@ -1,160 +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()
-        }
-    }
-}

+ 0 - 21
app/src/main/java/eu/kanade/data/chapter/ChapterMapper.kt

@@ -1,21 +0,0 @@
-package eu.kanade.data.chapter
-
-import eu.kanade.domain.chapter.model.Chapter
-
-val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Float, Long, Long, Long) -> Chapter =
-    { id, mangaId, url, name, scanlator, read, bookmark, lastPageRead, chapterNumber, sourceOrder, dateFetch, dateUpload ->
-        Chapter(
-            id = id,
-            mangaId = mangaId,
-            read = read,
-            bookmark = bookmark,
-            lastPageRead = lastPageRead,
-            dateFetch = dateFetch,
-            sourceOrder = sourceOrder,
-            url = url,
-            name = name,
-            dateUpload = dateUpload,
-            chapterNumber = chapterNumber,
-            scanlator = scanlator,
-        )
-    }

+ 0 - 26
app/src/main/java/eu/kanade/data/history/HistoryMapper.kt

@@ -1,26 +0,0 @@
-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 - 91
app/src/main/java/eu/kanade/data/history/HistoryRepositoryImpl.kt

@@ -1,91 +0,0 @@
-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
-        }
-    }
-}

+ 0 - 26
app/src/main/java/eu/kanade/data/manga/MangaMapper.kt

@@ -1,26 +0,0 @@
-package eu.kanade.data.manga
-
-import eu.kanade.domain.manga.model.Manga
-
-val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long) -> Manga =
-    { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, _, initialized, viewer, chapterFlags, coverLastModified, dateAdded ->
-        Manga(
-            id = id,
-            source = source,
-            favorite = favorite,
-            lastUpdate = lastUpdate ?: 0,
-            dateAdded = dateAdded,
-            viewerFlags = viewer,
-            chapterFlags = chapterFlags,
-            coverLastModified = coverLastModified,
-            url = url,
-            title = title,
-            artist = artist,
-            author = author,
-            description = description,
-            genre = genre,
-            status = status,
-            thumbnailUrl = thumbnailUrl,
-            initialized = initialized,
-        )
-    }

+ 0 - 26
app/src/main/java/eu/kanade/domain/DomainModule.kt

@@ -1,26 +0,0 @@
-package eu.kanade.domain
-
-import eu.kanade.data.history.HistoryRepositoryImpl
-import eu.kanade.domain.history.interactor.DeleteHistoryTable
-import eu.kanade.domain.history.interactor.GetHistory
-import eu.kanade.domain.history.interactor.GetNextChapterForManga
-import eu.kanade.domain.history.interactor.RemoveHistoryById
-import eu.kanade.domain.history.interactor.RemoveHistoryByMangaId
-import eu.kanade.domain.history.repository.HistoryRepository
-import uy.kohesive.injekt.api.InjektModule
-import uy.kohesive.injekt.api.InjektRegistrar
-import uy.kohesive.injekt.api.addFactory
-import uy.kohesive.injekt.api.addSingletonFactory
-import uy.kohesive.injekt.api.get
-
-class DomainModule : InjektModule {
-
-    override fun InjektRegistrar.registerInjectables() {
-        addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
-        addFactory { DeleteHistoryTable(get()) }
-        addFactory { GetHistory(get()) }
-        addFactory { GetNextChapterForManga(get()) }
-        addFactory { RemoveHistoryById(get()) }
-        addFactory { RemoveHistoryByMangaId(get()) }
-    }
-}

+ 0 - 16
app/src/main/java/eu/kanade/domain/chapter/model/Chapter.kt

@@ -1,16 +0,0 @@
-package eu.kanade.domain.chapter.model
-
-data class Chapter(
-    val id: Long,
-    val mangaId: Long,
-    val read: Boolean,
-    val bookmark: Boolean,
-    val lastPageRead: Long,
-    val dateFetch: Long,
-    val sourceOrder: Long,
-    val url: String,
-    val name: String,
-    val dateUpload: Long,
-    val chapterNumber: Float,
-    val scanlator: String?
-)

+ 0 - 12
app/src/main/java/eu/kanade/domain/history/interactor/DeleteHistoryTable.kt

@@ -1,12 +0,0 @@
-package eu.kanade.domain.history.interactor
-
-import eu.kanade.domain.history.repository.HistoryRepository
-
-class DeleteHistoryTable(
-    private val repository: HistoryRepository
-) {
-
-    suspend fun await(): Boolean {
-        return repository.deleteAllHistory()
-    }
-}

+ 0 - 21
app/src/main/java/eu/kanade/domain/history/interactor/GetHistory.kt

@@ -1,21 +0,0 @@
-package eu.kanade.domain.history.interactor
-
-import androidx.paging.Pager
-import androidx.paging.PagingConfig
-import androidx.paging.PagingData
-import eu.kanade.domain.history.model.HistoryWithRelations
-import eu.kanade.domain.history.repository.HistoryRepository
-import kotlinx.coroutines.flow.Flow
-
-class GetHistory(
-    private val repository: HistoryRepository
-) {
-
-    fun subscribe(query: String): Flow<PagingData<HistoryWithRelations>> {
-        return Pager(
-            PagingConfig(pageSize = 25)
-        ) {
-            repository.getHistory(query)
-        }.flow
-    }
-}

+ 0 - 13
app/src/main/java/eu/kanade/domain/history/interactor/GetNextChapterForManga.kt

@@ -1,13 +0,0 @@
-package eu.kanade.domain.history.interactor
-
-import eu.kanade.domain.chapter.model.Chapter
-import eu.kanade.domain.history.repository.HistoryRepository
-
-class GetNextChapterForManga(
-    private val repository: HistoryRepository
-) {
-
-    suspend fun await(mangaId: Long, chapterId: Long): Chapter? {
-        return repository.getNextChapterForManga(mangaId, chapterId)
-    }
-}

+ 0 - 13
app/src/main/java/eu/kanade/domain/history/interactor/RemoveHistoryById.kt

@@ -1,13 +0,0 @@
-package eu.kanade.domain.history.interactor
-
-import eu.kanade.domain.history.model.HistoryWithRelations
-import eu.kanade.domain.history.repository.HistoryRepository
-
-class RemoveHistoryById(
-    private val repository: HistoryRepository
-) {
-
-    suspend fun await(history: HistoryWithRelations) {
-        repository.resetHistory(history.id)
-    }
-}

+ 0 - 12
app/src/main/java/eu/kanade/domain/history/interactor/RemoveHistoryByMangaId.kt

@@ -1,12 +0,0 @@
-package eu.kanade.domain.history.interactor
-
-import eu.kanade.domain.history.repository.HistoryRepository
-
-class RemoveHistoryByMangaId(
-    private val repository: HistoryRepository
-) {
-
-    suspend fun await(mangaId: Long) {
-        repository.resetHistoryByMangaId(mangaId)
-    }
-}

+ 0 - 9
app/src/main/java/eu/kanade/domain/history/model/History.kt

@@ -1,9 +0,0 @@
-package eu.kanade.domain.history.model
-
-import java.util.*
-
-data class History(
-    val id: Long?,
-    val chapterId: Long,
-    val readAt: Date?
-)

+ 0 - 13
app/src/main/java/eu/kanade/domain/history/model/HistoryWithRelations.kt

@@ -1,13 +0,0 @@
-package eu.kanade.domain.history.model
-
-import java.util.*
-
-data class HistoryWithRelations(
-    val id: Long,
-    val chapterId: Long,
-    val mangaId: Long,
-    val title: String,
-    val thumbnailUrl: String,
-    val chapterNumber: Float,
-    val readAt: Date?
-)

+ 0 - 18
app/src/main/java/eu/kanade/domain/history/repository/HistoryRepository.kt

@@ -1,18 +0,0 @@
-package eu.kanade.domain.history.repository
-
-import androidx.paging.PagingSource
-import eu.kanade.domain.chapter.model.Chapter
-import eu.kanade.domain.history.model.HistoryWithRelations
-
-interface HistoryRepository {
-
-    fun getHistory(query: String): PagingSource<Long, HistoryWithRelations>
-
-    suspend fun getNextChapterForManga(mangaId: Long, chapterId: Long): Chapter?
-
-    suspend fun resetHistory(historyId: Long)
-
-    suspend fun resetHistoryByMangaId(mangaId: Long)
-
-    suspend fun deleteAllHistory(): Boolean
-}

+ 0 - 36
app/src/main/java/eu/kanade/domain/manga/model/Manga.kt

@@ -1,36 +0,0 @@
-package eu.kanade.domain.manga.model
-
-data class Manga(
-    val id: Long,
-    val source: Long,
-    val favorite: Boolean,
-    val lastUpdate: Long,
-    val dateAdded: Long,
-    val viewerFlags: Long,
-    val chapterFlags: Long,
-    val coverLastModified: Long,
-    val url: String,
-    val title: String,
-    val artist: String?,
-    val author: String?,
-    val description: String?,
-    val genre: List<String>?,
-    val status: Long,
-    val thumbnailUrl: String?,
-    val initialized: Boolean
-) {
-
-    val sorting: Long
-        get() = chapterFlags and CHAPTER_SORTING_MASK
-
-    companion object {
-
-        // Generic filter that does not filter anything
-        const val SHOW_ALL = 0x00000000L
-
-        const val CHAPTER_SORTING_SOURCE = 0x00000000L
-        const val CHAPTER_SORTING_NUMBER = 0x00000100L
-        const val CHAPTER_SORTING_UPLOAD_DATE = 0x00000200L
-        const val CHAPTER_SORTING_MASK = 0x00000300L
-    }
-}

+ 0 - 49
app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt

@@ -1,49 +0,0 @@
-package eu.kanade.presentation.components
-
-import android.view.ViewGroup
-import androidx.annotation.StringRes
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.viewinterop.AndroidView
-import eu.kanade.tachiyomi.widget.EmptyView
-
-@Composable
-fun EmptyScreen(
-    @StringRes textResource: Int,
-    actions: List<EmptyView.Action>? = null,
-) {
-    EmptyScreen(
-        message = stringResource(id = textResource),
-        actions = actions,
-    )
-}
-
-@Composable
-fun EmptyScreen(
-    message: String,
-    actions: List<EmptyView.Action>? = null,
-) {
-    Box(
-        modifier = Modifier
-            .fillMaxSize()
-    ) {
-        AndroidView(
-            factory = { context ->
-                EmptyView(context).apply {
-                    layoutParams = ViewGroup.LayoutParams(
-                        ViewGroup.LayoutParams.WRAP_CONTENT,
-                        ViewGroup.LayoutParams.WRAP_CONTENT,
-                    )
-                }
-            },
-            modifier = Modifier
-                .align(Alignment.Center),
-        ) { view ->
-            view.show(message, actions)
-        }
-    }
-}

+ 0 - 39
app/src/main/java/eu/kanade/presentation/components/MangaCover.kt

@@ -1,39 +0,0 @@
-package eu.kanade.presentation.components
-
-import androidx.compose.foundation.layout.aspectRatio
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.painter.ColorPainter
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.unit.dp
-import coil.compose.AsyncImage
-
-enum class MangaCoverAspect(val ratio: Float) {
-    SQUARE(1f / 1f),
-    COVER(2f / 3f)
-}
-
-@Composable
-fun MangaCover(
-    modifier: Modifier = Modifier,
-    data: String?,
-    aspect: MangaCoverAspect,
-    contentDescription: String = "",
-    shape: Shape = RoundedCornerShape(4.dp)
-) {
-    AsyncImage(
-        model = data,
-        placeholder = ColorPainter(CoverPlaceholderColor),
-        contentDescription = contentDescription,
-        modifier = modifier
-            .aspectRatio(aspect.ratio)
-            .clip(shape),
-        contentScale = ContentScale.Crop
-    )
-}
-
-private val CoverPlaceholderColor = Color(0x1F888888)

+ 0 - 297
app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt

@@ -1,297 +0,0 @@
-package eu.kanade.presentation.history
-
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.navigationBarsPadding
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.foundation.selection.toggleable
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.PlayArrow
-import androidx.compose.material.icons.outlined.Delete
-import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Checkbox
-import androidx.compose.material3.CircularProgressIndicator
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
-import androidx.compose.ui.input.nestedscroll.nestedScroll
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.dp
-import androidx.paging.compose.LazyPagingItems
-import androidx.paging.compose.collectAsLazyPagingItems
-import androidx.paging.compose.items
-import eu.kanade.domain.history.model.HistoryWithRelations
-import eu.kanade.presentation.components.EmptyScreen
-import eu.kanade.presentation.components.MangaCover
-import eu.kanade.presentation.components.MangaCoverAspect
-import eu.kanade.presentation.util.horizontalPadding
-import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.preference.PreferencesHelper
-import eu.kanade.tachiyomi.ui.recent.history.HistoryPresenter
-import eu.kanade.tachiyomi.ui.recent.history.UiModel
-import eu.kanade.tachiyomi.util.lang.toRelativeString
-import eu.kanade.tachiyomi.util.lang.toTimestampString
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-import java.text.DateFormat
-import java.text.DecimalFormat
-import java.text.DecimalFormatSymbols
-import java.util.Date
-
-@Composable
-fun HistoryScreen(
-    composeView: ComposeView,
-    presenter: HistoryPresenter,
-    onClickItem: (HistoryWithRelations) -> Unit,
-    onClickResume: (HistoryWithRelations) -> Unit,
-    onClickDelete: (HistoryWithRelations, Boolean) -> Unit,
-) {
-    val nestedScrollInterop = rememberNestedScrollInteropConnection(composeView)
-    val state by presenter.state.collectAsState()
-    val history = state.list?.collectAsLazyPagingItems()
-    when {
-        history == null -> {
-            CircularProgressIndicator()
-        }
-        history.itemCount == 0 -> {
-            EmptyScreen(
-                textResource = R.string.information_no_recent_manga
-            )
-        }
-        else -> {
-            HistoryContent(
-                nestedScroll = nestedScrollInterop,
-                history = history,
-                onClickItem = onClickItem,
-                onClickResume = onClickResume,
-                onClickDelete = onClickDelete,
-            )
-        }
-    }
-}
-
-@Composable
-fun HistoryContent(
-    history: LazyPagingItems<UiModel>,
-    onClickItem: (HistoryWithRelations) -> Unit,
-    onClickResume: (HistoryWithRelations) -> Unit,
-    onClickDelete: (HistoryWithRelations, Boolean) -> Unit,
-    preferences: PreferencesHelper = Injekt.get(),
-    nestedScroll: NestedScrollConnection
-) {
-    val relativeTime: Int = remember { preferences.relativeTime().get() }
-    val dateFormat: DateFormat = remember { preferences.dateFormat() }
-
-    val (removeState, setRemoveState) = remember { mutableStateOf<HistoryWithRelations?>(null) }
-
-    val scrollState = rememberLazyListState()
-    LazyColumn(
-        modifier = Modifier
-            .nestedScroll(nestedScroll),
-        state = scrollState,
-    ) {
-        items(history) { item ->
-            when (item) {
-                is UiModel.Header -> {
-                    HistoryHeader(
-                        modifier = Modifier
-                            .animateItemPlacement(),
-                        date = item.date,
-                        relativeTime = relativeTime,
-                        dateFormat = dateFormat
-                    )
-                }
-                is UiModel.Item -> {
-                    val value = item.item
-                    HistoryItem(
-                        modifier = Modifier.animateItemPlacement(),
-                        history = value,
-                        onClickItem = { onClickItem(value) },
-                        onClickResume = { onClickResume(value) },
-                        onClickDelete = { setRemoveState(value) },
-                    )
-                }
-                null -> {}
-            }
-        }
-        item {
-            Spacer(Modifier.navigationBarsPadding())
-        }
-    }
-
-    if (removeState != null) {
-        RemoveHistoryDialog(
-            onPositive = { all ->
-                onClickDelete(removeState, all)
-                setRemoveState(null)
-            },
-            onNegative = { setRemoveState(null) }
-        )
-    }
-}
-
-@Composable
-fun HistoryHeader(
-    modifier: Modifier = Modifier,
-    date: Date,
-    relativeTime: Int,
-    dateFormat: DateFormat,
-) {
-    Text(
-        modifier = modifier
-            .padding(horizontal = horizontalPadding, vertical = 8.dp),
-        text = date.toRelativeString(
-            LocalContext.current,
-            relativeTime,
-            dateFormat
-        ),
-        style = MaterialTheme.typography.bodyMedium.copy(
-            color = MaterialTheme.colorScheme.onSurfaceVariant,
-            fontWeight = FontWeight.SemiBold,
-        )
-    )
-}
-
-@Composable
-fun HistoryItem(
-    modifier: Modifier = Modifier,
-    history: HistoryWithRelations,
-    onClickItem: () -> Unit,
-    onClickResume: () -> Unit,
-    onClickDelete: () -> Unit,
-) {
-    Row(
-        modifier = modifier
-            .clickable(onClick = onClickItem)
-            .height(96.dp)
-            .padding(horizontal = horizontalPadding, vertical = 8.dp),
-        verticalAlignment = Alignment.CenterVertically,
-    ) {
-        MangaCover(
-            modifier = Modifier.fillMaxHeight(),
-            data = history.thumbnailUrl,
-            aspect = MangaCoverAspect.COVER
-        )
-        Column(
-            modifier = Modifier
-                .weight(1f)
-                .padding(start = horizontalPadding, end = 8.dp),
-        ) {
-            val textStyle = MaterialTheme.typography.bodyMedium.copy(
-                color = MaterialTheme.colorScheme.onSurface,
-            )
-            Text(
-                text = history.title,
-                maxLines = 2,
-                overflow = TextOverflow.Ellipsis,
-                style = textStyle.copy(fontWeight = FontWeight.SemiBold)
-            )
-            Row {
-                Text(
-                    text = if (history.chapterNumber > -1) {
-                        stringResource(
-                            R.string.recent_manga_time,
-                            chapterFormatter.format(history.chapterNumber),
-                            history.readAt?.toTimestampString() ?: "",
-                        )
-                    } else {
-                        history.readAt?.toTimestampString() ?: ""
-                    },
-                    modifier = Modifier.padding(top = 4.dp),
-                    style = textStyle
-                )
-            }
-        }
-        IconButton(onClick = onClickDelete) {
-            Icon(
-                imageVector = Icons.Outlined.Delete,
-                contentDescription = stringResource(id = R.string.action_delete),
-                tint = MaterialTheme.colorScheme.onSurface,
-            )
-        }
-        IconButton(onClick = onClickResume) {
-            Icon(
-                imageVector = Icons.Filled.PlayArrow,
-                contentDescription = stringResource(id = R.string.action_resume),
-                tint = MaterialTheme.colorScheme.onSurface,
-            )
-        }
-    }
-}
-
-@Composable
-fun RemoveHistoryDialog(
-    onPositive: (Boolean) -> Unit,
-    onNegative: () -> Unit
-) {
-    val (removeEverything, removeEverythingState) = remember { mutableStateOf(false) }
-
-    AlertDialog(
-        title = {
-            Text(text = stringResource(id = R.string.action_remove))
-        },
-        text = {
-            Column {
-                Text(text = stringResource(id = R.string.dialog_with_checkbox_remove_description))
-                Row(
-                    modifier = Modifier
-                        .padding(top = 16.dp)
-                        .toggleable(
-                            interactionSource = remember { MutableInteractionSource() },
-                            indication = null,
-                            value = removeEverything,
-                            onValueChange = removeEverythingState
-                        ),
-                    verticalAlignment = Alignment.CenterVertically
-                ) {
-                    Checkbox(
-                        checked = removeEverything,
-                        onCheckedChange = null,
-                    )
-                    Text(
-                        modifier = Modifier.padding(start = 4.dp),
-                        text = stringResource(id = R.string.dialog_with_checkbox_reset)
-                    )
-                }
-            }
-        },
-        onDismissRequest = onNegative,
-        confirmButton = {
-            TextButton(onClick = { onPositive(removeEverything) }) {
-                Text(text = stringResource(id = R.string.action_remove))
-            }
-        },
-        dismissButton = {
-            TextButton(onClick = onNegative) {
-                Text(text = stringResource(id = R.string.action_cancel))
-            }
-        },
-    )
-}
-
-private val chapterFormatter = DecimalFormat(
-    "#.###",
-    DecimalFormatSymbols().apply { decimalSeparator = '.' },
-)

+ 0 - 20
app/src/main/java/eu/kanade/presentation/theme/TachiyomiTheme.kt

@@ -1,20 +0,0 @@
-package eu.kanade.presentation.theme
-
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.LocalContext
-import com.google.android.material.composethemeadapter3.createMdc3Theme
-
-@Composable
-fun TachiyomiTheme(content: @Composable () -> Unit) {
-    val context = LocalContext.current
-    val (colorScheme, typography) = createMdc3Theme(
-        context = context
-    )
-
-    MaterialTheme(
-        colorScheme = colorScheme!!,
-        typography = typography!!,
-        content = content
-    )
-}

+ 0 - 5
app/src/main/java/eu/kanade/presentation/util/Constants.kt

@@ -1,5 +0,0 @@
-package eu.kanade.presentation.util
-
-import androidx.compose.ui.unit.dp
-
-val horizontalPadding = 16.dp

+ 0 - 5
app/src/main/java/eu/kanade/presentation/util/LazyListState.kt

@@ -1,5 +0,0 @@
-package eu.kanade.presentation.util
-
-import androidx.compose.foundation.lazy.LazyListState
-
-fun LazyListState.isScrolledToEnd() = layoutInfo.visibleItemsInfo.lastOrNull()?.index == layoutInfo.totalItemsCount - 1

+ 0 - 2
app/src/main/java/eu/kanade/tachiyomi/App.kt

@@ -24,7 +24,6 @@ import coil.decode.GifDecoder
 import coil.decode.ImageDecoderDecoder
 import coil.disk.DiskCache
 import coil.util.DebugLogger
-import eu.kanade.domain.DomainModule
 import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
 import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer
 import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder
@@ -76,7 +75,6 @@ open class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
         }
 
         Injekt.importModule(AppModule(this))
-        Injekt.importModule(DomainModule())
 
         setupAcra()
         setupNotificationChannels()

+ 1 - 38
app/src/main/java/eu/kanade/tachiyomi/AppModule.kt

@@ -2,18 +2,9 @@ package eu.kanade.tachiyomi
 
 import android.app.Application
 import androidx.core.content.ContextCompat
-import com.squareup.sqldelight.android.AndroidSqliteDriver
-import com.squareup.sqldelight.db.SqlDriver
-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.listOfStringsAdapter
 import eu.kanade.tachiyomi.data.cache.ChapterCache
 import eu.kanade.tachiyomi.data.cache.CoverCache
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
-import eu.kanade.tachiyomi.data.database.DbOpenCallback
 import eu.kanade.tachiyomi.data.download.DownloadManager
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.data.saver.ImageSaver
@@ -34,37 +25,11 @@ class AppModule(val app: Application) : InjektModule {
     override fun InjektRegistrar.registerInjectables() {
         addSingleton(app)
 
-        addSingletonFactory { DbOpenCallback() }
-
-        addSingletonFactory<SqlDriver> {
-            AndroidSqliteDriver(
-                schema = Database.Schema,
-                context = app,
-                name = DbOpenCallback.DATABASE_NAME,
-                callback = get<DbOpenCallback>()
-            )
-        }
-
-        addSingletonFactory {
-            Database(
-                driver = get(),
-                historyAdapter = History.Adapter(
-                    history_last_readAdapter = dateAdapter,
-                    history_time_readAdapter = dateAdapter
-                ),
-                mangasAdapter = Mangas.Adapter(
-                    genreAdapter = listOfStringsAdapter
-                )
-            )
-        }
-
-        addSingletonFactory<DatabaseHandler> { AndroidDatabaseHandler(get(), get()) }
-
         addSingletonFactory { Json { ignoreUnknownKeys = true } }
 
         addSingletonFactory { PreferencesHelper(app) }
 
-        addSingletonFactory { DatabaseHelper(app, get()) }
+        addSingletonFactory { DatabaseHelper(app) }
 
         addSingletonFactory { ChapterCache(app) }
 
@@ -92,8 +57,6 @@ class AppModule(val app: Application) : InjektModule {
 
             get<SourceManager>()
 
-            get<Database>()
-
             get<DatabaseHelper>()
 
             get<DownloadManager>()

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt

@@ -299,7 +299,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
                 }
             }
         }
-        databaseHelper.upsertHistoryLastRead(historyToBeUpdated).executeAsBlocking()
+        databaseHelper.updateHistoryLastRead(historyToBeUpdated).executeAsBlocking()
     }
 
     /**

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt

@@ -168,7 +168,7 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
                 }
             }
         }
-        databaseHelper.upsertHistoryLastRead(historyToBeUpdated).executeAsBlocking()
+        databaseHelper.updateHistoryLastRead(historyToBeUpdated).executeAsBlocking()
     }
 
     /**

+ 2 - 5
app/src/main/java/eu/kanade/tachiyomi/data/database/DatabaseHelper.kt

@@ -26,15 +26,12 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
 /**
  * This class provides operations to manage the database through its interfaces.
  */
-open class DatabaseHelper(
-    context: Context,
-    callback: DbOpenCallback
-) :
+open class DatabaseHelper(context: Context) :
     MangaQueries, ChapterQueries, TrackQueries, CategoryQueries, MangaCategoryQueries, HistoryQueries {
 
     private val configuration = SupportSQLiteOpenHelper.Configuration.builder(context)
         .name(DbOpenCallback.DATABASE_NAME)
-        .callback(callback)
+        .callback(DbOpenCallback())
         .build()
 
     override val db = DefaultStorIOSQLite.builder()

+ 80 - 10
app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt

@@ -2,28 +2,98 @@ package eu.kanade.tachiyomi.data.database
 
 import androidx.sqlite.db.SupportSQLiteDatabase
 import androidx.sqlite.db.SupportSQLiteOpenHelper
-import com.squareup.sqldelight.android.AndroidSqliteDriver
-import eu.kanade.tachiyomi.Database
+import eu.kanade.tachiyomi.data.database.tables.CategoryTable
+import eu.kanade.tachiyomi.data.database.tables.ChapterTable
+import eu.kanade.tachiyomi.data.database.tables.HistoryTable
+import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable
+import eu.kanade.tachiyomi.data.database.tables.MangaTable
+import eu.kanade.tachiyomi.data.database.tables.TrackTable
 
-class DbOpenCallback : SupportSQLiteOpenHelper.Callback(Database.Schema.version) {
+class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
 
     companion object {
         /**
          * Name of the database file.
          */
         const val DATABASE_NAME = "tachiyomi.db"
+
+        /**
+         * Version of the database.
+         */
+        const val DATABASE_VERSION = 14
     }
 
-    override fun onCreate(db: SupportSQLiteDatabase) {
-        Database.Schema.create(AndroidSqliteDriver(database = db, cacheSize = 1))
+    override fun onCreate(db: SupportSQLiteDatabase) = with(db) {
+        execSQL(MangaTable.createTableQuery)
+        execSQL(ChapterTable.createTableQuery)
+        execSQL(TrackTable.createTableQuery)
+        execSQL(CategoryTable.createTableQuery)
+        execSQL(MangaCategoryTable.createTableQuery)
+        execSQL(HistoryTable.createTableQuery)
+
+        // DB indexes
+        execSQL(MangaTable.createUrlIndexQuery)
+        execSQL(MangaTable.createLibraryIndexQuery)
+        execSQL(ChapterTable.createMangaIdIndexQuery)
+        execSQL(ChapterTable.createUnreadChaptersIndexQuery)
+        execSQL(HistoryTable.createChapterIdIndexQuery)
     }
 
     override fun onUpgrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) {
-        Database.Schema.migrate(
-            driver = AndroidSqliteDriver(database = db, cacheSize = 1),
-            oldVersion = oldVersion,
-            newVersion = newVersion
-        )
+        if (oldVersion < 2) {
+            db.execSQL(ChapterTable.sourceOrderUpdateQuery)
+
+            // Fix kissmanga covers after supporting cloudflare
+            db.execSQL(
+                """UPDATE mangas SET thumbnail_url =
+                    REPLACE(thumbnail_url, '93.174.95.110', 'kissmanga.com') WHERE source = 4""",
+            )
+        }
+        if (oldVersion < 3) {
+            // Initialize history tables
+            db.execSQL(HistoryTable.createTableQuery)
+            db.execSQL(HistoryTable.createChapterIdIndexQuery)
+        }
+        if (oldVersion < 4) {
+            db.execSQL(ChapterTable.bookmarkUpdateQuery)
+        }
+        if (oldVersion < 5) {
+            db.execSQL(ChapterTable.addScanlator)
+        }
+        if (oldVersion < 6) {
+            db.execSQL(TrackTable.addTrackingUrl)
+        }
+        if (oldVersion < 7) {
+            db.execSQL(TrackTable.addLibraryId)
+        }
+        if (oldVersion < 8) {
+            db.execSQL("DROP INDEX IF EXISTS mangas_favorite_index")
+            db.execSQL(MangaTable.createLibraryIndexQuery)
+            db.execSQL(ChapterTable.createUnreadChaptersIndexQuery)
+        }
+        if (oldVersion < 9) {
+            db.execSQL(TrackTable.addStartDate)
+            db.execSQL(TrackTable.addFinishDate)
+        }
+        if (oldVersion < 10) {
+            db.execSQL(MangaTable.addCoverLastModified)
+        }
+        if (oldVersion < 11) {
+            db.execSQL(MangaTable.addDateAdded)
+            db.execSQL(MangaTable.backfillDateAdded)
+        }
+        if (oldVersion < 12) {
+            db.execSQL(MangaTable.addNextUpdateCol)
+        }
+        if (oldVersion < 13) {
+            db.execSQL(TrackTable.renameTableToTemp)
+            db.execSQL(TrackTable.createTableQuery)
+            db.execSQL(TrackTable.insertFromTempTable)
+            db.execSQL(TrackTable.dropTempTable)
+        }
+        if (oldVersion < 14) {
+            db.execSQL(ChapterTable.fixDateUploadIfNeeded)
+        }
     }
 
     override fun onConfigure(db: SupportSQLiteDatabase) {

+ 34 - 6
app/src/main/java/eu/kanade/tachiyomi/data/database/queries/HistoryQueries.kt

@@ -4,11 +4,39 @@ import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
 import com.pushtorefresh.storio.sqlite.queries.RawQuery
 import eu.kanade.tachiyomi.data.database.DbProvider
 import eu.kanade.tachiyomi.data.database.models.History
-import eu.kanade.tachiyomi.data.database.resolvers.HistoryUpsertResolver
+import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
+import eu.kanade.tachiyomi.data.database.resolvers.HistoryLastReadPutResolver
+import eu.kanade.tachiyomi.data.database.resolvers.MangaChapterHistoryGetResolver
 import eu.kanade.tachiyomi.data.database.tables.HistoryTable
+import java.util.Date
 
 interface HistoryQueries : DbProvider {
 
+    /**
+     * Insert history into database
+     * @param history object containing history information
+     */
+    fun insertHistory(history: History) = db.put().`object`(history).prepare()
+
+    /**
+     * Returns history of recent manga containing last read chapter
+     * @param date recent date range
+     * @param limit the limit of manga to grab
+     * @param offset offset the db by
+     * @param search what to search in the db history
+     */
+    fun getRecentManga(date: Date, limit: Int = 25, offset: Int = 0, search: String = "") = db.get()
+        .listOfObjects(MangaChapterHistory::class.java)
+        .withQuery(
+            RawQuery.builder()
+                .query(getRecentMangasQuery(search))
+                .args(date.time, limit, offset)
+                .observesTables(HistoryTable.TABLE)
+                .build(),
+        )
+        .withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
+        .prepare()
+
     fun getHistoryByMangaId(mangaId: Long) = db.get()
         .listOfObjects(History::class.java)
         .withQuery(
@@ -36,9 +64,9 @@ interface HistoryQueries : DbProvider {
      * Inserts history object if not yet in database
      * @param history history object
      */
-    fun upsertHistoryLastRead(history: History) = db.put()
+    fun updateHistoryLastRead(history: History) = db.put()
         .`object`(history)
-        .withPutResolver(HistoryUpsertResolver())
+        .withPutResolver(HistoryLastReadPutResolver())
         .prepare()
 
     /**
@@ -46,12 +74,12 @@ interface HistoryQueries : DbProvider {
      * Inserts history object if not yet in database
      * @param historyList history object list
      */
-    fun upsertHistoryLastRead(historyList: List<History>) = db.put()
+    fun updateHistoryLastRead(historyList: List<History>) = db.put()
         .objects(historyList)
-        .withPutResolver(HistoryUpsertResolver())
+        .withPutResolver(HistoryLastReadPutResolver())
         .prepare()
 
-    fun dropHistoryTable() = db.delete()
+    fun deleteHistory() = db.delete()
         .byQuery(
             DeleteQuery.builder()
                 .table(HistoryTable.TABLE)

+ 1 - 2
app/src/main/java/eu/kanade/tachiyomi/data/database/queries/RawQueries.kt

@@ -70,8 +70,7 @@ fun getRecentMangasQuery(search: String = "") =
     SELECT ${Chapter.TABLE}.${Chapter.COL_MANGA_ID},${Chapter.TABLE}.${Chapter.COL_ID} as ${History.COL_CHAPTER_ID}, MAX(${History.TABLE}.${History.COL_LAST_READ}) as ${History.COL_LAST_READ}
     FROM ${Chapter.TABLE} JOIN ${History.TABLE}
     ON ${Chapter.TABLE}.${Chapter.COL_ID} = ${History.TABLE}.${History.COL_CHAPTER_ID}
-    GROUP BY ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
-    ) AS max_last_read
+    GROUP BY ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}) AS max_last_read
     ON ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = max_last_read.${Chapter.COL_MANGA_ID}
     WHERE ${History.TABLE}.${History.COL_LAST_READ} > ?
     AND max_last_read.${History.COL_CHAPTER_ID} = ${History.TABLE}.${History.COL_CHAPTER_ID}

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/HistoryUpsertResolver.kt → app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/HistoryLastReadPutResolver.kt

@@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.data.database.mappers.HistoryPutResolver
 import eu.kanade.tachiyomi.data.database.models.History
 import eu.kanade.tachiyomi.data.database.tables.HistoryTable
 
-class HistoryUpsertResolver : HistoryPutResolver() {
+class HistoryLastReadPutResolver : HistoryPutResolver() {
 
     /**
      * Updates last_read time of chapter

+ 9 - 0
app/src/main/java/eu/kanade/tachiyomi/data/database/tables/CategoryTable.kt

@@ -11,4 +11,13 @@ object CategoryTable {
     const val COL_ORDER = "sort"
 
     const val COL_FLAGS = "flags"
+
+    val createTableQuery: String
+        get() =
+            """CREATE TABLE $TABLE(
+            $COL_ID INTEGER NOT NULL PRIMARY KEY,
+            $COL_NAME TEXT NOT NULL,
+            $COL_ORDER INTEGER NOT NULL,
+            $COL_FLAGS INTEGER NOT NULL
+            )"""
 }

+ 38 - 0
app/src/main/java/eu/kanade/tachiyomi/data/database/tables/ChapterTable.kt

@@ -27,4 +27,42 @@ object ChapterTable {
     const val COL_CHAPTER_NUMBER = "chapter_number"
 
     const val COL_SOURCE_ORDER = "source_order"
+
+    val createTableQuery: String
+        get() =
+            """CREATE TABLE $TABLE(
+            $COL_ID INTEGER NOT NULL PRIMARY KEY,
+            $COL_MANGA_ID INTEGER NOT NULL,
+            $COL_URL TEXT NOT NULL,
+            $COL_NAME TEXT NOT NULL,
+            $COL_SCANLATOR TEXT,
+            $COL_READ BOOLEAN NOT NULL,
+            $COL_BOOKMARK BOOLEAN NOT NULL,
+            $COL_LAST_PAGE_READ INT NOT NULL,
+            $COL_CHAPTER_NUMBER FLOAT NOT NULL,
+            $COL_SOURCE_ORDER INTEGER NOT NULL,
+            $COL_DATE_FETCH LONG NOT NULL,
+            $COL_DATE_UPLOAD LONG NOT NULL,
+            FOREIGN KEY($COL_MANGA_ID) REFERENCES ${MangaTable.TABLE} (${MangaTable.COL_ID})
+            ON DELETE CASCADE
+            )"""
+
+    val createMangaIdIndexQuery: String
+        get() = "CREATE INDEX ${TABLE}_${COL_MANGA_ID}_index ON $TABLE($COL_MANGA_ID)"
+
+    val createUnreadChaptersIndexQuery: String
+        get() = "CREATE INDEX ${TABLE}_unread_by_manga_index ON $TABLE($COL_MANGA_ID, $COL_READ) " +
+            "WHERE $COL_READ = 0"
+
+    val sourceOrderUpdateQuery: String
+        get() = "ALTER TABLE $TABLE ADD COLUMN $COL_SOURCE_ORDER INTEGER DEFAULT 0"
+
+    val bookmarkUpdateQuery: String
+        get() = "ALTER TABLE $TABLE ADD COLUMN $COL_BOOKMARK BOOLEAN DEFAULT FALSE"
+
+    val addScanlator: String
+        get() = "ALTER TABLE $TABLE ADD COLUMN $COL_SCANLATOR TEXT DEFAULT NULL"
+
+    val fixDateUploadIfNeeded: String
+        get() = "UPDATE $TABLE SET $COL_DATE_UPLOAD = $COL_DATE_FETCH WHERE $COL_DATE_UPLOAD = 0"
 }

+ 20 - 0
app/src/main/java/eu/kanade/tachiyomi/data/database/tables/HistoryTable.kt

@@ -26,4 +26,24 @@ object HistoryTable {
      * Time read column name
      */
     const val COL_TIME_READ = "${TABLE}_time_read"
+
+    /**
+     * query to create history table
+     */
+    val createTableQuery: String
+        get() =
+            """CREATE TABLE $TABLE(
+            $COL_ID INTEGER NOT NULL PRIMARY KEY,
+            $COL_CHAPTER_ID INTEGER NOT NULL UNIQUE,
+            $COL_LAST_READ LONG,
+            $COL_TIME_READ LONG,
+            FOREIGN KEY($COL_CHAPTER_ID) REFERENCES ${ChapterTable.TABLE} (${ChapterTable.COL_ID})
+            ON DELETE CASCADE
+            )"""
+
+    /**
+     * query to index history chapter id
+     */
+    val createChapterIdIndexQuery: String
+        get() = "CREATE INDEX ${TABLE}_${COL_CHAPTER_ID}_index ON $TABLE($COL_CHAPTER_ID)"
 }

+ 12 - 0
app/src/main/java/eu/kanade/tachiyomi/data/database/tables/MangaCategoryTable.kt

@@ -9,4 +9,16 @@ object MangaCategoryTable {
     const val COL_MANGA_ID = "manga_id"
 
     const val COL_CATEGORY_ID = "category_id"
+
+    val createTableQuery: String
+        get() =
+            """CREATE TABLE $TABLE(
+            $COL_ID INTEGER NOT NULL PRIMARY KEY,
+            $COL_MANGA_ID INTEGER NOT NULL,
+            $COL_CATEGORY_ID INTEGER NOT NULL,
+            FOREIGN KEY($COL_CATEGORY_ID) REFERENCES ${CategoryTable.TABLE} (${CategoryTable.COL_ID})
+            ON DELETE CASCADE,
+            FOREIGN KEY($COL_MANGA_ID) REFERENCES ${MangaTable.TABLE} (${MangaTable.COL_ID})
+            ON DELETE CASCADE
+            )"""
 }

+ 49 - 0
app/src/main/java/eu/kanade/tachiyomi/data/database/tables/MangaTable.kt

@@ -47,4 +47,53 @@ object MangaTable {
     const val COMPUTED_COL_UNREAD_COUNT = "unread_count"
 
     const val COMPUTED_COL_READ_COUNT = "read_count"
+
+    val createTableQuery: String
+        get() =
+            """CREATE TABLE $TABLE(
+            $COL_ID INTEGER NOT NULL PRIMARY KEY,
+            $COL_SOURCE INTEGER NOT NULL,
+            $COL_URL TEXT NOT NULL,
+            $COL_ARTIST TEXT,
+            $COL_AUTHOR TEXT,
+            $COL_DESCRIPTION TEXT,
+            $COL_GENRE TEXT,
+            $COL_TITLE TEXT NOT NULL,
+            $COL_STATUS INTEGER NOT NULL,
+            $COL_THUMBNAIL_URL TEXT,
+            $COL_FAVORITE INTEGER NOT NULL,
+            $COL_LAST_UPDATE LONG,
+            $COL_NEXT_UPDATE LONG,
+            $COL_INITIALIZED BOOLEAN NOT NULL,
+            $COL_VIEWER INTEGER NOT NULL,
+            $COL_CHAPTER_FLAGS INTEGER NOT NULL,
+            $COL_COVER_LAST_MODIFIED LONG NOT NULL,
+            $COL_DATE_ADDED LONG NOT NULL
+            )"""
+
+    val createUrlIndexQuery: String
+        get() = "CREATE INDEX ${TABLE}_${COL_URL}_index ON $TABLE($COL_URL)"
+
+    val createLibraryIndexQuery: String
+        get() = "CREATE INDEX library_${COL_FAVORITE}_index ON $TABLE($COL_FAVORITE) " +
+            "WHERE $COL_FAVORITE = 1"
+
+    val addCoverLastModified: String
+        get() = "ALTER TABLE $TABLE ADD COLUMN $COL_COVER_LAST_MODIFIED LONG NOT NULL DEFAULT 0"
+
+    val addDateAdded: String
+        get() = "ALTER TABLE $TABLE ADD COLUMN $COL_DATE_ADDED LONG NOT NULL DEFAULT 0"
+
+    /**
+     * Used with addDateAdded to populate it with the oldest chapter fetch date.
+     */
+    val backfillDateAdded: String
+        get() = "UPDATE $TABLE SET $COL_DATE_ADDED = " +
+            "(SELECT MIN(${ChapterTable.COL_DATE_FETCH}) " +
+            "FROM $TABLE INNER JOIN ${ChapterTable.TABLE} " +
+            "ON $TABLE.$COL_ID = ${ChapterTable.TABLE}.${ChapterTable.COL_MANGA_ID} " +
+            "GROUP BY $TABLE.$COL_ID)"
+
+    val addNextUpdateCol: String
+        get() = "ALTER TABLE $TABLE ADD COLUMN $COL_NEXT_UPDATE LONG DEFAULT 0"
 }

+ 40 - 0
app/src/main/java/eu/kanade/tachiyomi/data/database/tables/TrackTable.kt

@@ -30,6 +30,43 @@ object TrackTable {
 
     const val COL_FINISH_DATE = "finish_date"
 
+    val createTableQuery: String
+        get() =
+            """CREATE TABLE $TABLE(
+            $COL_ID INTEGER NOT NULL PRIMARY KEY,
+            $COL_MANGA_ID INTEGER NOT NULL,
+            $COL_SYNC_ID INTEGER NOT NULL,
+            $COL_MEDIA_ID INTEGER NOT NULL,
+            $COL_LIBRARY_ID INTEGER,
+            $COL_TITLE TEXT NOT NULL,
+            $COL_LAST_CHAPTER_READ REAL NOT NULL,
+            $COL_TOTAL_CHAPTERS INTEGER NOT NULL,
+            $COL_STATUS INTEGER NOT NULL,
+            $COL_SCORE FLOAT NOT NULL,
+            $COL_TRACKING_URL TEXT NOT NULL,
+            $COL_START_DATE LONG NOT NULL,
+            $COL_FINISH_DATE LONG NOT NULL,
+            UNIQUE ($COL_MANGA_ID, $COL_SYNC_ID) ON CONFLICT REPLACE,
+            FOREIGN KEY($COL_MANGA_ID) REFERENCES ${MangaTable.TABLE} (${MangaTable.COL_ID})
+            ON DELETE CASCADE
+            )"""
+
+    val addTrackingUrl: String
+        get() = "ALTER TABLE $TABLE ADD COLUMN $COL_TRACKING_URL TEXT DEFAULT ''"
+
+    val addLibraryId: String
+        get() = "ALTER TABLE $TABLE ADD COLUMN $COL_LIBRARY_ID INTEGER NULL"
+
+    val addStartDate: String
+        get() = "ALTER TABLE $TABLE ADD COLUMN $COL_START_DATE LONG NOT NULL DEFAULT 0"
+
+    val addFinishDate: String
+        get() = "ALTER TABLE $TABLE ADD COLUMN $COL_FINISH_DATE LONG NOT NULL DEFAULT 0"
+
+    val renameTableToTemp: String
+        get() =
+            "ALTER TABLE $TABLE RENAME TO ${TABLE}_tmp"
+
     val insertFromTempTable: String
         get() =
             """
@@ -37,4 +74,7 @@ object TrackTable {
             |SELECT $COL_ID,$COL_MANGA_ID,$COL_SYNC_ID,$COL_MEDIA_ID,$COL_LIBRARY_ID,$COL_TITLE,$COL_LAST_CHAPTER_READ,$COL_TOTAL_CHAPTERS,$COL_STATUS,$COL_SCORE,$COL_TRACKING_URL,$COL_START_DATE,$COL_FINISH_DATE
             |FROM ${TABLE}_tmp
             """.trimMargin()
+
+    val dropTempTable: String
+        get() = "DROP TABLE ${TABLE}_tmp"
 }

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt

@@ -166,7 +166,7 @@ class NotificationReceiver : BroadcastReceiver() {
      * @param chapterId id of chapter
      */
     private fun openChapter(context: Context, mangaId: Long, chapterId: Long) {
-        val db = Injekt.get<DatabaseHelper>()
+        val db = DatabaseHelper(context)
         val manga = db.getManga(mangaId).executeAsBlocking()
         val chapter = db.getChapter(chapterId).executeAsBlocking()
         if (manga != null && chapter != null) {

+ 1 - 2
app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt

@@ -7,7 +7,6 @@ import androidx.core.content.edit
 import androidx.core.net.toUri
 import androidx.preference.PreferenceManager
 import com.fredporciuncula.flow.preferences.FlowSharedPreferences
-import eu.kanade.tachiyomi.BuildConfig
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Manga
 import eu.kanade.tachiyomi.data.track.TrackService
@@ -19,6 +18,7 @@ import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
 import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
 import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
 import eu.kanade.tachiyomi.util.system.DeviceUtil
+import eu.kanade.tachiyomi.util.system.isDevFlavor
 import eu.kanade.tachiyomi.widget.ExtendedNavigationView
 import java.io.File
 import java.text.DateFormat
@@ -26,7 +26,6 @@ import java.text.SimpleDateFormat
 import java.util.Locale
 import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
 import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values
-import eu.kanade.tachiyomi.util.system.isDevFlavor
 
 class PreferencesHelper(val context: Context) {
 

+ 0 - 26
app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ComposeController.kt

@@ -1,26 +0,0 @@
-package eu.kanade.tachiyomi.ui.base.controller
-
-import android.view.LayoutInflater
-import android.view.View
-import androidx.compose.runtime.Composable
-import eu.kanade.presentation.theme.TachiyomiTheme
-import eu.kanade.tachiyomi.databinding.ComposeControllerBinding
-import nucleus.presenter.Presenter
-
-abstract class ComposeController<P : Presenter<*>> : NucleusController<ComposeControllerBinding, P>() {
-
-    override fun createBinding(inflater: LayoutInflater): ComposeControllerBinding =
-        ComposeControllerBinding.inflate(inflater)
-
-    override fun onViewCreated(view: View) {
-        super.onViewCreated(view)
-
-        binding.root.setContent {
-            TachiyomiTheme {
-                ComposeContent()
-            }
-        }
-    }
-
-    @Composable abstract fun ComposeContent()
-}

+ 0 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt

@@ -35,7 +35,6 @@ import com.google.android.material.snackbar.Snackbar
 import dev.chrisbanes.insetter.applyInsetter
 import eu.davidea.flexibleadapter.FlexibleAdapter
 import eu.davidea.flexibleadapter.SelectableAdapter
-import eu.kanade.domain.history.model.HistoryWithRelations
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.cache.CoverCache
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
@@ -119,8 +118,6 @@ class MangaController :
     DownloadCustomChaptersDialog.Listener,
     DeleteChaptersDialog.Listener {
 
-    constructor(history: HistoryWithRelations) : this(history.mangaId)
-
     constructor(manga: Manga?, fromSource: Boolean = false) : super(
         bundleOf(
             MANGA_EXTRA to (manga?.id ?: 0),

+ 24 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaCoverImageView.kt

@@ -0,0 +1,24 @@
+package eu.kanade.tachiyomi.ui.manga.info
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.appcompat.widget.AppCompatImageView
+import kotlin.math.min
+
+/**
+ * A custom ImageView for holding a manga cover with:
+ * - width: min(maxWidth attr, 33% of parent width)
+ * - height: 2:3 width:height ratio
+ *
+ * Should be defined with a width of match_parent.
+ */
+class MangaCoverImageView(context: Context, attrs: AttributeSet?) : AppCompatImageView(context, attrs) {
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+
+        val width = min(maxWidth, measuredWidth / 3)
+        val height = width / 2 * 3
+        setMeasuredDimension(width, height)
+    }
+}

+ 3 - 8
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt

@@ -97,19 +97,14 @@ import kotlin.math.max
 class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
 
     companion object {
-
-        fun newIntent(context: Context, mangaId: Long?, chapterId: Long?): Intent {
+        fun newIntent(context: Context, manga: Manga, chapter: Chapter): Intent {
             return Intent(context, ReaderActivity::class.java).apply {
-                putExtra("manga", mangaId)
-                putExtra("chapter", chapterId)
+                putExtra("manga", manga.id)
+                putExtra("chapter", chapter.id)
                 addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
             }
         }
 
-        fun newIntent(context: Context, manga: Manga, chapter: Chapter): Intent {
-            return newIntent(context, manga.id, chapter.id)
-        }
-
         private const val ENABLED_BUTTON_IMAGE_ALPHA = 255
         private const val DISABLED_BUTTON_IMAGE_ALPHA = 64
 

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt

@@ -449,7 +449,7 @@ class ReaderPresenter(
     private fun saveChapterHistory(chapter: ReaderChapter) {
         if (!incognitoMode) {
             val history = History.create(chapter.chapter).apply { last_read = Date().time }
-            db.upsertHistoryLastRead(history).asRxCompletable()
+            db.updateHistoryLastRead(history).asRxCompletable()
                 .onErrorComplete()
                 .subscribeOn(Schedulers.io())
                 .subscribe()

+ 0 - 21
app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/ClearHistoryDialogController.kt

@@ -1,21 +0,0 @@
-package eu.kanade.tachiyomi.ui.recent.history
-
-import android.app.Dialog
-import android.os.Bundle
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.ui.base.controller.DialogController
-
-class ClearHistoryDialogController : DialogController() {
-    override fun onCreateDialog(savedViewState: Bundle?): Dialog {
-        return MaterialAlertDialogBuilder(activity!!)
-            .setMessage(R.string.clear_history_confirmation)
-            .setPositiveButton(android.R.string.ok) { _, _ ->
-                (targetController as? HistoryController)
-                    ?.presenter
-                    ?.deleteAllHistory()
-            }
-            .setNegativeButton(android.R.string.cancel, null)
-            .create()
-    }
-}

+ 51 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryAdapter.kt

@@ -0,0 +1,51 @@
+package eu.kanade.tachiyomi.ui.recent.history
+
+import eu.davidea.flexibleadapter.FlexibleAdapter
+import eu.davidea.flexibleadapter.items.IFlexible
+import eu.kanade.tachiyomi.source.SourceManager
+import uy.kohesive.injekt.injectLazy
+import java.text.DecimalFormat
+import java.text.DecimalFormatSymbols
+
+/**
+ * Adapter of HistoryHolder.
+ * Connection between Fragment and Holder
+ * Holder updates should be called from here.
+ *
+ * @param controller a HistoryController object
+ * @constructor creates an instance of the adapter.
+ */
+class HistoryAdapter(controller: HistoryController) :
+    FlexibleAdapter<IFlexible<*>>(null, controller, true) {
+
+    val sourceManager: SourceManager by injectLazy()
+
+    val resumeClickListener: OnResumeClickListener = controller
+    val removeClickListener: OnRemoveClickListener = controller
+    val itemClickListener: OnItemClickListener = controller
+
+    /**
+     * DecimalFormat used to display correct chapter number
+     */
+    val decimalFormat = DecimalFormat(
+        "#.###",
+        DecimalFormatSymbols()
+            .apply { decimalSeparator = '.' },
+    )
+
+    init {
+        setDisplayHeadersAtStartUp(true)
+    }
+
+    interface OnResumeClickListener {
+        fun onResumeClick(position: Int)
+    }
+
+    interface OnRemoveClickListener {
+        fun onRemoveClick(position: Int)
+    }
+
+    interface OnItemClickListener {
+        fun onItemClick(position: Int)
+    }
+}

+ 197 - 44
app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryController.kt

@@ -1,53 +1,193 @@
 package eu.kanade.tachiyomi.ui.recent.history
 
+import android.app.Dialog
+import android.os.Bundle
+import android.view.LayoutInflater
 import android.view.Menu
 import android.view.MenuInflater
 import android.view.MenuItem
+import android.view.View
 import androidx.appcompat.widget.SearchView
-import androidx.compose.runtime.Composable
-import eu.kanade.domain.chapter.model.Chapter
-import eu.kanade.presentation.history.HistoryScreen
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import dev.chrisbanes.insetter.applyInsetter
+import eu.davidea.flexibleadapter.FlexibleAdapter
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.ui.base.controller.ComposeController
+import eu.kanade.tachiyomi.data.backup.BackupRestoreService
+import eu.kanade.tachiyomi.data.database.DatabaseHelper
+import eu.kanade.tachiyomi.data.database.models.History
+import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.databinding.HistoryControllerBinding
+import eu.kanade.tachiyomi.ui.base.controller.DialogController
+import eu.kanade.tachiyomi.ui.base.controller.NucleusController
 import eu.kanade.tachiyomi.ui.base.controller.RootController
 import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
+import eu.kanade.tachiyomi.ui.browse.source.browse.ProgressItem
+import eu.kanade.tachiyomi.ui.main.MainActivity
 import eu.kanade.tachiyomi.ui.manga.MangaController
 import eu.kanade.tachiyomi.ui.reader.ReaderActivity
+import eu.kanade.tachiyomi.util.system.logcat
 import eu.kanade.tachiyomi.util.system.toast
+import eu.kanade.tachiyomi.util.view.onAnimationsFinished
+import kotlinx.coroutines.flow.drop
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import logcat.LogPriority
 import reactivecircus.flowbinding.appcompat.queryTextChanges
+import uy.kohesive.injekt.injectLazy
 
-class HistoryController : ComposeController<HistoryPresenter>(), RootController {
+/**
+ * Fragment that shows recently read manga.
+ */
+class HistoryController :
+    NucleusController<HistoryControllerBinding, HistoryPresenter>(),
+    RootController,
+    FlexibleAdapter.OnUpdateListener,
+    FlexibleAdapter.EndlessScrollListener,
+    HistoryAdapter.OnRemoveClickListener,
+    HistoryAdapter.OnResumeClickListener,
+    HistoryAdapter.OnItemClickListener,
+    RemoveHistoryDialog.Listener {
 
+    private val db: DatabaseHelper by injectLazy()
+
+    /**
+     * Adapter containing the recent manga.
+     */
+    var adapter: HistoryAdapter? = null
+        private set
+
+    /**
+     * Endless loading item.
+     */
+    private var progressItem: ProgressItem? = null
+
+    /**
+     * Search query.
+     */
     private var query = ""
 
-    override fun getTitle() = resources?.getString(R.string.label_recent_manga)
-
-    override fun createPresenter() = HistoryPresenter()
-
-    @Composable
-    override fun ComposeContent() {
-        HistoryScreen(
-            composeView = binding.root,
-            presenter = presenter,
-            onClickItem = { history ->
-                router.pushController(MangaController(history).withFadeTransaction())
-            },
-            onClickResume = { history ->
-                presenter.getNextChapterForManga(history.mangaId, history.chapterId)
-            },
-            onClickDelete = { history, all ->
-                if (all) {
-                    // Reset last read of chapter to 0L
-                    presenter.removeAllFromHistory(history.mangaId)
-                } else {
-                    // Remove all chapters belonging to manga from library
-                    presenter.removeFromHistory(history)
-                }
-            },
-        )
+    override fun getTitle(): String? {
+        return resources?.getString(R.string.label_recent_manga)
+    }
+
+    override fun createPresenter(): HistoryPresenter {
+        return HistoryPresenter()
+    }
+
+    override fun createBinding(inflater: LayoutInflater) = HistoryControllerBinding.inflate(inflater)
+
+    override fun onViewCreated(view: View) {
+        super.onViewCreated(view)
+
+        binding.recycler.applyInsetter {
+            type(navigationBars = true) {
+                padding()
+            }
+        }
+
+        // Initialize adapter
+        binding.recycler.layoutManager = LinearLayoutManager(view.context)
+        adapter = HistoryAdapter(this@HistoryController)
+        binding.recycler.setHasFixedSize(true)
+        binding.recycler.adapter = adapter
+        adapter?.fastScroller = binding.fastScroller
+    }
+
+    override fun onDestroyView(view: View) {
+        adapter = null
+        super.onDestroyView(view)
+    }
+
+    /**
+     * Populate adapter with chapters
+     *
+     * @param mangaHistory list of manga history
+     */
+    fun onNextManga(mangaHistory: List<HistoryItem>, cleanBatch: Boolean = false) {
+        if (adapter?.itemCount ?: 0 == 0) {
+            resetProgressItem()
+        }
+        if (cleanBatch) {
+            adapter?.updateDataSet(mangaHistory)
+        } else {
+            adapter?.onLoadMoreComplete(mangaHistory)
+        }
+        binding.recycler.onAnimationsFinished {
+            (activity as? MainActivity)?.ready = true
+        }
+    }
+
+    /**
+     * Safely error if next page load fails
+     */
+    fun onAddPageError(error: Throwable) {
+        adapter?.onLoadMoreComplete(null)
+        adapter?.endlessTargetCount = 1
+        logcat(LogPriority.ERROR, error)
+    }
+
+    override fun onUpdateEmptyView(size: Int) {
+        if (size > 0) {
+            binding.emptyView.hide()
+        } else {
+            binding.emptyView.show(R.string.information_no_recent_manga)
+        }
+    }
+
+    /**
+     * Sets a new progress item and reenables the scroll listener.
+     */
+    private fun resetProgressItem() {
+        progressItem = ProgressItem()
+        adapter?.endlessTargetCount = 0
+        adapter?.setEndlessScrollListener(this, progressItem!!)
+    }
+
+    override fun onLoadMore(lastPosition: Int, currentPage: Int) {
+        val view = view ?: return
+        if (BackupRestoreService.isRunning(view.context.applicationContext)) {
+            onAddPageError(Throwable())
+            return
+        }
+        val adapter = adapter ?: return
+        presenter.requestNext(adapter.itemCount - adapter.headerItems.size, query)
+    }
+
+    override fun noMoreLoad(newItemsSize: Int) {}
+
+    override fun onResumeClick(position: Int) {
+        val activity = activity ?: return
+        val (manga, chapter, _) = (adapter?.getItem(position) as? HistoryItem)?.mch ?: return
+
+        val nextChapter = presenter.getNextChapter(chapter, manga)
+        if (nextChapter != null) {
+            val intent = ReaderActivity.newIntent(activity, manga, nextChapter)
+            startActivity(intent)
+        } else {
+            activity.toast(R.string.no_next_chapter)
+        }
+    }
+
+    override fun onRemoveClick(position: Int) {
+        val (manga, _, history) = (adapter?.getItem(position) as? HistoryItem)?.mch ?: return
+        RemoveHistoryDialog(this, manga, history).showDialog(router)
+    }
+
+    override fun onItemClick(position: Int) {
+        val manga = (adapter?.getItem(position) as? HistoryItem)?.mch?.manga ?: return
+        router.pushController(MangaController(manga).withFadeTransaction())
+    }
+
+    override fun removeHistory(manga: Manga, history: History, all: Boolean) {
+        if (all) {
+            // Reset last read of chapter to 0L
+            presenter.removeAllFromHistory(manga.id!!)
+        } else {
+            // Remove all chapters belonging to manga from library
+            presenter.removeFromHistory(history)
+        }
     }
 
     override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -61,33 +201,46 @@ class HistoryController : ComposeController<HistoryPresenter>(), RootController
             searchView.clearFocus()
         }
         searchView.queryTextChanges()
+            .drop(1) // Drop first event after subscribed
             .filter { router.backstack.lastOrNull()?.controller == this }
             .onEach {
                 query = it.toString()
-                presenter.search(query)
+                presenter.updateList(query)
             }
             .launchIn(viewScope)
+
+        // Fixes problem with the overflow icon showing up in lieu of search
+        searchItem.fixExpand(
+            onExpand = { invalidateMenuOnExpand() },
+        )
     }
 
     override fun onOptionsItemSelected(item: MenuItem): Boolean {
-        return when (item.itemId) {
+        when (item.itemId) {
             R.id.action_clear_history -> {
-                val dialog = ClearHistoryDialogController()
-                dialog.targetController = this@HistoryController
-                dialog.showDialog(router)
-                true
+                val ctrl = ClearHistoryDialogController()
+                ctrl.targetController = this@HistoryController
+                ctrl.showDialog(router)
             }
-            else -> super.onOptionsItemSelected(item)
         }
+
+        return super.onOptionsItemSelected(item)
     }
 
-    fun openChapter(chapter: Chapter?) {
-        val activity = activity ?: return
-        if (chapter != null) {
-            val intent = ReaderActivity.newIntent(activity, chapter.mangaId, chapter.id)
-            startActivity(intent)
-        } else {
-            activity.toast(R.string.no_next_chapter)
+    class ClearHistoryDialogController : DialogController() {
+        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
+            return MaterialAlertDialogBuilder(activity!!)
+                .setMessage(R.string.clear_history_confirmation)
+                .setPositiveButton(android.R.string.ok) { _, _ ->
+                    (targetController as? HistoryController)?.clearHistory()
+                }
+                .setNegativeButton(android.R.string.cancel, null)
+                .create()
         }
     }
+
+    private fun clearHistory() {
+        db.deleteHistory().executeAsBlocking()
+        activity?.toast(R.string.clear_history_completed)
+    }
 }

+ 71 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryHolder.kt

@@ -0,0 +1,71 @@
+package eu.kanade.tachiyomi.ui.recent.history
+
+import android.view.View
+import coil.dispose
+import coil.load
+import eu.davidea.viewholders.FlexibleViewHolder
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
+import eu.kanade.tachiyomi.databinding.HistoryItemBinding
+import eu.kanade.tachiyomi.util.lang.toTimestampString
+import java.util.Date
+
+/**
+ * Holder that contains recent manga item
+ * Uses R.layout.item_recently_read.
+ * UI related actions should be called from here.
+ *
+ * @param view the inflated view for this holder.
+ * @param adapter the adapter handling this holder.
+ * @constructor creates a new recent chapter holder.
+ */
+class HistoryHolder(
+    view: View,
+    val adapter: HistoryAdapter,
+) : FlexibleViewHolder(view, adapter) {
+
+    private val binding = HistoryItemBinding.bind(view)
+
+    init {
+        binding.holder.setOnClickListener {
+            adapter.itemClickListener.onItemClick(bindingAdapterPosition)
+        }
+
+        binding.remove.setOnClickListener {
+            adapter.removeClickListener.onRemoveClick(bindingAdapterPosition)
+        }
+
+        binding.resume.setOnClickListener {
+            adapter.resumeClickListener.onResumeClick(bindingAdapterPosition)
+        }
+    }
+
+    /**
+     * Set values of view
+     *
+     * @param item item containing history information
+     */
+    fun bind(item: MangaChapterHistory) {
+        // Retrieve objects
+        val (manga, chapter, history) = item
+
+        // Set manga title
+        binding.mangaTitle.text = manga.title
+
+        // Set chapter number + timestamp
+        if (chapter.chapter_number > -1f) {
+            val formattedNumber = adapter.decimalFormat.format(chapter.chapter_number.toDouble())
+            binding.mangaSubtitle.text = itemView.context.getString(
+                R.string.recent_manga_time,
+                formattedNumber,
+                Date(history.last_read).toTimestampString(),
+            )
+        } else {
+            binding.mangaSubtitle.text = Date(history.last_read).toTimestampString()
+        }
+
+        // Set cover
+        binding.cover.dispose()
+        binding.cover.load(item.manga)
+    }
+}

+ 42 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryItem.kt

@@ -0,0 +1,42 @@
+package eu.kanade.tachiyomi.ui.recent.history
+
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+import eu.davidea.flexibleadapter.FlexibleAdapter
+import eu.davidea.flexibleadapter.items.AbstractSectionableItem
+import eu.davidea.flexibleadapter.items.IFlexible
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
+import eu.kanade.tachiyomi.ui.recent.DateSectionItem
+
+class HistoryItem(val mch: MangaChapterHistory, header: DateSectionItem) :
+    AbstractSectionableItem<HistoryHolder, DateSectionItem>(header) {
+
+    override fun getLayoutRes(): Int {
+        return R.layout.history_item
+    }
+
+    override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): HistoryHolder {
+        return HistoryHolder(view, adapter as HistoryAdapter)
+    }
+
+    override fun bindViewHolder(
+        adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
+        holder: HistoryHolder,
+        position: Int,
+        payloads: List<Any?>?,
+    ) {
+        holder.bind(mch)
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (other is HistoryItem) {
+            return mch.manga.id == other.mch.manga.id
+        }
+        return false
+    }
+
+    override fun hashCode(): Int {
+        return mch.manga.id!!.hashCode()
+    }
+}

+ 124 - 94
app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryPresenter.kt

@@ -1,127 +1,157 @@
 package eu.kanade.tachiyomi.ui.recent.history
 
 import android.os.Bundle
-import androidx.paging.PagingData
-import androidx.paging.cachedIn
-import androidx.paging.insertSeparators
-import androidx.paging.map
-import eu.kanade.domain.history.interactor.DeleteHistoryTable
-import eu.kanade.domain.history.interactor.GetHistory
-import eu.kanade.domain.history.interactor.GetNextChapterForManga
-import eu.kanade.domain.history.interactor.RemoveHistoryById
-import eu.kanade.domain.history.interactor.RemoveHistoryByMangaId
-import eu.kanade.domain.history.model.HistoryWithRelations
-import eu.kanade.tachiyomi.R
+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.models.MangaChapterHistory
+import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
-import eu.kanade.tachiyomi.util.lang.launchIO
-import eu.kanade.tachiyomi.util.lang.launchUI
+import eu.kanade.tachiyomi.ui.recent.DateSectionItem
 import eu.kanade.tachiyomi.util.lang.toDateKey
-import eu.kanade.tachiyomi.util.system.toast
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.update
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-import java.util.*
+import rx.Observable
+import rx.Subscription
+import rx.android.schedulers.AndroidSchedulers
+import uy.kohesive.injekt.injectLazy
+import java.text.DateFormat
+import java.util.Calendar
+import java.util.Date
+import java.util.TreeMap
 
 /**
  * Presenter of HistoryFragment.
  * Contains information and data for fragment.
  * Observable updates should be called from here.
  */
-class HistoryPresenter(
-    private val getHistory: GetHistory = Injekt.get(),
-    private val getNextChapterForManga: GetNextChapterForManga = Injekt.get(),
-    private val deleteHistoryTable: DeleteHistoryTable = Injekt.get(),
-    private val removeHistoryById: RemoveHistoryById = Injekt.get(),
-    private val removeHistoryByMangaId: RemoveHistoryByMangaId = Injekt.get(),
-) : BasePresenter<HistoryController>() {
-
-    private var _query: MutableStateFlow<String> = MutableStateFlow("")
-    private var _state: MutableStateFlow<HistoryState> = MutableStateFlow(HistoryState.EMPTY)
-    val state: StateFlow<HistoryState> = _state
+class HistoryPresenter : BasePresenter<HistoryController>() {
+
+    private val db: DatabaseHelper by injectLazy()
+    private val preferences: PreferencesHelper by injectLazy()
+
+    private val relativeTime: Int = preferences.relativeTime().get()
+    private val dateFormat: DateFormat = preferences.dateFormat()
+
+    private var recentMangaSubscription: Subscription? = null
 
     override fun onCreate(savedState: Bundle?) {
         super.onCreate(savedState)
 
-        presenterScope.launchIO {
-            _state.update { state ->
-                state.copy(
-                    list = _query.flatMapLatest { query ->
-                        getHistory.subscribe(query)
-                            .map { pagingData ->
-                                pagingData
-                                    .map {
-                                        UiModel.Item(it)
-                                    }
-                                    .insertSeparators { before, after ->
-                                        val beforeDate = before?.item?.readAt?.time?.toDateKey() ?: Date(0)
-                                        val afterDate = after?.item?.readAt?.time?.toDateKey() ?: Date(0)
-                                        when {
-                                            beforeDate.time != afterDate.time && afterDate.time != 0L -> UiModel.Header(afterDate)
-                                            // Return null to avoid adding a separator between two items.
-                                            else -> null
-                                        }
-                                    }
-                            }
-                    }
-                        .cachedIn(presenterScope),
-                )
-            }
-        }
+        // Used to get a list of recently read manga
+        updateList()
     }
 
-    fun search(query: String) {
-        presenterScope.launchIO {
-            _query.emit(query)
-        }
+    fun requestNext(offset: Int, search: String = "") {
+        getRecentMangaObservable(offset = offset, search = search)
+            .subscribeLatestCache(
+                { view, mangas ->
+                    view.onNextManga(mangas)
+                },
+                HistoryController::onAddPageError,
+            )
     }
 
-    fun removeFromHistory(history: HistoryWithRelations) {
-        presenterScope.launchIO {
-            removeHistoryById.await(history)
+    /**
+     * Get recent manga observable
+     * @return list of history
+     */
+    private fun getRecentMangaObservable(limit: Int = 25, offset: Int = 0, search: String = ""): Observable<List<HistoryItem>> {
+        // Set date limit for recent manga
+        val cal = Calendar.getInstance().apply {
+            time = Date()
+            add(Calendar.YEAR, -50)
         }
+
+        return db.getRecentManga(cal.time, limit, offset, search).asRxObservable()
+            .map { recents ->
+                val map = TreeMap<Date, MutableList<MangaChapterHistory>> { d1, d2 -> d2.compareTo(d1) }
+                val byDay = recents
+                    .groupByTo(map) { it.history.last_read.toDateKey() }
+                byDay.flatMap { entry ->
+                    val dateItem = DateSectionItem(entry.key, relativeTime, dateFormat)
+                    entry.value.map { HistoryItem(it, dateItem) }
+                }
+            }
+            .observeOn(AndroidSchedulers.mainThread())
     }
 
-    fun removeAllFromHistory(mangaId: Long) {
-        presenterScope.launchIO {
-            removeHistoryByMangaId.await(mangaId)
-        }
+    /**
+     * Reset last read of chapter to 0L
+     * @param history history belonging to chapter
+     */
+    fun removeFromHistory(history: History) {
+        history.last_read = 0L
+        db.updateHistoryLastRead(history).asRxObservable()
+            .subscribe()
     }
 
-    fun getNextChapterForManga(mangaId: Long, chapterId: Long) {
-        presenterScope.launchIO {
-            val chapter = getNextChapterForManga.await(mangaId, chapterId)
-            launchUI {
-                view?.openChapter(chapter)
-            }
-        }
+    /**
+     * Pull a list of history from the db
+     * @param search a search query to use for filtering
+     */
+    fun updateList(search: String = "") {
+        recentMangaSubscription?.unsubscribe()
+        recentMangaSubscription = getRecentMangaObservable(search = search)
+            .subscribeLatestCache(
+                { view, mangas ->
+                    view.onNextManga(mangas, true)
+                },
+                HistoryController::onAddPageError,
+            )
     }
 
-    fun deleteAllHistory() {
-        presenterScope.launchIO {
-            val result = deleteHistoryTable.await()
-            if (!result) return@launchIO
-            launchUI {
-                view?.activity?.toast(R.string.clear_history_completed)
+    /**
+     * Removes all chapters belonging to manga from history.
+     * @param mangaId id of manga
+     */
+    fun removeAllFromHistory(mangaId: Long) {
+        db.getHistoryByMangaId(mangaId).asRxSingle()
+            .map { list ->
+                list.forEach { it.last_read = 0L }
+                db.updateHistoryLastRead(list).executeAsBlocking()
             }
-        }
+            .subscribe()
     }
-}
 
-sealed class UiModel {
-    data class Item(val item: HistoryWithRelations) : UiModel()
-    data class Header(val date: Date) : UiModel()
-}
+    /**
+     * Retrieves the next chapter of the given one.
+     *
+     * @param chapter the chapter of the history object.
+     * @param manga the manga of the chapter.
+     */
+    fun getNextChapter(chapter: Chapter, manga: Manga): Chapter? {
+        if (!chapter.read) {
+            return 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) }
 
-data class HistoryState(
-    val list: Flow<PagingData<UiModel>>? = null,
-) {
+        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.chapter_number
 
-    companion object {
-        val EMPTY = HistoryState(null)
+                ((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")
+        }
     }
 }

+ 54 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/RemoveHistoryDialog.kt

@@ -0,0 +1,54 @@
+package eu.kanade.tachiyomi.ui.recent.history
+
+import android.app.Dialog
+import android.os.Bundle
+import com.bluelinelabs.conductor.Controller
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.database.models.History
+import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.ui.base.controller.DialogController
+import eu.kanade.tachiyomi.widget.DialogCheckboxView
+
+class RemoveHistoryDialog<T>(bundle: Bundle? = null) : DialogController(bundle)
+        where T : Controller, T : RemoveHistoryDialog.Listener {
+
+    private var manga: Manga? = null
+
+    private var history: History? = null
+
+    constructor(target: T, manga: Manga, history: History) : this() {
+        this.manga = manga
+        this.history = history
+        targetController = target
+    }
+
+    override fun onCreateDialog(savedViewState: Bundle?): Dialog {
+        val activity = activity!!
+
+        // Create custom view
+        val dialogCheckboxView = DialogCheckboxView(activity).apply {
+            setDescription(R.string.dialog_with_checkbox_remove_description)
+            setOptionDescription(R.string.dialog_with_checkbox_reset)
+        }
+
+        return MaterialAlertDialogBuilder(activity)
+            .setTitle(R.string.action_remove)
+            .setView(dialogCheckboxView)
+            .setPositiveButton(R.string.action_remove) { _, _ -> onPositive(dialogCheckboxView.isChecked()) }
+            .setNegativeButton(android.R.string.cancel, null)
+            .create()
+    }
+
+    private fun onPositive(checked: Boolean) {
+        val target = targetController as? Listener ?: return
+        val manga = manga ?: return
+        val history = history ?: return
+
+        target.removeHistory(manga, history, checked)
+    }
+
+    interface Listener {
+        fun removeHistory(manga: Manga, history: History, all: Boolean)
+    }
+}

+ 1 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt

@@ -9,7 +9,6 @@ import android.webkit.WebView
 import androidx.core.net.toUri
 import androidx.preference.PreferenceScreen
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import eu.kanade.tachiyomi.BuildConfig
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.cache.ChapterCache
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
@@ -40,6 +39,7 @@ import eu.kanade.tachiyomi.util.preference.summaryRes
 import eu.kanade.tachiyomi.util.preference.switchPreference
 import eu.kanade.tachiyomi.util.preference.titleRes
 import eu.kanade.tachiyomi.util.system.DeviceUtil
+import eu.kanade.tachiyomi.util.system.isDevFlavor
 import eu.kanade.tachiyomi.util.system.isPackageInstalled
 import eu.kanade.tachiyomi.util.system.logcat
 import eu.kanade.tachiyomi.util.system.powerManager
@@ -49,7 +49,6 @@ import logcat.LogPriority
 import rikka.sui.Sui
 import uy.kohesive.injekt.injectLazy
 import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
-import eu.kanade.tachiyomi.util.system.isDevFlavor
 
 class SettingsAdvancedController : SettingsController() {
 

+ 3 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/setting/database/ClearDatabaseController.kt

@@ -9,6 +9,7 @@ import android.view.MenuInflater
 import android.view.MenuItem
 import android.view.View
 import androidx.core.view.forEach
+import androidx.core.view.get
 import androidx.core.view.isVisible
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
@@ -36,6 +37,7 @@ class ClearDatabaseController :
     private var menu: Menu? = null
 
     private var actionFab: ExtendedFloatingActionButton? = null
+    private var actionFabScrollListener: RecyclerView.OnScrollListener? = null
 
     init {
         setHasOptionsMenu(true)
@@ -141,6 +143,7 @@ class ClearDatabaseController :
 
     override fun cleanupFab(fab: ExtendedFloatingActionButton) {
         actionFab?.setOnClickListener(null)
+        actionFabScrollListener?.let { recycler?.removeOnScrollListener(it) }
         actionFab = null
     }
 

+ 1 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/setting/database/ClearDatabasePresenter.kt

@@ -1,7 +1,6 @@
 package eu.kanade.tachiyomi.ui.setting.database
 
 import android.os.Bundle
-import eu.kanade.tachiyomi.Database
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
 import eu.kanade.tachiyomi.source.SourceManager
 import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
@@ -14,7 +13,6 @@ import uy.kohesive.injekt.api.get
 class ClearDatabasePresenter : BasePresenter<ClearDatabaseController>() {
 
     private val db = Injekt.get<DatabaseHelper>()
-    private val database = Injekt.get<Database>()
 
     private val sourceManager = Injekt.get<SourceManager>()
 
@@ -28,7 +26,7 @@ class ClearDatabasePresenter : BasePresenter<ClearDatabaseController>() {
 
     fun clearDatabaseForSourceIds(sources: List<Long>) {
         db.deleteMangasNotInLibraryBySourceIds(sources).executeAsBlocking()
-        database.historyQueries.removeResettedHistory()
+        db.deleteHistoryNoLastRead().executeAsBlocking()
     }
 
     private fun getDatabaseSourcesObservable(): Observable<List<ClearDatabaseSourceItem>> {

+ 1 - 12
app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiCoordinatorLayout.kt

@@ -5,10 +5,8 @@ import android.os.Parcel
 import android.os.Parcelable
 import android.util.AttributeSet
 import android.view.View
-import androidx.compose.ui.platform.ComposeView
 import androidx.coordinatorlayout.R
 import androidx.coordinatorlayout.widget.CoordinatorLayout
-import androidx.core.view.ViewCompat
 import androidx.core.view.doOnLayout
 import androidx.core.view.isVisible
 import androidx.customview.view.AbsSavedState
@@ -65,16 +63,7 @@ class TachiyomiCoordinatorLayout @JvmOverloads constructor(
         super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed)
         // Disable elevation overlay when tabs are visible
         if (canLiftAppBarOnScroll) {
-            if (target is ComposeView) {
-                val scrollCondition = if (type == ViewCompat.TYPE_NON_TOUCH) {
-                    dyUnconsumed >= 0
-                } else {
-                    dyConsumed != 0 || dyUnconsumed >= 0
-                }
-                appBarLayout?.isLifted = scrollCondition && tabLayout?.isVisible == false
-            } else {
-                appBarLayout?.isLifted = (dyConsumed != 0 || dyUnconsumed >= 0) && tabLayout?.isVisible == false
-            }
+            appBarLayout?.isLifted = (dyConsumed != 0 || dyUnconsumed >= 0) && tabLayout?.isVisible == false
         }
     }
 

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/widget/preference/ThemesPreferenceAdapter.kt

@@ -25,7 +25,7 @@ class ThemesPreferenceAdapter(private val clickListener: OnItemClickListener) :
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ThemeViewHolder {
         val themeResIds = ThemingDelegate.getThemeResIds(themes[viewType], preferences.themeDarkAmoled().get())
         val themedContext = themeResIds.fold(parent.context) {
-            context, themeResId ->
+                context, themeResId ->
             ContextThemeWrapper(context, themeResId)
         }
 

+ 0 - 4
app/src/main/res/layout/compose_controller.xml

@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.compose.ui.platform.ComposeView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" />

+ 33 - 0
app/src/main/res/layout/history_controller.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/recycler"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipToPadding="false"
+        android:paddingTop="4dp"
+        android:paddingBottom="@dimen/action_toolbar_list_padding"
+        tools:listitem="@layout/history_item" />
+
+    <eu.kanade.tachiyomi.widget.MaterialFastScroll
+        android:id="@+id/fast_scroller"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="end"
+        app:fastScrollerBubbleEnabled="false"
+        tools:visibility="visible" />
+
+    <eu.kanade.tachiyomi.widget.EmptyView
+        android:id="@+id/empty_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:visibility="gone" />
+
+</FrameLayout>

+ 85 - 0
app/src/main/res/layout/history_item.xml

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/holder"
+    android:layout_width="match_parent"
+    android:layout_height="96dp"
+    android:paddingStart="16dp"
+    android:paddingTop="8dp"
+    android:paddingEnd="8dp"
+    android:paddingBottom="8dp"
+    android:background="?attr/selectableItemBackground"
+    android:orientation="horizontal">
+
+    <com.google.android.material.imageview.ShapeableImageView
+        android:id="@+id/cover"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:contentDescription="@string/description_cover"
+        android:scaleType="centerCrop"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintDimensionRatio="h,3:2"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:shapeAppearance="@style/ShapeAppearanceOverlay.Cover"
+        tools:src="@mipmap/ic_launcher" />
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="8dp"
+        android:orientation="vertical"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@+id/remove"
+        app:layout_constraintStart_toEndOf="@+id/cover"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <TextView
+            android:id="@+id/manga_title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:maxLines="2"
+            android:textAppearance="?attr/textAppearanceTitleSmall"
+            tools:text="Title" />
+
+        <TextView
+            android:id="@+id/manga_subtitle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="2dp"
+            android:textAppearance="?attr/textAppearanceBodyMedium"
+            android:textColor="?android:attr/textColorSecondary"
+            tools:text="Subtitle" />
+
+    </LinearLayout>
+
+    <ImageButton
+        android:id="@+id/remove"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:contentDescription="@string/action_resume"
+        android:padding="8dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@+id/resume"
+        app:layout_constraintTop_toTopOf="parent"
+        app:srcCompat="@drawable/ic_delete_24dp"
+        app:tint="?android:attr/textColorPrimary" />
+
+    <ImageButton
+        android:id="@+id/resume"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:contentDescription="@string/action_resume"
+        android:padding="8dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:srcCompat="@drawable/ic_play_arrow_24dp"
+        app:tint="?android:attr/textColorPrimary" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 0 - 6
app/src/main/sqldelight/data/categories.sq

@@ -1,6 +0,0 @@
-CREATE TABLE categories(
-    _id INTEGER NOT NULL PRIMARY KEY,
-    name TEXT NOT NULL,
-    sort INTEGER NOT NULL,
-    flags INTEGER NOT NULL
-);

+ 0 - 29
app/src/main/sqldelight/data/chapters.sq

@@ -1,29 +0,0 @@
-CREATE TABLE chapters(
-    _id INTEGER NOT NULL PRIMARY KEY,
-    manga_id INTEGER NOT NULL,
-    url TEXT NOT NULL,
-    name TEXT NOT NULL,
-    scanlator TEXT,
-    read INTEGER AS Boolean NOT NULL,
-    bookmark INTEGER AS Boolean NOT NULL,
-    last_page_read INTEGER NOT NULL,
-    chapter_number REAL AS Float NOT NULL,
-    source_order INTEGER NOT NULL,
-    date_fetch INTEGER AS Long NOT NULL,
-    date_upload INTEGER AS Long NOT NULL,
-    FOREIGN KEY(manga_id) REFERENCES mangas (_id)
-    ON DELETE CASCADE
-);
-
-CREATE INDEX chapters_manga_id_index ON chapters(manga_id);
-CREATE INDEX chapters_unread_by_manga_index ON chapters(manga_id, read) WHERE read = 0;
-
-getChapterById:
-SELECT *
-FROM chapters
-WHERE _id = :id;
-
-getChapterByMangaId:
-SELECT *
-FROM chapters
-WHERE manga_id = :mangaId;

+ 0 - 37
app/src/main/sqldelight/data/history.sq

@@ -1,37 +0,0 @@
-import java.util.Date;
-
-CREATE TABLE history(
-    history_id INTEGER NOT NULL PRIMARY KEY,
-    history_chapter_id INTEGER NOT NULL UNIQUE,
-    history_last_read INTEGER AS Date,
-    history_time_read INTEGER AS Date,
-    FOREIGN KEY(history_chapter_id) REFERENCES chapters (_id)
-    ON DELETE CASCADE
-);
-
-CREATE INDEX history_history_chapter_id_index ON history(history_chapter_id);
-
-resetHistoryById:
-UPDATE history
-SET history_last_read = 0
-WHERE history_id = :historyId;
-
-resetHistoryByMangaId:
-UPDATE history
-SET history_last_read = 0
-WHERE history_id IN (
-    SELECT H.history_id
-    FROM mangas M
-    INNER JOIN chapters C
-    ON M._id = C.manga_id
-    INNER JOIN history H
-    ON C._id = H.history_chapter_id
-    WHERE M._id = :mangaId
-);
-
-removeAllHistory:
-DELETE FROM history;
-
-removeResettedHistory:
-DELETE FROM history
-WHERE history_last_read = 0;

+ 0 - 18
app/src/main/sqldelight/data/manga_sync.sq

@@ -1,18 +0,0 @@
-CREATE TABLE manga_sync(
-    _id INTEGER NOT NULL PRIMARY KEY,
-    manga_id INTEGER NOT NULL,
-    sync_id INTEGER NOT NULL,
-    remote_id INTEGER NOT NULL,
-    library_id INTEGER,
-    title TEXT NOT NULL,
-    last_chapter_read REAL NOT NULL,
-    total_chapters INTEGER NOT NULL,
-    status INTEGER NOT NULL,
-    score REAL AS Float NOT NULL,
-    remote_url TEXT NOT NULL,
-    start_date INTEGER AS Long NOT NULL,
-    finish_date INTEGER AS Long NOT NULL,
-    UNIQUE (manga_id, sync_id) ON CONFLICT REPLACE,
-    FOREIGN KEY(manga_id) REFERENCES mangas (_id)
-    ON DELETE CASCADE
-);

+ 0 - 31
app/src/main/sqldelight/data/mangas.sq

@@ -1,31 +0,0 @@
-import java.lang.String;
-import kotlin.collections.List;
-
-CREATE TABLE mangas(
-    _id INTEGER NOT NULL PRIMARY KEY,
-    source INTEGER NOT NULL,
-    url TEXT NOT NULL,
-    artist TEXT,
-    author TEXT,
-    description TEXT,
-    genre TEXT AS List<String>,
-    title TEXT NOT NULL,
-    status INTEGER NOT NULL,
-    thumbnail_url TEXT,
-    favorite INTEGER AS Boolean NOT NULL,
-    last_update INTEGER AS Long,
-    next_update INTEGER AS Long,
-    initialized INTEGER AS Boolean NOT NULL,
-    viewer INTEGER NOT NULL,
-    chapter_flags INTEGER NOT NULL,
-    cover_last_modified INTEGER AS Long NOT NULL,
-    date_added INTEGER AS Long NOT NULL
-);
-
-CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1;
-CREATE INDEX mangas_url_index ON mangas(url);
-
-getMangaById:
-SELECT *
-FROM mangas
-WHERE _id = :id;

+ 0 - 9
app/src/main/sqldelight/data/mangas_categories.sq

@@ -1,9 +0,0 @@
-CREATE TABLE mangas_categories(
-    _id INTEGER NOT NULL PRIMARY KEY,
-    manga_id INTEGER NOT NULL,
-    category_id INTEGER NOT NULL,
-    FOREIGN KEY(category_id) REFERENCES categories (_id)
-    ON DELETE CASCADE,
-    FOREIGN KEY(manga_id) REFERENCES mangas (_id)
-    ON DELETE CASCADE
-);

+ 0 - 6
app/src/main/sqldelight/migrations/1.sqm

@@ -1,6 +0,0 @@
-ALTER TABLE chapters
-ADD COLUMN source_order INTEGER DEFAULT 0;
-
-UPDATE mangas
-SET thumbnail_url = replace(thumbnail_url, '93.174.95.110', 'kissmanga.com')
-WHERE source = 4;

+ 0 - 11
app/src/main/sqldelight/migrations/10.sqm

@@ -1,11 +0,0 @@
-ALTER TABLE mangas
-ADD COLUMN date_added INTEGER NOT NULL DEFAULT 0;
-
-UPDATE mangas
-SET date_added = (
-    SELECT MIN(date_fetch)
-    FROM mangas M
-    INNER JOIN chapters C
-    ON M._id = C.manga_id
-    GROUP BY M._id
-);

+ 0 - 2
app/src/main/sqldelight/migrations/11.sqm

@@ -1,2 +0,0 @@
-ALTER TABLE mangas
-ADD COLUMN next_update INTEGER DEFAULT 0;

+ 0 - 9
app/src/main/sqldelight/migrations/12.sqm

@@ -1,9 +0,0 @@
-ALTER TABLE manga_sync
-RENAME TO manga_sync_tmp;
-
-INSERT INTO manga_sync(_id, manga_id, sync_id, remote_id, library_id, title, last_chapter_read, total_chapters, status, score, remote_url, start_date, finish_date)
-SELECT _id, manga_id, sync_id, remote_id, library_id, title, last_chapter_read, total_chapters, status, score, remote_url, start_date, finish_date
-FROM manga_sync_tmp;
-
-
-DROP TABLE manga_sync_tmp;

+ 0 - 3
app/src/main/sqldelight/migrations/13.sqm

@@ -1,3 +0,0 @@
-UPDATE chapters
-SET date_upload = date_fetch
-WHERE date_upload = 0;

+ 0 - 149
app/src/main/sqldelight/migrations/14.sqm

@@ -1,149 +0,0 @@
-DROP INDEX IF EXISTS chapters_manga_id_index;
-DROP INDEX IF EXISTS chapters_unread_by_manga_index;
-DROP INDEX IF EXISTS history_history_chapter_id_index;
-DROP INDEX IF EXISTS library_favorite_index;
-DROP INDEX IF EXISTS mangas_url_index;
-
-ALTER TABLE mangas RENAME TO manga_temp;
-CREATE TABLE mangas(
-    _id INTEGER NOT NULL PRIMARY KEY,
-    source INTEGER NOT NULL,
-    url TEXT NOT NULL,
-    artist TEXT,
-    author TEXT,
-    description TEXT,
-    genre TEXT,
-    title TEXT NOT NULL,
-    status INTEGER NOT NULL,
-    thumbnail_url TEXT,
-    favorite INTEGER NOT NULL,
-    last_update INTEGER AS Long,
-    next_update INTEGER AS Long,
-    initialized INTEGER AS Boolean NOT NULL,
-    viewer INTEGER NOT NULL,
-    chapter_flags INTEGER NOT NULL,
-    cover_last_modified INTEGER AS Long NOT NULL,
-    date_added INTEGER AS Long NOT NULL
-);
-INSERT INTO mangas
-SELECT _id,source,url,artist,author,description,genre,title,status,thumbnail_url,favorite,last_update,next_update,initialized,viewer,chapter_flags,cover_last_modified,date_added
-FROM manga_temp;
-
-ALTER TABLE categories RENAME TO categories_temp;
-CREATE TABLE categories(
-    _id INTEGER NOT NULL PRIMARY KEY,
-    name TEXT NOT NULL,
-    sort INTEGER NOT NULL,
-    flags INTEGER NOT NULL
-);
-INSERT INTO categories
-SELECT _id,name,sort,flags
-FROM categories_temp;
-
-ALTER TABLE chapters RENAME TO chapters_temp;
-CREATE TABLE chapters(
-    _id INTEGER NOT NULL PRIMARY KEY,
-    manga_id INTEGER NOT NULL,
-    url TEXT NOT NULL,
-    name TEXT NOT NULL,
-    scanlator TEXT,
-    read INTEGER AS Boolean NOT NULL,
-    bookmark INTEGER AS Boolean NOT NULL,
-    last_page_read INTEGER NOT NULL,
-    chapter_number REAL AS Float NOT NULL,
-    source_order INTEGER NOT NULL,
-    date_fetch INTEGER AS Long NOT NULL,
-    date_upload INTEGER AS Long NOT NULL,
-    FOREIGN KEY(manga_id) REFERENCES mangas (_id)
-    ON DELETE CASCADE
-);
-INSERT INTO chapters
-SELECT _id,manga_id,url,name,scanlator,read,bookmark,last_page_read,chapter_number,source_order,date_fetch,date_upload
-FROM chapters_temp;
-
-ALTER TABLE history RENAME TO history_temp;
-CREATE TABLE history(
-    history_id INTEGER NOT NULL PRIMARY KEY,
-    history_chapter_id INTEGER NOT NULL UNIQUE,
-    history_last_read INTEGER AS Long,
-    history_time_read INTEGER AS Long,
-    FOREIGN KEY(history_chapter_id) REFERENCES chapters (_id)
-    ON DELETE CASCADE
-);
-INSERT INTO history
-SELECT history_id, history_chapter_id, history_last_read, history_time_read
-FROM history_temp;
-
-ALTER TABLE mangas_categories RENAME TO mangas_categories_temp;
-CREATE TABLE mangas_categories(
-    _id INTEGER NOT NULL PRIMARY KEY,
-    manga_id INTEGER NOT NULL,
-    category_id INTEGER NOT NULL,
-    FOREIGN KEY(category_id) REFERENCES categories (_id)
-    ON DELETE CASCADE,
-    FOREIGN KEY(manga_id) REFERENCES mangas (_id)
-    ON DELETE CASCADE
-);
-INSERT INTO mangas_categories
-SELECT _id, manga_id, category_id
-FROM mangas_categories_temp;
-
-ALTER TABLE manga_sync RENAME TO manga_sync_temp;
-CREATE TABLE manga_sync(
-    _id INTEGER NOT NULL PRIMARY KEY,
-    manga_id INTEGER NOT NULL,
-    sync_id INTEGER NOT NULL,
-    remote_id INTEGER NOT NULL,
-    library_id INTEGER,
-    title TEXT NOT NULL,
-    last_chapter_read REAL NOT NULL,
-    total_chapters INTEGER NOT NULL,
-    status INTEGER NOT NULL,
-    score REAL AS Float NOT NULL,
-    remote_url TEXT NOT NULL,
-    start_date INTEGER AS Long NOT NULL,
-    finish_date INTEGER AS Long NOT NULL,
-    UNIQUE (manga_id, sync_id) ON CONFLICT REPLACE,
-    FOREIGN KEY(manga_id) REFERENCES mangas (_id)
-    ON DELETE CASCADE
-);
-INSERT INTO manga_sync
-SELECT _id, manga_id, sync_id, remote_id, library_id, title, last_chapter_read, total_chapters, status, score, remote_url, start_date, finish_date
-FROM manga_sync_temp;
-
-CREATE INDEX chapters_manga_id_index ON chapters(manga_id);
-CREATE INDEX chapters_unread_by_manga_index ON chapters(manga_id, read) WHERE read = 0;
-CREATE INDEX history_history_chapter_id_index ON history(history_chapter_id);
-CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1;
-CREATE INDEX mangas_url_index ON mangas(url);
-
-CREATE VIEW IF NOT EXISTS historyView AS
-SELECT
-history.history_id AS id,
-mangas._id AS mangaId,
-chapters._id AS chapterId,
-mangas.title,
-mangas.thumbnail_url AS thumnailUrl,
-chapters.chapter_number AS chapterNumber,
-history.history_last_read AS readAt,
-max_last_read.history_last_read AS maxReadAt,
-max_last_read.history_chapter_id AS maxReadAtChapterId
-FROM mangas
-JOIN chapters
-ON mangas._id = chapters.manga_id
-JOIN history
-ON chapters._id = history.history_chapter_id
-JOIN (
-SELECT chapters.manga_id,chapters._id AS history_chapter_id, MAX(history.history_last_read) AS history_last_read
-FROM chapters JOIN history
-ON chapters._id = history.history_chapter_id
-GROUP BY chapters.manga_id
-) AS max_last_read
-ON chapters.manga_id = max_last_read.manga_id;
-
-DROP TABLE IF EXISTS manga_sync_temp;
-DROP TABLE IF EXISTS mangas_categories_temp;
-DROP TABLE IF EXISTS history_temp;
-DROP TABLE IF EXISTS chapters_temp;
-DROP TABLE IF EXISTS categories_temp;
-DROP TABLE IF EXISTS manga_temp;

+ 0 - 10
app/src/main/sqldelight/migrations/2.sqm

@@ -1,10 +0,0 @@
-CREATE TABLE history(
-    history_id INTEGER NOT NULL PRIMARY KEY,
-    history_chapter_id INTEGER NOT NULL UNIQUE,
-    history_last_read INTEGER,
-    history_time_read INTEGER,
-    FOREIGN KEY(history_chapter_id) REFERENCES chapters (_id)
-    ON DELETE CASCADE
-);
-
-CREATE INDEX history_history_chapter_id_index ON history(history_chapter_id);

+ 0 - 2
app/src/main/sqldelight/migrations/3.sqm

@@ -1,2 +0,0 @@
-ALTER TABLE chapters
-ADD COLUMN bookmark INTEGER DEFAULT 0;

+ 0 - 2
app/src/main/sqldelight/migrations/4.sqm

@@ -1,2 +0,0 @@
-ALTER TABLE chapters
-ADD COLUMN scanlator TEXT DEFAULT NULL;

+ 0 - 2
app/src/main/sqldelight/migrations/5.sqm

@@ -1,2 +0,0 @@
-ALTER TABLE manga_sync
-ADD COLUMN remote_url TEXT DEFAULT '';

+ 0 - 2
app/src/main/sqldelight/migrations/6.sqm

@@ -1,2 +0,0 @@
-ALTER TABLE manga_sync
-ADD COLUMN library_id INTEGER;

+ 0 - 9
app/src/main/sqldelight/migrations/7.sqm

@@ -1,9 +0,0 @@
-DROP INDEX IF EXISTS mangas_favorite_index;
-
-CREATE INDEX library_favorite_index
-ON mangas(favorite)
-WHERE favorite = 1;
-
-CREATE INDEX chapters_unread_by_manga_index
-ON chapters(manga_id, read)
-WHERE read = 0;

+ 0 - 5
app/src/main/sqldelight/migrations/8.sqm

@@ -1,5 +0,0 @@
-ALTER TABLE manga_sync
-ADD COLUMN start_date INTEGER NOT NULL DEFAULT 0;
-
-ALTER TABLE manga_sync
-ADD COLUMN finish_date INTEGER NOT NULL DEFAULT 0;

+ 0 - 2
app/src/main/sqldelight/migrations/9.sqm

@@ -1,2 +0,0 @@
-ALTER TABLE mangas
-ADD COLUMN cover_last_modified INTEGER NOT NULL DEFAULT 0;

+ 0 - 46
app/src/main/sqldelight/view/historyView.sq

@@ -1,46 +0,0 @@
-CREATE VIEW historyView AS
-SELECT
-history.history_id AS id,
-mangas._id AS mangaId,
-chapters._id AS chapterId,
-mangas.title,
-mangas.thumbnail_url AS thumnailUrl,
-chapters.chapter_number AS chapterNumber,
-history.history_last_read AS readAt,
-max_last_read.history_last_read AS maxReadAt,
-max_last_read.history_chapter_id AS maxReadAtChapterId
-FROM mangas
-JOIN chapters
-ON mangas._id = chapters.manga_id
-JOIN history
-ON chapters._id = history.history_chapter_id
-JOIN (
-SELECT chapters.manga_id,chapters._id AS history_chapter_id, MAX(history.history_last_read) AS history_last_read
-FROM chapters JOIN history
-ON chapters._id = history.history_chapter_id
-GROUP BY chapters.manga_id
-) AS max_last_read
-ON chapters.manga_id = max_last_read.manga_id;
-
-countHistory:
-SELECT count(*)
-FROM historyView
-WHERE historyView.readAt > 0
-AND maxReadAtChapterId = historyView.chapterId
-AND lower(historyView.title) LIKE ('%' || :query || '%');
-
-history:
-SELECT
-id,
-mangaId,
-chapterId,
-title,
-thumnailUrl,
-chapterNumber,
-readAt
-FROM historyView
-WHERE historyView.readAt > 0
-AND maxReadAtChapterId = historyView.chapterId
-AND lower(historyView.title) LIKE ('%' || :query || '%')
-ORDER BY readAt DESC
-LIMIT :limit OFFSET :offset;

+ 1 - 1
app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt

@@ -344,7 +344,7 @@ class BackupTest {
 
     private fun clearDatabase() {
         db.deleteMangas().executeAsBlocking()
-        db.dropHistoryTable().executeAsBlocking()
+        db.deleteHistory().executeAsBlocking()
     }
 
     private fun getSingleHistory(chapter: Chapter): DHistory {

+ 0 - 1
build.gradle.kts

@@ -4,7 +4,6 @@ buildscript {
         classpath(libs.google.services.gradle)
         classpath(libs.aboutlibraries.gradle)
         classpath(kotlinx.serialization.gradle)
-        classpath("com.squareup.sqldelight:gradle-plugin:1.5.3")
     }
 }
 

+ 0 - 3
gradle/androidx.versions.toml

@@ -21,9 +21,6 @@ lifecycle-runtimektx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", ve
 work-runtime = "androidx.work:work-runtime-ktx:2.6.0"
 guava = "com.google.guava:guava:31.1-android"
 
-paging-runtime = "androidx.paging:paging-runtime:3.1.1"
-paging-compose = "androidx.paging:paging-compose:1.0.0-alpha14"
-
 [bundles]
 lifecycle = ["lifecycle-common", "lifecycle-process", "lifecycle-runtimektx"]
 workmanager = ["work-runtime", "guava"]

+ 0 - 9
gradle/compose.versions.toml

@@ -1,9 +0,0 @@
-[versions]
-compose = "1.2.0-alpha07"
-
-[libraries]
-foundation = { module = "androidx.compose.foundation:foundation", version.ref="compose" }
-material3-core = "androidx.compose.material3:material3:1.0.0-alpha09"
-material3-adapter = "com.google.android.material:compose-theme-adapter-3:1.0.6"
-animation = { module = "androidx.compose.animation:animation", version.ref="compose" }
-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref="compose" }

+ 1 - 1
gradle/kotlinx.versions.toml

@@ -1,5 +1,5 @@
 [versions]
-kotlin_version = "1.6.10"
+kotlin_version = "1.6.20"
 coroutines_version = "1.6.1"
 serialization_version = "1.3.2"
 

+ 3 - 9
gradle/libs.versions.toml

@@ -7,7 +7,6 @@ conductor_version = "3.1.2"
 flowbinding_version = "1.2.0"
 shizuku_version = "12.1.0"
 robolectric_version = "3.1.4"
-sqldelight = "1.5.3"
 
 [libraries]
 android-shortcut-gradle = "com.github.zellius:android-shortcut-gradle-plugin:0.1.2"
@@ -50,7 +49,6 @@ injekt-core = "com.github.inorichi.injekt:injekt-core:65b0440"
 
 coil-core = { module = "io.coil-kt:coil", version.ref = "coil_version" }
 coil-gif = { module = "io.coil-kt:coil-gif", version.ref = "coil_version" }
-coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil_version" }
 
 subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:846abe0"
 image-decoder = "com.github.tachiyomiorg:image-decoder:7481a4a"
@@ -80,7 +78,7 @@ flowbinding-viewpager = { module = "io.github.reactivecircus.flowbinding:flowbin
 
 logcat = "com.squareup.logcat:logcat:0.1"
 
-acra-http = "ch.acra:acra-http:5.9.1"
+acra-http = "ch.acra:acra-http:5.9.3"
 firebase-analytics = "com.google.firebase:firebase-analytics-ktx:20.0.2"
 
 aboutlibraries-core = { module = "com.mikepenz:aboutlibraries-core", version.ref = "aboutlib_version" }
@@ -98,22 +96,18 @@ robolectric-playservices = { module = "org.robolectric:shadows-play-services", v
 
 leakcanary-android = "com.squareup.leakcanary:leakcanary-android:2.7"
 
-sqldelight-android-driver = { module = "com.squareup.sqldelight:android-driver", version.ref ="sqldelight" }
-sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions-jvm", version.ref ="sqldelight" }
-sqldelight-android-paging = { module = "com.squareup.sqldelight:android-paging3-extensions", version.ref ="sqldelight" }
-
 [bundles]
 reactivex = ["rxandroid","rxjava","rxrelay"]
 okhttp = ["okhttp-core","okhttp-logging","okhttp-dnsoverhttps"]
 js-engine = ["quickjs-android", "duktape-android"]
 sqlite = ["sqlitektx", "sqlite-android"]
 nucleus = ["nucleus-core","nucleus-supportv7"]
-coil = ["coil-core","coil-gif","coil-compose"]
+coil = ["coil-core","coil-gif",]
 flowbinding = ["flowbinding-android","flowbinding-appcompat","flowbinding-recyclerview","flowbinding-swiperefreshlayout","flowbinding-viewpager"]
 conductor = ["conductor-core","conductor-viewpager","conductor-support-preference"]
 shizuku = ["shizuku-api","shizuku-provider"]
 robolectric = ["robolectric-core","robolectric-playservices"]
 
 [plugins]
-kotlinter = { id = "org.jmailen.kotlinter", version = "3.6.0"}
+kotlinter = { id = "org.jmailen.kotlinter", version = "3.10.0"}
 versionsx = { id = "com.github.ben-manes.versions", version = "0.42.0"}

+ 0 - 3
settings.gradle.kts

@@ -22,9 +22,6 @@ dependencyResolutionManagement {
         create("androidx") {
             from(files("gradle/androidx.versions.toml"))
         }
-        create("compose") {
-            from(files("gradle/compose.versions.toml"))
-        }
     }
     repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
     repositories {