diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt index 5b18bcd10..05dd2a6ba 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt @@ -2,6 +2,7 @@ package eu.kanade.presentation.browse.anime import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -19,7 +20,6 @@ import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSearchItemRe import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.GlobalAnimeSearchState import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.domain.entries.anime.model.Anime -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.padding diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSearchScreen.kt index 9829db273..a1db4bf93 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSearchScreen.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.browse.anime import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.State import eu.kanade.presentation.browse.GlobalSearchEmptyResultItem @@ -14,7 +15,6 @@ import eu.kanade.tachiyomi.ui.browse.anime.migration.search.MigrateAnimeSearchSt import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSearchItemResult import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.domain.entries.anime.model.Anime -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Scaffold @Composable diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/components/BrowseAnimeSourceList.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/components/BrowseAnimeSourceList.kt index e7386ff21..4265d3a48 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/anime/components/BrowseAnimeSourceList.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/anime/components/BrowseAnimeSourceList.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.browse.anime.components import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -14,7 +15,6 @@ import eu.kanade.presentation.library.EntryListItem import kotlinx.coroutines.flow.StateFlow import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.domain.entries.anime.model.AnimeCover -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.util.plus @Composable diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt index 701ff6088..a8cf3fdd8 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt @@ -2,6 +2,7 @@ package eu.kanade.presentation.browse.manga import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -19,7 +20,6 @@ import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.GlobalMangaSearch import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSearchItemResult import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.domain.entries.manga.model.Manga -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.padding diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSearchScreen.kt index 12db7ad92..4caa6f7e7 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSearchScreen.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.browse.manga import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.State import eu.kanade.presentation.browse.GlobalSearchEmptyResultItem @@ -14,7 +15,6 @@ import eu.kanade.tachiyomi.ui.browse.manga.migration.search.MigrateMangaSearchSt import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSearchItemResult import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.domain.entries.manga.model.Manga -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Scaffold @Composable diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/components/BrowseMangaSourceList.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/components/BrowseMangaSourceList.kt index 517671a27..cfda5c4c2 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/manga/components/BrowseMangaSourceList.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/manga/components/BrowseMangaSourceList.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.browse.manga.components import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -13,7 +14,6 @@ import eu.kanade.presentation.library.EntryListItem import kotlinx.coroutines.flow.StateFlow import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.domain.entries.manga.model.MangaCover -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.util.plus @Composable diff --git a/app/src/main/java/eu/kanade/presentation/category/components/CategoryContent.kt b/app/src/main/java/eu/kanade/presentation/category/components/CategoryContent.kt index 71fc2bc8d..43053e5ef 100644 --- a/app/src/main/java/eu/kanade/presentation/category/components/CategoryContent.kt +++ b/app/src/main/java/eu/kanade/presentation/category/components/CategoryContent.kt @@ -2,13 +2,13 @@ package eu.kanade.presentation.category.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import tachiyomi.domain.category.model.Category -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.padding @Composable diff --git a/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt b/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt index cb76d4f70..c4252adad 100644 --- a/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState @@ -81,7 +82,6 @@ import tachiyomi.domain.items.episode.model.Episode import tachiyomi.domain.items.service.missingItemsCount import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.source.anime.model.StubAnimeSource -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.TwoPanelBox import tachiyomi.presentation.core.components.VerticalFastScroller import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton diff --git a/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt index da1d9ca39..b0cc4ed41 100644 --- a/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState @@ -75,7 +76,6 @@ import tachiyomi.domain.items.chapter.model.Chapter import tachiyomi.domain.items.service.missingItemsCount import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.source.manga.model.StubMangaSource -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.TwoPanelBox import tachiyomi.presentation.core.components.VerticalFastScroller import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt index 62564b7fd..8aefd7fb6 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt @@ -4,6 +4,7 @@ import androidx.annotation.StringRes import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape @@ -50,7 +51,6 @@ import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import eu.kanade.presentation.util.LocalBackPress import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Scaffold import cafe.adriel.voyager.core.screen.Screen as VoyagerScreen diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt index 8b16084c6..12393080d 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt @@ -4,6 +4,7 @@ import android.content.Context import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.rememberScrollState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack @@ -39,7 +40,6 @@ import eu.kanade.tachiyomi.util.system.workManager import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.util.plus diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt index 6983e206f..82ababc88 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons @@ -28,7 +29,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import eu.kanade.tachiyomi.R -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Divider import tachiyomi.presentation.core.util.isScrolledToEnd import tachiyomi.presentation.core.util.isScrolledToStart diff --git a/app/src/main/java/eu/kanade/presentation/more/stats/AnimeStatsScreenContent.kt b/app/src/main/java/eu/kanade/presentation/more/stats/AnimeStatsScreenContent.kt index 6a3e906bf..94435ecdf 100644 --- a/app/src/main/java/eu/kanade/presentation/more/stats/AnimeStatsScreenContent.kt +++ b/app/src/main/java/eu/kanade/presentation/more/stats/AnimeStatsScreenContent.kt @@ -3,6 +3,7 @@ package eu.kanade.presentation.more.stats import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.CollectionsBookmark @@ -19,7 +20,6 @@ import eu.kanade.presentation.more.stats.components.StatsSection import eu.kanade.presentation.more.stats.data.StatsData import eu.kanade.presentation.util.toDurationString import eu.kanade.tachiyomi.R -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.padding import java.util.Locale import kotlin.time.DurationUnit diff --git a/app/src/main/java/eu/kanade/presentation/more/stats/MangaStatsScreenContent.kt b/app/src/main/java/eu/kanade/presentation/more/stats/MangaStatsScreenContent.kt index ca2bdd995..6df431aac 100644 --- a/app/src/main/java/eu/kanade/presentation/more/stats/MangaStatsScreenContent.kt +++ b/app/src/main/java/eu/kanade/presentation/more/stats/MangaStatsScreenContent.kt @@ -3,6 +3,7 @@ package eu.kanade.presentation.more.stats import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.CollectionsBookmark @@ -19,7 +20,6 @@ import eu.kanade.presentation.more.stats.components.StatsSection import eu.kanade.presentation.more.stats.data.StatsData import eu.kanade.presentation.util.toDurationString import eu.kanade.tachiyomi.R -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.padding import java.util.Locale import kotlin.time.DurationUnit diff --git a/app/src/main/java/eu/kanade/presentation/more/storage/StorageScreenContent.kt b/app/src/main/java/eu/kanade/presentation/more/storage/StorageScreenContent.kt index 0df61c617..49ed79ad2 100644 --- a/app/src/main/java/eu/kanade/presentation/more/storage/StorageScreenContent.kt +++ b/app/src/main/java/eu/kanade/presentation/more/storage/StorageScreenContent.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -19,7 +20,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import eu.kanade.presentation.util.isTabletUi import tachiyomi.domain.category.model.Category -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.screens.LoadingScreen import kotlin.random.Random diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupAnime.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupAnime.kt index 8c0ee14fc..f4e2e4c4a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupAnime.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupAnime.kt @@ -37,6 +37,8 @@ data class BackupAnime( @ProtoNumber(103) var viewer_flags: Int = 0, @ProtoNumber(104) var history: List = emptyList(), @ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE, + @ProtoNumber(106) var lastModifiedAt: Long = 0, + @ProtoNumber(107) var favoriteModifiedAt: Long? = null, ) { fun getAnimeImpl(): Anime { return Anime.create().copy( @@ -54,6 +56,8 @@ data class BackupAnime( viewerFlags = this@BackupAnime.viewer_flags.toLong(), episodeFlags = this@BackupAnime.episodeFlags.toLong(), updateStrategy = this@BackupAnime.updateStrategy, + lastModifiedAt = this@BackupAnime.lastModifiedAt, + favoriteModifiedAt = this@BackupAnime.favoriteModifiedAt, ) } @@ -86,6 +90,8 @@ data class BackupAnime( viewer_flags = anime.skipIntroLength, episodeFlags = anime.episodeFlags.toInt(), updateStrategy = anime.updateStrategy, + lastModifiedAt = anime.lastModifiedAt, + favoriteModifiedAt = anime.favoriteModifiedAt, ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt index 472813019..744401a35 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt @@ -20,6 +20,7 @@ data class BackupChapter( // chapterNumber is called number is 1.x @ProtoNumber(9) var chapterNumber: Float = 0F, @ProtoNumber(10) var sourceOrder: Long = 0, + @ProtoNumber(11) var lastModifiedAt: Long = 0, ) { fun toChapterImpl(): Chapter { return Chapter.create().copy( @@ -33,11 +34,13 @@ data class BackupChapter( dateFetch = this@BackupChapter.dateFetch, dateUpload = this@BackupChapter.dateUpload, sourceOrder = this@BackupChapter.sourceOrder, + lastModifiedAt = this@BackupChapter.lastModifiedAt, ) } } -val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, read: Boolean, bookmark: Boolean, lastPageRead: Long, chapterNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long -> +val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, read: Boolean, bookmark: +Boolean, lastPageRead: Long, chapterNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long, lastModifiedAt: Long -> BackupChapter( url = url, name = name, @@ -49,5 +52,6 @@ val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlat dateFetch = dateFetch, dateUpload = dateUpload, sourceOrder = source_order, + lastModifiedAt = lastModifiedAt, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupEpisode.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupEpisode.kt index e534a245d..b6fd63661 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupEpisode.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupEpisode.kt @@ -21,6 +21,7 @@ data class BackupEpisode( // episodeNumber is called number is 1.x @ProtoNumber(9) var episodeNumber: Float = 0F, @ProtoNumber(10) var sourceOrder: Long = 0, + @ProtoNumber(11) var lastModifiedAt: Long = 0, ) { fun toEpisodeImpl(): Episode { return Episode.create().copy( @@ -35,11 +36,13 @@ data class BackupEpisode( dateFetch = this@BackupEpisode.dateFetch, dateUpload = this@BackupEpisode.dateUpload, sourceOrder = this@BackupEpisode.sourceOrder, + lastModifiedAt = this@BackupEpisode.lastModifiedAt, ) } } -val backupEpisodeMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, seen: Boolean, bookmark: Boolean, lastSecondSeen: Long, totalSeconds: Long, episodeNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long -> +val backupEpisodeMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, seen: Boolean, bookmark: +Boolean, lastSecondSeen: Long, totalSeconds: Long, episodeNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long, lastModifiedAt: Long -> BackupEpisode( url = url, name = name, @@ -52,5 +55,6 @@ val backupEpisodeMapper = { _: Long, _: Long, url: String, name: String, scanlat dateFetch = dateFetch, dateUpload = dateUpload, sourceOrder = source_order, + lastModifiedAt = lastModifiedAt, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt index bcc4bee89..ff0bda129 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt @@ -39,6 +39,8 @@ data class BackupManga( @ProtoNumber(103) var viewer_flags: Int? = null, @ProtoNumber(104) var history: List = emptyList(), @ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE, + @ProtoNumber(106) var lastModifiedAt: Long = 0, + @ProtoNumber(107) var favoriteModifiedAt: Long? = null, ) { fun getMangaImpl(): Manga { return Manga.create().copy( @@ -56,6 +58,8 @@ data class BackupManga( viewerFlags = (this@BackupManga.viewer_flags ?: this@BackupManga.viewer).toLong(), chapterFlags = this@BackupManga.chapterFlags.toLong(), updateStrategy = this@BackupManga.updateStrategy, + lastModifiedAt = this@BackupManga.lastModifiedAt, + favoriteModifiedAt = this@BackupManga.favoriteModifiedAt, ) } @@ -89,6 +93,8 @@ data class BackupManga( viewer_flags = manga.viewerFlags.toInt(), chapterFlags = manga.chapterFlags.toInt(), updateStrategy = manga.updateStrategy, + lastModifiedAt = manga.lastModifiedAt, + favoriteModifiedAt = manga.favoriteModifiedAt, ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/anime/Episode.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/anime/Episode.kt index 1beea7895..d171bba81 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/anime/Episode.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/anime/Episode.kt @@ -21,6 +21,8 @@ interface Episode : SEpisode, Serializable { var date_fetch: Long var source_order: Int + + var last_modified: Long } fun Episode.toDomainEpisode(): DomainEpisode? { @@ -39,5 +41,6 @@ fun Episode.toDomainEpisode(): DomainEpisode? { dateUpload = date_upload, episodeNumber = episode_number, scanlator = scanlator, + lastModifiedAt = last_modified, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/anime/EpisodeImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/anime/EpisodeImpl.kt index 6307276f5..485ddb200 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/anime/EpisodeImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/anime/EpisodeImpl.kt @@ -28,6 +28,8 @@ class EpisodeImpl : Episode { override var source_order: Int = 0 + override var last_modified: Long = 0 + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || javaClass != other.javaClass) return false diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/manga/Chapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/manga/Chapter.kt index 8c738fcb9..9242061b9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/manga/Chapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/manga/Chapter.kt @@ -19,6 +19,8 @@ interface Chapter : SChapter, Serializable { var date_fetch: Long var source_order: Int + + var last_modified: Long } fun Chapter.toDomainChapter(): DomainChapter? { @@ -36,5 +38,6 @@ fun Chapter.toDomainChapter(): DomainChapter? { dateUpload = date_upload, chapterNumber = chapter_number, scanlator = scanlator, + lastModifiedAt = last_modified, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/manga/ChapterImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/manga/ChapterImpl.kt index 27a9f15fe..e5a8eb359 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/manga/ChapterImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/manga/ChapterImpl.kt @@ -26,6 +26,8 @@ class ChapterImpl : Chapter { override var source_order: Int = 0 + override var last_modified: Long = 0 + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || javaClass != other.javaClass) return false diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/SourceFilterAnimeDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/SourceFilterAnimeDialog.kt index b4bc84424..e4fec475b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/SourceFilterAnimeDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/SourceFilterAnimeDialog.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Button import androidx.compose.material3.Divider @@ -26,7 +27,6 @@ import tachiyomi.domain.entries.TriStateFilter import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CollapsibleBox import tachiyomi.presentation.core.components.HeadingItem -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.TextItem diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/SourceFilterMangaDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/SourceFilterMangaDialog.kt index 8ea676007..a72cbbdb7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/SourceFilterMangaDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/SourceFilterMangaDialog.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme @@ -24,7 +25,6 @@ import tachiyomi.domain.entries.TriStateFilter import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CollapsibleBox import tachiyomi.presentation.core.components.HeadingItem -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.TextItem import tachiyomi.presentation.core.components.material.Button diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/player/settings/dialogs/EpisodeListDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/player/settings/dialogs/EpisodeListDialog.kt index d0b09ec50..06a7263c5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/player/settings/dialogs/EpisodeListDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/player/settings/dialogs/EpisodeListDialog.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons @@ -38,7 +39,6 @@ import eu.kanade.tachiyomi.data.database.models.anime.Episode import eu.kanade.tachiyomi.ui.entries.anime.episodeDecimalFormat import eu.kanade.tachiyomi.util.lang.toRelativeString import tachiyomi.domain.entries.anime.model.Anime -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.VerticalFastScroller import tachiyomi.presentation.core.components.material.ReadItemAlpha import tachiyomi.presentation.core.components.material.padding diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 92163033d..4622af657 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -235,13 +235,18 @@ class ReaderActivity : BaseActivity() { readingModeToast?.cancel() } + override fun onPause() { + viewModel.flushReadTimer() + super.onPause() + } + /** * Set menu visibility again on activity resume to apply immersive mode again if needed. * Helps with rotations. */ override fun onResume() { super.onResume() - viewModel.setReadStartTime() + viewModel.restartReadTimer() setMenuVisibility(viewModel.state.value.menuVisible, animate = false) } @@ -591,7 +596,7 @@ class ReaderActivity : BaseActivity() { * Sets the visibility of the menu according to [visible] and with an optional parameter to * [animate] the views. */ - fun setMenuVisibility(visible: Boolean, animate: Boolean = true) { + private fun setMenuVisibility(visible: Boolean, animate: Boolean = true) { viewModel.showMenus(visible) if (visible) { windowInsetsController.show(WindowInsetsCompat.Type.systemBars()) @@ -796,7 +801,6 @@ class ReaderActivity : BaseActivity() { * Called from the viewer whenever a [page] is marked as active. It updates the values of the * bottom menu and delegates the change to the presenter. */ - @SuppressLint("SetTextI18n") fun onPageSelected(page: ReaderPage) { viewModel.onPageSelected(page) } @@ -814,7 +818,7 @@ class ReaderActivity : BaseActivity() { * the viewer is reaching the beginning or end of a chapter or the transition page is active. */ fun requestPreloadChapter(chapter: ReaderChapter) { - lifecycleScope.launchIO { viewModel.preloadChapter(chapter) } + lifecycleScope.launchIO { viewModel.preload(chapter) } } /** @@ -901,7 +905,7 @@ class ReaderActivity : BaseActivity() { /** * Updates viewer inset depending on fullscreen reader preferences. */ - fun updateViewerInset(fullscreen: Boolean) { + private fun updateViewerInset(fullscreen: Boolean) { viewModel.state.value.viewer?.getView()?.applyInsetter { if (!fullscreen) { type(navigationBars = true, statusBars = true) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index 6c8650e8e..65d5e891a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -57,7 +57,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import logcat.LogPriority import tachiyomi.core.util.lang.launchIO @@ -314,12 +313,15 @@ class ReaderViewModel( * Called when the user changed to the given [chapter] when changing pages from the viewer. * It's used only to set this chapter as active. */ - private suspend fun loadNewChapter(chapter: ReaderChapter) { + private fun loadNewChapter(chapter: ReaderChapter) { val loader = loader ?: return - logcat { "Loading ${chapter.chapter.url}" } + viewModelScope.launchIO { + logcat { "Loading ${chapter.chapter.url}" } + + flushReadTimer() + restartReadTimer() - withIOContext { try { loadChapter(loader, chapter) } catch (e: Throwable) { @@ -358,7 +360,7 @@ class ReaderViewModel( * Called when the viewers decide it's a good time to preload a [chapter] and improve the UX so * that the user doesn't have to wait too long to continue reading. */ - private suspend fun preload(chapter: ReaderChapter) { + suspend fun preload(chapter: ReaderChapter) { if (chapter.state is ReaderChapter.State.Loaded || chapter.state == ReaderChapter.State.Loading) { return } @@ -397,9 +399,7 @@ class ReaderViewModel( fun onViewerLoaded(viewer: Viewer?) { mutableState.update { - it.copy( - viewer = viewer, - ) + it.copy(viewer = viewer) } } @@ -414,31 +414,19 @@ class ReaderViewModel( return } - val currentChapters = state.value.viewerChapters ?: return - val pages = page.chapter.pages ?: return val selectedChapter = page.chapter + val pages = selectedChapter.pages ?: return // Save last page read and mark as read if needed - saveReadingProgress() - mutableState.update { - it.copy( - currentPage = page.index + 1, - ) - } - if (!incognitoMode) { - selectedChapter.chapter.last_page_read = page.index - if (selectedChapter.pages?.lastIndex == page.index) { - selectedChapter.chapter.read = true - updateTrackChapterRead(selectedChapter) - deleteChapterIfNeeded(selectedChapter) - } + viewModelScope.launchNonCancellable { + updateChapterProgress(page.index) } - if (selectedChapter != currentChapters.currChapter) { + if (selectedChapter != getCurrentChapter()) { logcat { "Setting ${selectedChapter.chapter.url} as active" } - setReadStartTime() - viewModelScope.launch { loadNewChapter(selectedChapter) } + loadNewChapter(selectedChapter) } + val inDownloadRange = page.number.toDouble() / pages.size > 0.25 if (inDownloadRange) { downloadNextChapters() @@ -508,42 +496,54 @@ class ReaderViewModel( } /** - * Called when reader chapter is changed in reader or when activity is paused. + * Saves the chapter progress (last read page and whether it's read) + * if incognito mode isn't on. */ - private fun saveReadingProgress() { - getCurrentChapter()?.let { - viewModelScope.launchNonCancellable { - saveChapterProgress(it) - saveChapterHistory(it) + private suspend fun updateChapterProgress(pageIndex: Int) { + val readerChapter = getCurrentChapter() ?: return + + mutableState.update { + it.copy(currentPage = pageIndex + 1) + } + + if (!incognitoMode) { + readerChapter.requestedPage = pageIndex + readerChapter.chapter.last_page_read = pageIndex + + if (readerChapter.pages?.lastIndex == pageIndex) { + readerChapter.chapter.read = true + updateTrackChapterRead(readerChapter) + deleteChapterIfNeeded(readerChapter) } + + updateChapter.await( + ChapterUpdate( + id = readerChapter.chapter.id!!, + read = readerChapter.chapter.read, + bookmark = readerChapter.chapter.bookmark, + lastPageRead = readerChapter.chapter.last_page_read.toLong(), + ), + ) + } + } + + fun restartReadTimer() { + chapterReadStartTime = Date().time + } + + fun flushReadTimer() { + viewModelScope.launchNonCancellable { + updateHistory() } } /** - * Saves this [readerChapter] progress (last read page and whether it's read). - * if incognito mode isn't on. + * Saves the chapter last read history if incognito mode isn't on. */ - private suspend fun saveChapterProgress(readerChapter: ReaderChapter) { - if (!incognitoMode) return - - val chapter = readerChapter.chapter - readerChapter.requestedPage = chapter.last_page_read - updateChapter.await( - ChapterUpdate( - id = chapter.id!!, - read = chapter.read, - bookmark = chapter.bookmark, - lastPageRead = chapter.last_page_read.toLong(), - ), - ) - } - - /** - * Saves this [readerChapter] last read history if incognito mode isn't on. - */ - private suspend fun saveChapterHistory(readerChapter: ReaderChapter) { + private suspend fun updateHistory() { if (incognitoMode) return + val readerChapter = getCurrentChapter() ?: return val chapterId = readerChapter.chapter.id!! val readAt = Date() val sessionReadDuration = chapterReadStartTime?.let { readAt.time - it } ?: 0 @@ -552,17 +552,6 @@ class ReaderViewModel( chapterReadStartTime = null } - fun setReadStartTime() { - chapterReadStartTime = Date().time - } - - /** - * Called from the activity to preload the given [chapter]. - */ - suspend fun preloadChapter(chapter: ReaderChapter) { - preload(chapter) - } - /** * Called from the activity to load and set the next chapter as active. */ diff --git a/data/src/main/java/tachiyomi/data/entries/anime/AnimeMapper.kt b/data/src/main/java/tachiyomi/data/entries/anime/AnimeMapper.kt index 40856953b..6ead4695e 100644 --- a/data/src/main/java/tachiyomi/data/entries/anime/AnimeMapper.kt +++ b/data/src/main/java/tachiyomi/data/entries/anime/AnimeMapper.kt @@ -4,8 +4,8 @@ import eu.kanade.tachiyomi.source.model.UpdateStrategy import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.domain.library.anime.LibraryAnime -val animeMapper: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long) -> Anime = - { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, episodeFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval -> +val animeMapper: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?) -> Anime = + { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, episodeFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt -> Anime( id = id, source = source, @@ -27,11 +27,13 @@ val animeMapper: (Long, Long, String, String?, String?, String?, List?, thumbnailUrl = thumbnailUrl, updateStrategy = updateStrategy, initialized = initialized, + lastModifiedAt = lastModifiedAt, + favoriteModifiedAt = favoriteModifiedAt, ) } -val libraryAnime: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long, Long, Long, Long, Long, Long) -> LibraryAnime = - { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, episodeFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, totalCount, seenCount, latestUpload, episodeFetchedAt, lastSeen, bookmarkCount, category -> +val libraryAnime: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Long, Long, Long, Long, Long, Long) -> LibraryAnime = + { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, episodeFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, seenCount, latestUpload, episodeFetchedAt, lastSeen, bookmarkCount, category -> LibraryAnime( anime = animeMapper( id, @@ -54,6 +56,8 @@ val libraryAnime: (Long, Long, String, String?, String?, String?, List?, dateAdded, updateStrategy, calculateInterval, + lastModifiedAt, + favoriteModifiedAt, ), category = category, totalEpisodes = totalCount, diff --git a/data/src/main/java/tachiyomi/data/entries/manga/MangaMapper.kt b/data/src/main/java/tachiyomi/data/entries/manga/MangaMapper.kt index d80c2da4b..8db93fecf 100644 --- a/data/src/main/java/tachiyomi/data/entries/manga/MangaMapper.kt +++ b/data/src/main/java/tachiyomi/data/entries/manga/MangaMapper.kt @@ -4,8 +4,8 @@ import eu.kanade.tachiyomi.source.model.UpdateStrategy import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.domain.library.manga.LibraryManga -val mangaMapper: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long) -> Manga = - { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval -> +val mangaMapper: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?) -> Manga = + { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt -> Manga( id = id, source = source, @@ -27,11 +27,13 @@ val mangaMapper: (Long, Long, String, String?, String?, String?, List?, thumbnailUrl = thumbnailUrl, updateStrategy = updateStrategy, initialized = initialized, + lastModifiedAt = lastModifiedAt, + favoriteModifiedAt = favoriteModifiedAt, ) } -val libraryManga: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long, Long, Long, Long, Long, Long) -> LibraryManga = - { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category -> +val libraryManga: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Long, Long, Long, Long, Long, Long) -> LibraryManga = + { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category -> LibraryManga( manga = mangaMapper( id, @@ -54,6 +56,8 @@ val libraryManga: (Long, Long, String, String?, String?, String?, List?, dateAdded, updateStrategy, calculateInterval, + lastModifiedAt, + favoriteModifiedAt, ), category = category, totalChapters = totalCount, diff --git a/data/src/main/java/tachiyomi/data/items/chapter/ChapterMapper.kt b/data/src/main/java/tachiyomi/data/items/chapter/ChapterMapper.kt index 0f837988b..b1dfe4f6d 100644 --- a/data/src/main/java/tachiyomi/data/items/chapter/ChapterMapper.kt +++ b/data/src/main/java/tachiyomi/data/items/chapter/ChapterMapper.kt @@ -2,8 +2,8 @@ package tachiyomi.data.items.chapter import tachiyomi.domain.items.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 -> +val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Float, Long, Long, Long, Long) -> Chapter = + { id, mangaId, url, name, scanlator, read, bookmark, lastPageRead, chapterNumber, sourceOrder, dateFetch, dateUpload, lastModifiedAt -> Chapter( id = id, mangaId = mangaId, @@ -17,5 +17,6 @@ val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, dateUpload = dateUpload, chapterNumber = chapterNumber, scanlator = scanlator, + lastModifiedAt = lastModifiedAt, ) } diff --git a/data/src/main/java/tachiyomi/data/items/episode/EpisodeMapper.kt b/data/src/main/java/tachiyomi/data/items/episode/EpisodeMapper.kt index a7c651b57..4aea882f4 100644 --- a/data/src/main/java/tachiyomi/data/items/episode/EpisodeMapper.kt +++ b/data/src/main/java/tachiyomi/data/items/episode/EpisodeMapper.kt @@ -2,8 +2,8 @@ package tachiyomi.data.items.episode import tachiyomi.domain.items.episode.model.Episode -val episodeMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Long, Float, Long, Long, Long) -> Episode = - { id, animeId, url, name, scanlator, seen, bookmark, lastSecondSeen, totalSeconds, episodeNumber, sourceOrder, dateFetch, dateUpload -> +val episodeMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Long, Float, Long, Long, Long, Long) -> Episode = + { id, animeId, url, name, scanlator, seen, bookmark, lastSecondSeen, totalSeconds, episodeNumber, sourceOrder, dateFetch, dateUpload, lastModifiedAt -> Episode( id = id, animeId = animeId, @@ -18,5 +18,6 @@ val episodeMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, dateUpload = dateUpload, episodeNumber = episodeNumber, scanlator = scanlator, + lastModifiedAt = lastModifiedAt, ) } diff --git a/data/src/main/sqldelight/data/chapters.sq b/data/src/main/sqldelight/data/chapters.sq index 165916241..b995116fb 100644 --- a/data/src/main/sqldelight/data/chapters.sq +++ b/data/src/main/sqldelight/data/chapters.sq @@ -11,6 +11,7 @@ CREATE TABLE chapters( source_order INTEGER NOT NULL, date_fetch INTEGER AS Long NOT NULL, date_upload INTEGER AS Long NOT NULL, + last_modified_at INTEGER AS Long NOT NULL, FOREIGN KEY(manga_id) REFERENCES mangas (_id) ON DELETE CASCADE ); @@ -18,6 +19,15 @@ CREATE TABLE chapters( 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 TRIGGER update_last_modified_at_chapters +AFTER UPDATE ON chapters +FOR EACH ROW +BEGIN + UPDATE chapters + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + getChapterById: SELECT * FROM chapters @@ -50,8 +60,8 @@ DELETE FROM chapters WHERE _id IN :chapterIds; insert: -INSERT INTO chapters(manga_id,url,name,scanlator,read,bookmark,last_page_read,chapter_number,source_order,date_fetch,date_upload) -VALUES (:mangaId,:url,:name,:scanlator,:read,:bookmark,:lastPageRead,:chapterNumber,:sourceOrder,:dateFetch,:dateUpload); +INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order,date_fetch, date_upload, last_modified_at) +VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, strftime('%s', 'now')); update: UPDATE chapters diff --git a/data/src/main/sqldelight/data/mangas.sq b/data/src/main/sqldelight/data/mangas.sq index b5661e061..1ed66298f 100644 --- a/data/src/main/sqldelight/data/mangas.sq +++ b/data/src/main/sqldelight/data/mangas.sq @@ -22,12 +22,31 @@ CREATE TABLE mangas( cover_last_modified INTEGER AS Long NOT NULL, date_added INTEGER AS Long NOT NULL, update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0, - calculate_interval INTEGER DEFAULT 0 NOT NULL + calculate_interval INTEGER DEFAULT 0 NOT NULL, + last_modified_at INTEGER AS Long NOT NULL, + favorite_modified_at INTEGER AS Long ); CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1; CREATE INDEX mangas_url_index ON mangas(url); +CREATE TRIGGER update_favorite_modified_at_mangas +AFTER UPDATE OF favorite ON mangas +BEGIN + UPDATE mangas + SET favorite_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +CREATE TRIGGER update_last_modified_at_mangas +AFTER UPDATE ON mangas +FOR EACH ROW +BEGIN + UPDATE mangas + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + getMangaById: SELECT * FROM mangas @@ -45,6 +64,15 @@ SELECT * FROM mangas WHERE favorite = 1; +getAllManga: +SELECT * +FROM mangas; + +getMangasWithFavoriteTimestamp: +SELECT * +FROM mangas +WHERE favorite_modified_at IS NOT NULL; + getSourceIdWithFavoriteCount: SELECT source, @@ -81,8 +109,8 @@ DELETE FROM mangas WHERE favorite = 0 AND source IN :sourceIds; insert: -INSERT INTO mangas(source,url,artist,author,description,genre,title,status,thumbnail_url,favorite,last_update,next_update,initialized,viewer,chapter_flags,cover_last_modified,date_added,update_strategy,calculate_interval) -VALUES (:source,:url,:artist,:author,:description,:genre,:title,:status,:thumbnailUrl,:favorite,:lastUpdate,:nextUpdate,:initialized,:viewerFlags,:chapterFlags,:coverLastModified,:dateAdded,:updateStrategy,:calculateInterval); +INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at) +VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, strftime('%s', 'now')); update: UPDATE mangas SET diff --git a/data/src/main/sqldelight/data/mangas_categories.sq b/data/src/main/sqldelight/data/mangas_categories.sq index a97c9d3ca..c10387a6a 100644 --- a/data/src/main/sqldelight/data/mangas_categories.sq +++ b/data/src/main/sqldelight/data/mangas_categories.sq @@ -2,15 +2,25 @@ CREATE TABLE mangas_categories( _id INTEGER NOT NULL PRIMARY KEY, manga_id INTEGER NOT NULL, category_id INTEGER NOT NULL, + last_modified_at INTEGER AS Long NOT NULL, FOREIGN KEY(category_id) REFERENCES categories (_id) ON DELETE CASCADE, FOREIGN KEY(manga_id) REFERENCES mangas (_id) ON DELETE CASCADE ); +CREATE TRIGGER update_last_modified_at_mangas_categories +AFTER UPDATE ON mangas_categories +FOR EACH ROW +BEGIN + UPDATE mangas_categories + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + insert: -INSERT INTO mangas_categories(manga_id, category_id) -VALUES (:mangaId, :categoryId); +INSERT INTO mangas_categories(manga_id, category_id, last_modified_at) +VALUES (:mangaId, :categoryId, strftime('%s', 'now')); deleteMangaCategoryByMangaId: DELETE FROM mangas_categories diff --git a/data/src/main/sqldelight/migrations/26.sqm b/data/src/main/sqldelight/migrations/26.sqm new file mode 100644 index 000000000..0cd1c4c07 --- /dev/null +++ b/data/src/main/sqldelight/migrations/26.sqm @@ -0,0 +1,49 @@ +ALTER TABLE mangas ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0; +ALTER TABLE mangas ADD COLUMN favorite_modified_at INTEGER AS Long; +ALTER TABLE mangas_categories ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0; +ALTER TABLE chapters ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0; + +UPDATE mangas SET last_modified_at = strftime('%s', 'now'); +UPDATE mangas SET favorite_modified_at = strftime('%s', 'now') WHERE favorite = 1; +UPDATE mangas_categories SET last_modified_at = strftime('%s', 'now'); +UPDATE chapters SET last_modified_at = strftime('%s', 'now'); + +-- Create triggers +DROP TRIGGER IF EXISTS update_last_modified_at_mangas; +CREATE TRIGGER update_last_modified_at_mangas +AFTER UPDATE ON mangas +FOR EACH ROW +BEGIN + UPDATE mangas + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +DROP TRIGGER IF EXISTS update_favorite_modified_at_mangas; +CREATE TRIGGER update_last_favorited_at_mangas +AFTER UPDATE OF favorite ON mangas +BEGIN + UPDATE mangas + SET favorite_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +DROP TRIGGER IF EXISTS update_last_modified_at_chapters; +CREATE TRIGGER update_last_modified_at_chapters +AFTER UPDATE ON chapters +FOR EACH ROW +BEGIN + UPDATE chapters + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +DROP TRIGGER IF EXISTS update_last_modified_at_mangas_categories; +CREATE TRIGGER update_last_modified_at_mangas_categories +AFTER UPDATE ON mangas_categories +FOR EACH ROW +BEGIN + UPDATE mangas_categories + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; \ No newline at end of file diff --git a/data/src/main/sqldelightanime/dataanime/animes.sq b/data/src/main/sqldelightanime/dataanime/animes.sq index 91937df83..dcab1e0ad 100644 --- a/data/src/main/sqldelightanime/dataanime/animes.sq +++ b/data/src/main/sqldelightanime/dataanime/animes.sq @@ -22,12 +22,31 @@ CREATE TABLE animes( cover_last_modified INTEGER AS Long NOT NULL, date_added INTEGER AS Long NOT NULL, update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0, - calculate_interval INTEGER DEFAULT 0 NOT NULL + calculate_interval INTEGER DEFAULT 0 NOT NULL, + last_modified_at INTEGER AS Long NOT NULL, + favorite_modified_at INTEGER AS Long ); CREATE INDEX animelib_favorite_index ON animes(favorite) WHERE favorite = 1; CREATE INDEX animes_url_index ON animes(url); +CREATE TRIGGER update_favorite_modified_at_animes +AFTER UPDATE OF favorite ON animes +BEGIN + UPDATE animes + SET favorite_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +CREATE TRIGGER update_last_modified_at_animes +AFTER UPDATE ON animes +FOR EACH ROW +BEGIN + UPDATE animes + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + getAnimeById: SELECT * FROM animes @@ -45,6 +64,15 @@ SELECT * FROM animes WHERE favorite = 1; +getAllAnime: +SELECT * +FROM animes; + +getAnimesWithFavoriteTimestamp: +SELECT * +FROM animes +WHERE favorite_modified_at IS NOT NULL; + getAnimeSourceIdWithFavoriteCount: SELECT source, @@ -81,8 +109,8 @@ DELETE FROM animes WHERE favorite = 0 AND source IN :sourceIds; insert: -INSERT INTO animes(source,url,artist,author,description,genre,title,status,thumbnail_url,favorite,last_update,next_update,initialized,viewer,episode_flags,cover_last_modified,date_added,update_strategy,calculate_interval) -VALUES (:source,:url,:artist,:author,:description,:genre,:title,:status,:thumbnailUrl,:favorite,:lastUpdate,:nextUpdate,:initialized,:viewerFlags,:episodeFlags,:coverLastModified,:dateAdded,:updateStrategy,:calculateInterval); +INSERT INTO animes(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, episode_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at) +VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :episodeFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, strftime('%s', 'now')); update: UPDATE animes SET diff --git a/data/src/main/sqldelightanime/dataanime/animes_categories.sq b/data/src/main/sqldelightanime/dataanime/animes_categories.sq index c3745d049..fe1e5ae05 100644 --- a/data/src/main/sqldelightanime/dataanime/animes_categories.sq +++ b/data/src/main/sqldelightanime/dataanime/animes_categories.sq @@ -2,15 +2,25 @@ CREATE TABLE animes_categories( _id INTEGER NOT NULL PRIMARY KEY, anime_id INTEGER NOT NULL, category_id INTEGER NOT NULL, + last_modified_at INTEGER AS Long NOT NULL, FOREIGN KEY(category_id) REFERENCES categories (_id) ON DELETE CASCADE, FOREIGN KEY(anime_id) REFERENCES animes (_id) ON DELETE CASCADE ); +CREATE TRIGGER update_last_modified_at_animes_categories +AFTER UPDATE ON animes_categories +FOR EACH ROW +BEGIN + UPDATE animes_categories + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + insert: -INSERT INTO animes_categories(anime_id, category_id) -VALUES (:animeId, :categoryId); +INSERT INTO animes_categories(anime_id, category_id, last_modified_at) +VALUES (:animeId, :categoryId, strftime('%s', 'now')); deleteAnimeCategoryByAnimeId: DELETE FROM animes_categories diff --git a/data/src/main/sqldelightanime/dataanime/episodes.sq b/data/src/main/sqldelightanime/dataanime/episodes.sq index aa68183f6..87876091d 100644 --- a/data/src/main/sqldelightanime/dataanime/episodes.sq +++ b/data/src/main/sqldelightanime/dataanime/episodes.sq @@ -12,6 +12,7 @@ CREATE TABLE episodes( source_order INTEGER NOT NULL, date_fetch INTEGER AS Long NOT NULL, date_upload INTEGER AS Long NOT NULL, + last_modified_at INTEGER AS Long NOT NULL, FOREIGN KEY(anime_id) REFERENCES animes (_id) ON DELETE CASCADE ); @@ -19,6 +20,15 @@ CREATE TABLE episodes( CREATE INDEX episodes_anime_id_index ON episodes(anime_id); CREATE INDEX episodes_unseen_by_anime_index ON episodes(anime_id, seen) WHERE seen = 0; +CREATE TRIGGER update_last_modified_at_episodes +AFTER UPDATE ON episodes +FOR EACH ROW +BEGIN + UPDATE episodes + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + getEpisodeById: SELECT * FROM episodes @@ -52,8 +62,8 @@ DELETE FROM episodes WHERE _id IN :episodeIds; insert: -INSERT INTO episodes(anime_id,url,name,scanlator,seen,bookmark,last_second_seen,total_seconds,episode_number,source_order,date_fetch,date_upload) -VALUES (:animeId,:url,:name,:scanlator,:seen,:bookmark,:lastSecondSeen,:totalSeconds,:episodeNumber,:sourceOrder,:dateFetch,:dateUpload); +INSERT INTO episodes(anime_id, url, name, scanlator, seen, bookmark, last_second_seen, total_seconds, episode_number, source_order, date_fetch, date_upload, last_modified_at) +VALUES (:animeId, :url, :name, :scanlator, :seen, :bookmark, :lastSecondSeen, :totalSeconds, :episodeNumber, :sourceOrder, :dateFetch, :dateUpload, strftime('%s', 'now')); update: UPDATE episodes diff --git a/data/src/main/sqldelightanime/migrations/126.sqm b/data/src/main/sqldelightanime/migrations/126.sqm new file mode 100644 index 000000000..ea7e2b40c --- /dev/null +++ b/data/src/main/sqldelightanime/migrations/126.sqm @@ -0,0 +1,49 @@ +ALTER TABLE animes ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0; +ALTER TABLE animes ADD COLUMN favorite_modified_at INTEGER AS Long; +ALTER TABLE animes_categories ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0; +ALTER TABLE episodes ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0; + +UPDATE animes SET last_modified_at = strftime('%s', 'now'); +UPDATE animes SET favorite_modified_at = strftime('%s', 'now') WHERE favorite = 1; +UPDATE animes_categories SET last_modified_at = strftime('%s', 'now'); +UPDATE episodes SET last_modified_at = strftime('%s', 'now'); + +-- Create triggers +DROP TRIGGER IF EXISTS update_last_modified_at_mangas; +CREATE TRIGGER update_last_modified_at_mangas +AFTER UPDATE ON animes +FOR EACH ROW +BEGIN + UPDATE animes + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +DROP TRIGGER IF EXISTS update_favorite_modified_at_mangas; +CREATE TRIGGER update_last_favorited_at_mangas +AFTER UPDATE OF favorite ON animes +BEGIN + UPDATE animes + SET favorite_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +DROP TRIGGER IF EXISTS update_last_modified_at_chapters; +CREATE TRIGGER update_last_modified_at_chapters +AFTER UPDATE ON episodes +FOR EACH ROW +BEGIN + UPDATE episodes + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +DROP TRIGGER IF EXISTS update_last_modified_at_mangas_categories; +CREATE TRIGGER update_last_modified_at_mangas_categories +AFTER UPDATE ON animes_categories +FOR EACH ROW +BEGIN + UPDATE animes_categories + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; \ No newline at end of file diff --git a/domain/src/main/java/tachiyomi/domain/entries/anime/model/Anime.kt b/domain/src/main/java/tachiyomi/domain/entries/anime/model/Anime.kt index ede00f275..18a92d417 100644 --- a/domain/src/main/java/tachiyomi/domain/entries/anime/model/Anime.kt +++ b/domain/src/main/java/tachiyomi/domain/entries/anime/model/Anime.kt @@ -26,6 +26,8 @@ data class Anime( val thumbnailUrl: String?, val updateStrategy: UpdateStrategy, val initialized: Boolean, + val lastModifiedAt: Long, + val favoriteModifiedAt: Long?, ) : Serializable { val sorting: Long @@ -129,6 +131,8 @@ data class Anime( thumbnailUrl = null, updateStrategy = UpdateStrategy.ALWAYS_UPDATE, initialized = false, + lastModifiedAt = 0L, + favoriteModifiedAt = null, ) } } diff --git a/domain/src/main/java/tachiyomi/domain/entries/manga/model/Manga.kt b/domain/src/main/java/tachiyomi/domain/entries/manga/model/Manga.kt index 354abffc5..ff09e369b 100644 --- a/domain/src/main/java/tachiyomi/domain/entries/manga/model/Manga.kt +++ b/domain/src/main/java/tachiyomi/domain/entries/manga/model/Manga.kt @@ -25,6 +25,8 @@ data class Manga( val thumbnailUrl: String?, val updateStrategy: UpdateStrategy, val initialized: Boolean, + val lastModifiedAt: Long, + val favoriteModifiedAt: Long?, ) : Serializable { val sorting: Long @@ -110,6 +112,8 @@ data class Manga( thumbnailUrl = null, updateStrategy = UpdateStrategy.ALWAYS_UPDATE, initialized = false, + lastModifiedAt = 0L, + favoriteModifiedAt = null, ) } } diff --git a/domain/src/main/java/tachiyomi/domain/items/chapter/model/Chapter.kt b/domain/src/main/java/tachiyomi/domain/items/chapter/model/Chapter.kt index 83c5bd187..d66aee367 100644 --- a/domain/src/main/java/tachiyomi/domain/items/chapter/model/Chapter.kt +++ b/domain/src/main/java/tachiyomi/domain/items/chapter/model/Chapter.kt @@ -13,6 +13,7 @@ data class Chapter( val dateUpload: Long, val chapterNumber: Float, val scanlator: String?, + val lastModifiedAt: Long, ) { val isRecognizedNumber: Boolean get() = chapterNumber >= 0f @@ -31,6 +32,7 @@ data class Chapter( dateUpload = -1, chapterNumber = -1f, scanlator = null, + lastModifiedAt = 0, ) } } diff --git a/domain/src/main/java/tachiyomi/domain/items/episode/model/Episode.kt b/domain/src/main/java/tachiyomi/domain/items/episode/model/Episode.kt index 9e8962214..7a36eb985 100644 --- a/domain/src/main/java/tachiyomi/domain/items/episode/model/Episode.kt +++ b/domain/src/main/java/tachiyomi/domain/items/episode/model/Episode.kt @@ -14,6 +14,7 @@ data class Episode( val dateUpload: Long, val episodeNumber: Float, val scanlator: String?, + val lastModifiedAt: Long, ) { val isRecognizedNumber: Boolean get() = episodeNumber >= 0f @@ -33,6 +34,7 @@ data class Episode( dateUpload = -1, episodeNumber = -1f, scanlator = null, + lastModifiedAt = 0, ) } } diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 99769e9b6..1c5c86a92 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,7 +1,7 @@ [versions] compiler = "1.4.8" -compose-bom = "2023.06.00-alpha01" -accompanist = "0.31.4-beta" +compose-bom = "2023.07.00-alpha01" +accompanist = "0.31.5-beta" [libraries] activity = "androidx.activity:activity-compose:1.7.2" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 50d962e79..d8f0be4e9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "10.8.0" +aboutlib_version = "10.8.2" okhttp_version = "5.0.0-alpha.11" shizuku_version = "12.2.0" sqlite = "2.3.1" @@ -57,7 +57,7 @@ flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013 photoview = "com.github.chrisbanes:PhotoView:2.3.0" directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" insetter = "dev.chrisbanes.insetter:insetter:0.6.1" -compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.0.3" +compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.0.4" compose-simpleicons = "br.com.devsrsouza.compose.icons.android:simple-icons:1.0.0" swipe = "me.saket.swipe:swipe:1.2.0" diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyGrid.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyGrid.kt index 085caf59f..f0061e11a 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyGrid.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyGrid.kt @@ -1,6 +1,5 @@ package tachiyomi.presentation.core.components -import androidx.compose.foundation.gestures.FlingBehavior import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.grid.GridCells @@ -14,7 +13,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import tachiyomi.presentation.core.util.flingBehaviorIgnoringMotionScale @Composable fun FastScrollLazyVerticalGrid( @@ -31,7 +29,6 @@ fun FastScrollLazyVerticalGrid( verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, - flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(), userScrollEnabled: Boolean = true, content: LazyGridScope.() -> Unit, ) { @@ -54,7 +51,6 @@ fun FastScrollLazyVerticalGrid( reverseLayout = reverseLayout, verticalArrangement = verticalArrangement, horizontalArrangement = horizontalArrangement, - flingBehavior = flingBehavior, userScrollEnabled = userScrollEnabled, content = content, ) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyList.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyList.kt index afce11140..e5d27e0e1 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyList.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyList.kt @@ -1,9 +1,9 @@ package tachiyomi.presentation.core.components -import androidx.compose.foundation.gestures.FlingBehavior import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState @@ -15,38 +15,6 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.dp import tachiyomi.presentation.core.util.drawVerticalScrollbar -import tachiyomi.presentation.core.util.flingBehaviorIgnoringMotionScale - -/** - * LazyColumn with fling animation fix - * - * @see flingBehaviorIgnoringMotionScale - */ -@Composable -fun LazyColumn( - modifier: Modifier = Modifier, - state: LazyListState = rememberLazyListState(), - contentPadding: PaddingValues = PaddingValues(0.dp), - reverseLayout: Boolean = false, - verticalArrangement: Arrangement.Vertical = - if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, - horizontalAlignment: Alignment.Horizontal = Alignment.Start, - flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(), - userScrollEnabled: Boolean = true, - content: LazyListScope.() -> Unit, -) { - androidx.compose.foundation.lazy.LazyColumn( - modifier = modifier, - state = state, - contentPadding = contentPadding, - reverseLayout = reverseLayout, - verticalArrangement = verticalArrangement, - horizontalAlignment = horizontalAlignment, - flingBehavior = flingBehavior, - userScrollEnabled = userScrollEnabled, - content = content, - ) -} /** * LazyColumn with scrollbar. @@ -60,7 +28,6 @@ fun ScrollbarLazyColumn( verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, horizontalAlignment: Alignment.Horizontal = Alignment.Start, - flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(), userScrollEnabled: Boolean = true, content: LazyListScope.() -> Unit, ) { @@ -81,7 +48,6 @@ fun ScrollbarLazyColumn( reverseLayout = reverseLayout, verticalArrangement = verticalArrangement, horizontalAlignment = horizontalAlignment, - flingBehavior = flingBehavior, userScrollEnabled = userScrollEnabled, content = content, ) @@ -99,7 +65,6 @@ fun FastScrollLazyColumn( verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, horizontalAlignment: Alignment.Horizontal = Alignment.Start, - flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(), userScrollEnabled: Boolean = true, content: LazyListScope.() -> Unit, ) { @@ -115,7 +80,6 @@ fun FastScrollLazyColumn( reverseLayout = reverseLayout, verticalArrangement = verticalArrangement, horizontalAlignment = horizontalAlignment, - flingBehavior = flingBehavior, userScrollEnabled = userScrollEnabled, content = content, ) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/WheelPicker.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/WheelPicker.kt index e1d2fd320..6d24aa545 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/WheelPicker.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/WheelPicker.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.LazyListItemInfo import androidx.compose.foundation.lazy.LazyListState diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/util/Scrollable.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/util/Scrollable.kt deleted file mode 100644 index 96d9166b1..000000000 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/util/Scrollable.kt +++ /dev/null @@ -1,60 +0,0 @@ -package tachiyomi.presentation.core.util - -import androidx.compose.animation.core.AnimationState -import androidx.compose.animation.core.DecayAnimationSpec -import androidx.compose.animation.core.animateDecay -import androidx.compose.animation.rememberSplineBasedDecay -import androidx.compose.foundation.gestures.FlingBehavior -import androidx.compose.foundation.gestures.ScrollScope -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.MotionDurationScale -import kotlinx.coroutines.withContext -import kotlin.math.abs - -/** - * FlingBehavior that always uses the default motion scale. - * - * This makes the scrolling animation works like View's lists - * when "Remove animation" settings is on. - */ -@Composable -fun flingBehaviorIgnoringMotionScale(): FlingBehavior { - val flingSpec = rememberSplineBasedDecay() - return remember(flingSpec) { - DefaultFlingBehavior(flingSpec) - } -} - -private val DefaultMotionDurationScale = object : MotionDurationScale { - // Use default motion scale factor - override val scaleFactor: Float = 1f -} - -private class DefaultFlingBehavior( - private val flingDecay: DecayAnimationSpec, -) : FlingBehavior { - override suspend fun ScrollScope.performFling(initialVelocity: Float): Float { - // come up with the better threshold, but we need it since spline curve gives us NaNs - return if (abs(initialVelocity) > 1f) { - var velocityLeft = initialVelocity - var lastValue = 0f - withContext(DefaultMotionDurationScale) { - AnimationState( - initialValue = 0f, - initialVelocity = initialVelocity, - ).animateDecay(flingDecay) { - val delta = value - lastValue - val consumed = scrollBy(delta) - lastValue = value - velocityLeft = this.velocity - // avoid rounding errors and stop if anything is unconsumed - if (abs(delta - consumed) > 0.5f) this.cancelAnimation() - } - } - velocityLeft - } else { - initialVelocity - } - } -}