diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/domain/MigrateUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/domain/MigrateUseCase.kt index 12d92bcf0..503e9495e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/domain/MigrateUseCase.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/domain/MigrateUseCase.kt @@ -18,178 +18,178 @@ import org.koitharu.kotatsu.tracker.data.TrackEntity import javax.inject.Inject class MigrateUseCase - @Inject - constructor( - private val mangaRepositoryFactory: MangaRepository.Factory, - private val mangaDataRepository: MangaDataRepository, - private val database: MangaDatabase, - private val progressUpdateUseCase: ProgressUpdateUseCase, - private val scrobblers: Set<@JvmSuppressWildcards Scrobbler>, +@Inject +constructor( + private val mangaRepositoryFactory: MangaRepository.Factory, + private val mangaDataRepository: MangaDataRepository, + private val database: MangaDatabase, + private val progressUpdateUseCase: ProgressUpdateUseCase, + private val scrobblers: Set<@JvmSuppressWildcards Scrobbler>, +) { + suspend operator fun invoke( + oldManga: Manga, + newManga: Manga, ) { - suspend operator fun invoke( - oldManga: Manga, - newManga: Manga, - ) { - val oldDetails = - if (oldManga.chapters.isNullOrEmpty()) { - runCatchingCancellable { - mangaRepositoryFactory.create(oldManga.source).getDetails(oldManga) - }.getOrDefault(oldManga) - } else { - oldManga - } - val newDetails = - if (newManga.chapters.isNullOrEmpty()) { - mangaRepositoryFactory.create(newManga.source).getDetails(newManga) - } else { - newManga - } - mangaDataRepository.storeManga(newDetails) - database.withTransaction { - // replace favorites - val favoritesDao = database.getFavouritesDao() - val oldFavourites = favoritesDao.findAllRaw(oldDetails.id) - if (oldFavourites.isNotEmpty()) { - favoritesDao.delete(oldManga.id) - for (f in oldFavourites) { - val e = - f.copy( - mangaId = newManga.id, - ) - favoritesDao.upsert(e) - } - } - // replace history - val historyDao = database.getHistoryDao() - val oldHistory = historyDao.find(oldDetails.id) - val newHistory = - if (oldHistory != null) { - val newHistory = makeNewHistory(oldDetails, newDetails, oldHistory) - historyDao.delete(oldDetails.id) - historyDao.upsert(newHistory) - newHistory - } else { - null - } - // track - val tracksDao = database.getTracksDao() - val oldTrack = tracksDao.find(oldDetails.id) - if (oldTrack != null) { - val lastChapter = newDetails.chapters?.lastOrNull() - val newTrack = - TrackEntity( - mangaId = newDetails.id, - lastChapterId = lastChapter?.id ?: 0L, - newChapters = 0, - lastCheckTime = System.currentTimeMillis(), - lastChapterDate = lastChapter?.uploadDate ?: 0L, - lastResult = TrackEntity.RESULT_EXTERNAL_MODIFICATION, - lastError = null, + val oldDetails = + if (oldManga.chapters.isNullOrEmpty()) { + runCatchingCancellable { + mangaRepositoryFactory.create(oldManga.source).getDetails(oldManga) + }.getOrDefault(oldManga) + } else { + oldManga + } + val newDetails = + if (newManga.chapters.isNullOrEmpty()) { + mangaRepositoryFactory.create(newManga.source).getDetails(newManga) + } else { + newManga + } + mangaDataRepository.storeManga(newDetails) + database.withTransaction { + // replace favorites + val favoritesDao = database.getFavouritesDao() + val oldFavourites = favoritesDao.findAllRaw(oldDetails.id) + if (oldFavourites.isNotEmpty()) { + favoritesDao.delete(oldManga.id) + for (f in oldFavourites) { + val e = + f.copy( + mangaId = newManga.id, ) - tracksDao.delete(oldDetails.id) - tracksDao.upsert(newTrack) + favoritesDao.upsert(e) } - // scrobbling - for (scrobbler in scrobblers) { - if (!scrobbler.isEnabled) { - continue - } - val prevInfo = scrobbler.getScrobblingInfoOrNull(oldDetails.id) ?: continue - scrobbler.unregisterScrobbling(oldDetails.id) - scrobbler.linkManga(newDetails.id, prevInfo.targetId) - scrobbler.updateScrobblingInfo( + } + // replace history + val historyDao = database.getHistoryDao() + val oldHistory = historyDao.find(oldDetails.id) + val newHistory = + if (oldHistory != null) { + val newHistory = makeNewHistory(oldDetails, newDetails, oldHistory) + historyDao.delete(oldDetails.id) + historyDao.upsert(newHistory) + newHistory + } else { + null + } + // track + val tracksDao = database.getTracksDao() + val oldTrack = tracksDao.find(oldDetails.id) + if (oldTrack != null) { + val lastChapter = newDetails.chapters?.lastOrNull() + val newTrack = + TrackEntity( mangaId = newDetails.id, - rating = prevInfo.rating, - status = - prevInfo.status ?: when { - newHistory == null -> ScrobblingStatus.PLANNED - newHistory.percent == 1f -> ScrobblingStatus.COMPLETED - else -> ScrobblingStatus.READING - }, - comment = prevInfo.comment, + lastChapterId = lastChapter?.id ?: 0L, + newChapters = 0, + lastCheckTime = System.currentTimeMillis(), + lastChapterDate = lastChapter?.uploadDate ?: 0L, + lastResult = TrackEntity.RESULT_EXTERNAL_MODIFICATION, + lastError = null, ) - if (newHistory != null) { - scrobbler.scrobble( - manga = newDetails, - chapterId = newHistory.chapterId, - ) - } - } + tracksDao.delete(oldDetails.id) + tracksDao.upsert(newTrack) } - progressUpdateUseCase(newManga) - } - - private fun makeNewHistory( - oldManga: Manga, - newManga: Manga, - history: HistoryEntity, - ): HistoryEntity { - if (oldManga.chapters.isNullOrEmpty()) { // probably broken manga/source - val branch = newManga.getPreferredBranch(null) - val chapters = checkNotNull(newManga.getChapters(branch)) - val currentChapter = - if (history.percent in 0f..1f) { - chapters[(chapters.lastIndex * history.percent).toInt()] - } else { - chapters.first() - } - return HistoryEntity( - mangaId = newManga.id, - createdAt = history.createdAt, - updatedAt = System.currentTimeMillis(), - chapterId = currentChapter.id, - page = history.page, - scroll = history.scroll, - percent = history.percent, - deletedAt = 0, - chaptersCount = chapters.size, + // scrobbling + for (scrobbler in scrobblers) { + if (!scrobbler.isEnabled) { + continue + } + val prevInfo = scrobbler.getScrobblingInfoOrNull(oldDetails.id) ?: continue + scrobbler.unregisterScrobbling(oldDetails.id) + scrobbler.linkManga(newDetails.id, prevInfo.targetId) + scrobbler.updateScrobblingInfo( + mangaId = newDetails.id, + rating = prevInfo.rating, + status = + prevInfo.status ?: when { + newHistory == null -> ScrobblingStatus.PLANNED + newHistory.percent == 1f -> ScrobblingStatus.COMPLETED + else -> ScrobblingStatus.READING + }, + comment = prevInfo.comment, ) - } - val branch = oldManga.getPreferredBranch(history.toMangaHistory()) - val oldChapters = checkNotNull(oldManga.getChapters(branch)) - var index = oldChapters.indexOfFirst { it.id == history.chapterId } - if (index < 0) { - index = - if (history.percent in 0f..1f) { - (oldChapters.lastIndex * history.percent).toInt() - } else { - 0 - } - } - val newChapters = checkNotNull(newManga.chapters).groupBy { it.branch } - val newBranch = - if (newChapters.containsKey(branch)) { - branch - } else { - newManga.getPreferredBranch(null) + if (newHistory != null) { + scrobbler.scrobble( + manga = newDetails, + chapterId = newHistory.chapterId, + ) } - val newChapterId = - checkNotNull(newChapters[newBranch]) - .let { - val oldChapter = oldChapters[index] - it.findByNumber(oldChapter.volume, oldChapter.number) ?: it.getOrNull(index) ?: it.last() - }.id + } + } + progressUpdateUseCase(newManga) + } + private fun makeNewHistory( + oldManga: Manga, + newManga: Manga, + history: HistoryEntity, + ): HistoryEntity { + if (oldManga.chapters.isNullOrEmpty()) { // probably broken manga/source + val branch = newManga.getPreferredBranch(null) + val chapters = checkNotNull(newManga.getChapters(branch)) + val currentChapter = + if (history.percent in 0f..1f) { + chapters[(chapters.lastIndex * history.percent).toInt()] + } else { + chapters.first() + } return HistoryEntity( mangaId = newManga.id, createdAt = history.createdAt, updatedAt = System.currentTimeMillis(), - chapterId = newChapterId, + chapterId = currentChapter.id, page = history.page, scroll = history.scroll, - percent = PROGRESS_NONE, + percent = history.percent, deletedAt = 0, - chaptersCount = checkNotNull(newChapters[newBranch]).size, + chaptersCount = chapters.count { it.branch == currentChapter.branch }, ) } - - private fun List.findByNumber( - volume: Int, - number: Float, - ): MangaChapter? = - if (number <= 0f) { - null + val branch = oldManga.getPreferredBranch(history.toMangaHistory()) + val oldChapters = checkNotNull(oldManga.getChapters(branch)) + var index = oldChapters.indexOfFirst { it.id == history.chapterId } + if (index < 0) { + index = + if (history.percent in 0f..1f) { + (oldChapters.lastIndex * history.percent).toInt() + } else { + 0 + } + } + val newChapters = checkNotNull(newManga.chapters).groupBy { it.branch } + val newBranch = + if (newChapters.containsKey(branch)) { + branch } else { - firstOrNull { it.volume == volume && it.number == number } + newManga.getPreferredBranch(null) } + val newChapterId = + checkNotNull(newChapters[newBranch]) + .let { + val oldChapter = oldChapters[index] + it.findByNumber(oldChapter.volume, oldChapter.number) ?: it.getOrNull(index) ?: it.last() + }.id + + return HistoryEntity( + mangaId = newManga.id, + createdAt = history.createdAt, + updatedAt = System.currentTimeMillis(), + chapterId = newChapterId, + page = history.page, + scroll = history.scroll, + percent = PROGRESS_NONE, + deletedAt = 0, + chaptersCount = checkNotNull(newChapters[newBranch]).size, + ) } + + private fun List.findByNumber( + volume: Int, + number: Float, + ): MangaChapter? = + if (number <= 0f) { + null + } else { + firstOrNull { it.volume == volume && it.number == number } + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt index 3622b2488..d903b4aa1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt @@ -62,7 +62,7 @@ fun alternativeAD( } } } - binding.progressView.setPercent(item.progress, ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads) + binding.progressView.setProgress(item.progress, ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads) binding.chipSource.also { chip -> chip.text = item.manga.source.getTitle(chip.context) ImageRequest.Builder(context) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesViewModel.kt index 40cb46ad6..918227088 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesViewModel.kt @@ -21,7 +21,7 @@ import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.core.util.ext.require import org.koitharu.kotatsu.history.data.HistoryRepository -import org.koitharu.kotatsu.history.data.PROGRESS_NONE +import org.koitharu.kotatsu.list.domain.ReadingProgress import org.koitharu.kotatsu.list.ui.model.EmptyState import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.LoadingFooter @@ -89,11 +89,7 @@ class AlternativesViewModel @Inject constructor( } } - private suspend fun getProgress(mangaId: Long): Float { - return if (settings.isReadingIndicatorsEnabled) { - historyRepository.getProgress(mangaId) - } else { - PROGRESS_NONE - } + private suspend fun getProgress(mangaId: Long): ReadingProgress? { + return historyRepository.getProgress(mangaId, settings.progressIndicatorMode) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/MangaAlternativeModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/MangaAlternativeModel.kt index 18571c17a..da9d9b5a6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/MangaAlternativeModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/MangaAlternativeModel.kt @@ -1,12 +1,13 @@ package org.koitharu.kotatsu.alternatives.ui import org.koitharu.kotatsu.core.model.chaptersCount +import org.koitharu.kotatsu.list.domain.ReadingProgress import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.parsers.model.Manga data class MangaAlternativeModel( val manga: Manga, - val progress: Float, + val progress: ReadingProgress?, private val referenceChapters: Int, ) : ListModel { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkLargeAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkLargeAD.kt index e0aab168f..bdbd35ef0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkLargeAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkLargeAD.kt @@ -37,6 +37,6 @@ fun bookmarkLargeAD( source(item.manga.source) enqueueWith(coil) } - binding.progressView.percent = item.percent + binding.progressView.setProgress(item.percent, false) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt index fdbd623d8..23df27186 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -192,8 +192,8 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { get() = prefs.getBoolean(KEY_FEED_HEADER, true) set(value) = prefs.edit { putBoolean(KEY_FEED_HEADER, value) } - val isReadingIndicatorsEnabled: Boolean - get() = prefs.getBoolean(KEY_READING_INDICATORS, true) + val progressIndicatorMode: ProgressIndicatorMode + get() = prefs.getEnumValue(KEY_PROGRESS_INDICATORS, ProgressIndicatorMode.PERCENT_READ) val isHistoryExcludeNsfw: Boolean get() = prefs.getBoolean(KEY_HISTORY_EXCLUDE_NSFW, false) @@ -619,7 +619,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { const val KEY_BACKUP_PERIODICAL_LAST = "backup_periodic_last" const val KEY_HISTORY_GROUPING = "history_grouping" const val KEY_UPDATED_GROUPING = "updated_grouping" - const val KEY_READING_INDICATORS = "reading_indicators" + const val KEY_PROGRESS_INDICATORS = "progress_indicators" const val KEY_REVERSE_CHAPTERS = "reverse_chapters" const val KEY_GRID_VIEW_CHAPTERS = "grid_view_chapters" const val KEY_HISTORY_EXCLUDE_NSFW = "history_exclude_nsfw" diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/ProgressIndicatorMode.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/ProgressIndicatorMode.kt new file mode 100644 index 000000000..6bf1da864 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/ProgressIndicatorMode.kt @@ -0,0 +1,6 @@ +package org.koitharu.kotatsu.core.prefs + +enum class ProgressIndicatorMode { + + NONE, PERCENT_READ, PERCENT_LEFT, CHAPTERS_READ, CHAPTERS_LEFT; +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt index d820bb4f6..679f36693 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt @@ -45,7 +45,7 @@ class RelatedListViewModel @Inject constructor( override val content = combine( mangaList, - listMode, + observeListModeWithTriggers(), listError, ) { list, mode, error -> when { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt index 0b2224e19..717d6c321 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt @@ -28,7 +28,6 @@ import org.koitharu.kotatsu.explore.domain.ExploreRepository import org.koitharu.kotatsu.explore.ui.model.ExploreButtons import org.koitharu.kotatsu.explore.ui.model.MangaSourceItem import org.koitharu.kotatsu.explore.ui.model.RecommendationsItem -import org.koitharu.kotatsu.history.data.PROGRESS_NONE import org.koitharu.kotatsu.list.ui.model.EmptyHint import org.koitharu.kotatsu.list.ui.model.ListHeader import org.koitharu.kotatsu.list.ui.model.ListModel @@ -197,7 +196,8 @@ class ExploreViewModel @Inject constructor( coverUrl = manga.coverUrl, manga = manga, counter = 0, - progress = PROGRESS_NONE, + progress = null, + isFavorite = false, ) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/data/FavouritesDao.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/data/FavouritesDao.kt index 13f54b9d6..982540f1d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/data/FavouritesDao.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/data/FavouritesDao.kt @@ -115,6 +115,9 @@ abstract class FavouritesDao { @Query("SELECT DISTINCT category_id FROM favourites WHERE manga_id IN (:mangaIds) AND deleted_at = 0 ORDER BY favourites.created_at ASC") abstract suspend fun findCategoriesIds(mangaIds: Collection): List + @Query("SELECT COUNT(category_id) FROM favourites WHERE manga_id = :mangaId AND deleted_at = 0") + abstract suspend fun findCategoriesCount(mangaId: Long): Int + /** INSERT **/ @Insert(onConflict = OnConflictStrategy.REPLACE) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt index 84387e157..eecac0d30 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt @@ -115,6 +115,10 @@ class FavouritesRepository @Inject constructor( return db.getFavouriteCategoriesDao().find(id.toInt()).toFavouriteCategory() } + suspend fun isFavorite(mangaId: Long): Boolean { + return db.getFavouritesDao().findCategoriesCount(mangaId) != 0 + } + suspend fun getCategoriesIds(mangaIds: Collection): Set { return db.getFavouritesDao().findCategoriesIds(mangaIds).toSet() } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt index 17124863e..249b70c47 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt @@ -66,7 +66,7 @@ class FavouritesListViewModel @Inject constructor( override val content = combine( observeFavorites(), - listMode, + observeListModeWithTriggers(), refreshTrigger, ) { list, mode, _ -> when { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryDao.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryDao.kt index 8add1dc3f..93b3b8db2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryDao.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryDao.kt @@ -88,7 +88,7 @@ abstract class HistoryDao { abstract suspend fun insert(entity: HistoryEntity): Long @Query( - "UPDATE history SET page = :page, chapter_id = :chapterId, scroll = :scroll, percent = :percent, updated_at = :updatedAt, deleted_at = 0 WHERE manga_id = :mangaId", + "UPDATE history SET page = :page, chapter_id = :chapterId, scroll = :scroll, percent = :percent, updated_at = :updatedAt, chapters = :chapters, deleted_at = 0 WHERE manga_id = :mangaId", ) abstract suspend fun update( mangaId: Long, @@ -96,6 +96,7 @@ abstract class HistoryDao { chapterId: Long, scroll: Float, percent: Float, + chapters: Int, updatedAt: Long, ): Int @@ -116,6 +117,7 @@ abstract class HistoryDao { chapterId = entity.chapterId, scroll = entity.scroll, percent = entity.percent, + chapters = entity.chaptersCount, updatedAt = entity.updatedAt, ) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryRepository.kt index c5736a1f3..f9a85f9df 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryRepository.kt @@ -19,10 +19,12 @@ import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.model.isNsfw import org.koitharu.kotatsu.core.parser.MangaDataRepository import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode import org.koitharu.kotatsu.core.ui.util.ReversibleHandle import org.koitharu.kotatsu.core.util.ext.mapItems import org.koitharu.kotatsu.history.domain.model.MangaWithHistory import org.koitharu.kotatsu.list.domain.ListSortOrder +import org.koitharu.kotatsu.list.domain.ReadingProgress import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler @@ -102,6 +104,7 @@ class HistoryRepository @Inject constructor( assert(manga.chapters != null) db.withTransaction { mangaRepository.storeManga(manga) + val branch = manga.chapters?.findById(chapterId)?.branch db.getHistoryDao().upsert( HistoryEntity( mangaId = manga.id, @@ -111,7 +114,7 @@ class HistoryRepository @Inject constructor( page = page, scroll = scroll.toFloat(), // we migrate to int, but decide to not update database percent = percent, - chaptersCount = manga.chapters?.size ?: -1, + chaptersCount = manga.chapters?.count { it.branch == branch } ?: 0, deletedAt = 0L, ), ) @@ -124,8 +127,13 @@ class HistoryRepository @Inject constructor( return db.getHistoryDao().find(manga.id)?.recoverIfNeeded(manga)?.toMangaHistory() } - suspend fun getProgress(mangaId: Long): Float { - return db.getHistoryDao().findProgress(mangaId) ?: PROGRESS_NONE + suspend fun getProgress(mangaId: Long, mode: ProgressIndicatorMode): ReadingProgress? { + val entity = db.getHistoryDao().find(mangaId) ?: return null + return ReadingProgress( + percent = entity.percent, + totalChapters = entity.chaptersCount, + mode = mode, + ).takeIf { it.isValid() } } suspend fun clear() { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt index 7b9f9e678..6485c98f3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt @@ -88,7 +88,7 @@ class HistoryListViewModel @Inject constructor( override val content = combine( observeHistory(), isGroupingEnabled, - listMode, + observeListModeWithTriggers(), networkState, settings.observeAsFlow(AppSettings.KEY_INCOGNITO_MODE) { isIncognitoModeEnabled }, ) { list, grouped, mode, online, incognito -> diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/util/ReadingProgressDrawable.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/util/ReadingProgressDrawable.kt index 618b8d788..13e5f0da6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/util/ReadingProgressDrawable.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/util/ReadingProgressDrawable.kt @@ -26,7 +26,6 @@ class ReadingProgressDrawable( private val outlineColor: Int private val backgroundColor: Int private val textColor: Int - private val textPattern = context.getString(R.string.percent_string_pattern) private val textBounds = Rect() private val tempRect = Rect() private val hasBackground: Boolean @@ -36,14 +35,18 @@ class ReadingProgressDrawable( private val desiredWidth: Int private val autoFitTextSize: Boolean - var progress: Float = PROGRESS_NONE + var percent: Float = PROGRESS_NONE + set(value) { + field = value + invalidateSelf() + } + + var text = "" set(value) { field = value - text = textPattern.format((value * 100f).toInt().toString()) paint.getTextBounds(text, 0, text.length, textBounds) invalidateSelf() } - private var text = "" init { val ta = context.obtainStyledAttributes(styleResId, R.styleable.ProgressDrawable) @@ -79,7 +82,7 @@ class ReadingProgressDrawable( } override fun draw(canvas: Canvas) { - if (progress < 0f) { + if (percent < 0f) { return } val cx = bounds.exactCenterX() @@ -103,12 +106,12 @@ class ReadingProgressDrawable( cx + innerRadius, cy + innerRadius, -90f, - 360f * progress, + 360f * percent, false, paint, ) if (hasText) { - if (checkDrawable != null && progress >= 1f - Math.ulp(progress)) { + if (checkDrawable != null && percent >= 1f - Math.ulp(percent)) { tempRect.set(bounds) tempRect.scale(0.6) checkDrawable.bounds = tempRect diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/util/ReadingProgressView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/util/ReadingProgressView.kt index 744201215..1e19c9b93 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/util/ReadingProgressView.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/util/ReadingProgressView.kt @@ -11,8 +11,14 @@ import android.view.animation.AccelerateDecelerateInterpolator import androidx.annotation.AttrRes import androidx.annotation.StyleRes import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.CHAPTERS_LEFT +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.CHAPTERS_READ +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.NONE +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.PERCENT_LEFT +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.PERCENT_READ import org.koitharu.kotatsu.core.util.ext.getAnimationDuration import org.koitharu.kotatsu.history.data.PROGRESS_NONE +import org.koitharu.kotatsu.list.domain.ReadingProgress class ReadingProgressView @JvmOverloads constructor( context: Context, @@ -20,17 +26,30 @@ class ReadingProgressView @JvmOverloads constructor( @AttrRes defStyleAttr: Int = 0, ) : View(context, attrs, defStyleAttr), ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { + private val percentPattern = context.getString(R.string.percent_string_pattern) private var percentAnimator: ValueAnimator? = null private val animationDuration = context.getAnimationDuration(android.R.integer.config_shortAnimTime) @StyleRes private val drawableStyle: Int - var percent: Float - get() = peekProgressDrawable()?.progress ?: PROGRESS_NONE + var progress: ReadingProgress? = null set(value) { + field = value cancelAnimation() - getProgressDrawable().progress = value + getProgressDrawable().also { + it.percent = value?.percent ?: PROGRESS_NONE + it.text = when (value?.mode) { + null, + NONE -> "" + + PERCENT_READ -> percentPattern.format((value.percent * 100f).toInt().toString()) + PERCENT_LEFT -> "-" + percentPattern.format((value.percentLeft * 100f).toInt().toString()) + + CHAPTERS_READ -> value.chapters.toString() + CHAPTERS_LEFT -> "-" + value.chaptersLeft.toString() + } + } } init { @@ -39,7 +58,11 @@ class ReadingProgressView @JvmOverloads constructor( ta.recycle() outlineProvider = OutlineProvider() if (isInEditMode) { - percent = 0.27f + progress = ReadingProgress( + percent = 0.27f, + totalChapters = 20, + mode = PERCENT_READ, + ) } } @@ -53,7 +76,7 @@ class ReadingProgressView @JvmOverloads constructor( override fun onAnimationUpdate(animation: ValueAnimator) { val p = animation.animatedValue as Float - getProgressDrawable().progress = p + getProgressDrawable().percent = p } override fun onAnimationStart(animation: Animator) = Unit @@ -68,16 +91,25 @@ class ReadingProgressView @JvmOverloads constructor( override fun onAnimationRepeat(animation: Animator) = Unit - fun setPercent(value: Float, animate: Boolean) { + fun setProgress(percent: Float, animate: Boolean) { + setProgress( + value = ReadingProgress(percent, 1, PERCENT_READ), + animate = animate, + ) + } + + fun setProgress(value: ReadingProgress?, animate: Boolean) { val currentDrawable = peekProgressDrawable() - if (!animate || currentDrawable == null || value == PROGRESS_NONE) { - percent = value + if (!animate || currentDrawable == null || value == null) { + progress = value return } percentAnimator?.cancel() + val currentPercent = currentDrawable.percent.coerceAtLeast(0f) + progress = value.copy(percent = currentPercent) percentAnimator = ValueAnimator.ofFloat( - currentDrawable.progress.coerceAtLeast(0f), - value, + currentDrawable.percent.coerceAtLeast(0f), + value.percent, ).apply { duration = animationDuration interpolator = AccelerateDecelerateInterpolator() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/domain/MangaListMapper.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/domain/MangaListMapper.kt index cc903ef66..802833859 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/domain/MangaListMapper.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/domain/MangaListMapper.kt @@ -11,7 +11,6 @@ import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.history.data.HistoryRepository -import org.koitharu.kotatsu.history.data.PROGRESS_NONE import org.koitharu.kotatsu.list.ui.model.MangaCompactListModel import org.koitharu.kotatsu.list.ui.model.MangaDetailedListModel import org.koitharu.kotatsu.list.ui.model.MangaGridModel @@ -59,6 +58,7 @@ class MangaListMapper @Inject constructor( manga = manga, counter = getCounter(manga.id), progress = getProgress(manga.id), + isFavorite = isFavorite(manga.id), ) suspend fun toDetailedListModel(manga: Manga) = MangaDetailedListModel( @@ -69,6 +69,7 @@ class MangaListMapper @Inject constructor( manga = manga, counter = getCounter(manga.id), progress = getProgress(manga.id), + isFavorite = isFavorite(manga.id), tags = mapTags(manga.tags), ) @@ -79,6 +80,7 @@ class MangaListMapper @Inject constructor( manga = manga, counter = getCounter(manga.id), progress = getProgress(manga.id), + isFavorite = isFavorite(manga.id), ) fun mapTags(tags: Collection) = tags.map { @@ -97,12 +99,12 @@ class MangaListMapper @Inject constructor( } } - private suspend fun getProgress(mangaId: Long): Float { - return if (settings.isReadingIndicatorsEnabled) { - historyRepository.getProgress(mangaId) - } else { - PROGRESS_NONE - } + private suspend fun getProgress(mangaId: Long): ReadingProgress? { + return historyRepository.getProgress(mangaId, settings.progressIndicatorMode) + } + + private fun isFavorite(mangaId: Long): Boolean { + return false // TODO favouritesRepository.isFavorite(mangaId) } @ColorRes diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/domain/ReadingProgress.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/domain/ReadingProgress.kt new file mode 100644 index 000000000..e0120adcb --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/domain/ReadingProgress.kt @@ -0,0 +1,35 @@ +package org.koitharu.kotatsu.list.domain + +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.CHAPTERS_LEFT +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.CHAPTERS_READ +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.NONE +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.PERCENT_LEFT +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.PERCENT_READ + +data class ReadingProgress( + val percent: Float, + val totalChapters: Int, + val mode: ProgressIndicatorMode, +) { + + val percentLeft: Float + get() = 1f - percent + + val chapters: Int + get() = (totalChapters * percent).toInt() + + val chaptersLeft: Int + get() = (totalChapters * percentLeft).toInt() + + fun isValid() = when (mode) { + NONE -> false + PERCENT_READ, + PERCENT_LEFT -> percent in 0f..1f + + CHAPTERS_READ, + CHAPTERS_LEFT -> totalChapters > 0 && percent in 0f..1f + } + + fun isReversed() = mode == PERCENT_LEFT || mode == CHAPTERS_LEFT +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt index e8af462a8..c0aa69200 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt @@ -2,11 +2,16 @@ package org.koitharu.kotatsu.list.ui import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.plus import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.core.prefs.observeAsStateFlow import org.koitharu.kotatsu.core.ui.BaseViewModel @@ -55,4 +60,13 @@ abstract class MangaListViewModel( } else { this } + + protected fun observeListModeWithTriggers(): Flow = combine( + listMode, + settings.observe().filter { key -> + key == AppSettings.KEY_PROGRESS_INDICATORS || key == AppSettings.KEY_TRACKER_ENABLED + }.onStart { emit("") } + ) { mode, _ -> + mode + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt index 0fc374be2..ade54bbdd 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.list.ui.adapter import android.view.View +import androidx.core.view.isVisible import androidx.lifecycle.LifecycleOwner import coil.ImageLoader import com.google.android.material.badge.BadgeDrawable @@ -14,7 +15,7 @@ import org.koitharu.kotatsu.core.util.ext.newImageRequest import org.koitharu.kotatsu.core.util.ext.setOnContextClickListenerCompat import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemMangaGridBinding -import org.koitharu.kotatsu.list.ui.ListModelDiffCallback +import org.koitharu.kotatsu.list.ui.ListModelDiffCallback.Companion.PAYLOAD_PROGRESS_CHANGED import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.MangaGridModel import org.koitharu.kotatsu.list.ui.size.ItemSizeResolver @@ -41,7 +42,8 @@ fun mangaGridItemAD( bind { payloads -> binding.textViewTitle.text = item.title - binding.progressView.setPercent(item.progress, ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads) + binding.progressView.setProgress(item.progress, PAYLOAD_PROGRESS_CHANGED in payloads) + binding.imageViewFavorite.isVisible = item.isFavorite binding.imageViewCover.newImageRequest(lifecycleOwner, item.coverUrl)?.run { size(CoverSizeResolver(binding.imageViewCover)) defaultPlaceholders(context) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt index 241e69f21..aa8449e47 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt @@ -39,7 +39,10 @@ fun mangaListDetailedItemAD( bind { payloads -> binding.textViewTitle.text = item.title binding.textViewAuthor.textAndVisible = item.manga.author - binding.progressView.setPercent(item.progress, ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads) + binding.progressView.setProgress( + value = item.progress, + animate = ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads, + ) binding.imageViewCover.newImageRequest(lifecycleOwner, item.coverUrl)?.run { size(CoverSizeResolver(binding.imageViewCover)) defaultPlaceholders(context) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaCompactListModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaCompactListModel.kt index 11ba59dd9..fae6ccfb1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaCompactListModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaCompactListModel.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.list.ui.model +import org.koitharu.kotatsu.list.domain.ReadingProgress import org.koitharu.kotatsu.parsers.model.Manga data class MangaCompactListModel( @@ -9,5 +10,6 @@ data class MangaCompactListModel( override val coverUrl: String, override val manga: Manga, override val counter: Int, - override val progress: Float, + override val progress: ReadingProgress?, + override val isFavorite: Boolean, ) : MangaListModel() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaDetailedListModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaDetailedListModel.kt index 48be34fbf..8b6633788 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaDetailedListModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaDetailedListModel.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.list.ui.model import org.koitharu.kotatsu.core.ui.widgets.ChipsView +import org.koitharu.kotatsu.list.domain.ReadingProgress import org.koitharu.kotatsu.parsers.model.Manga data class MangaDetailedListModel( @@ -10,6 +11,7 @@ data class MangaDetailedListModel( override val coverUrl: String, override val manga: Manga, override val counter: Int, - override val progress: Float, + override val progress: ReadingProgress?, + override val isFavorite: Boolean, val tags: List, ) : MangaListModel() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaGridModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaGridModel.kt index 1105451e1..244c3f7b7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaGridModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaGridModel.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.list.ui.model +import org.koitharu.kotatsu.list.domain.ReadingProgress import org.koitharu.kotatsu.parsers.model.Manga data class MangaGridModel( @@ -8,5 +9,6 @@ data class MangaGridModel( override val coverUrl: String, override val manga: Manga, override val counter: Int, - override val progress: Float, + override val progress: ReadingProgress?, + override val isFavorite: Boolean, ) : MangaListModel() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaListModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaListModel.kt index 264d58f08..322bf0b6a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaListModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaListModel.kt @@ -1,6 +1,8 @@ package org.koitharu.kotatsu.list.ui.model -import org.koitharu.kotatsu.list.ui.ListModelDiffCallback +import org.koitharu.kotatsu.list.domain.ReadingProgress +import org.koitharu.kotatsu.list.ui.ListModelDiffCallback.Companion.PAYLOAD_ANYTHING_CHANGED +import org.koitharu.kotatsu.list.ui.ListModelDiffCallback.Companion.PAYLOAD_PROGRESS_CHANGED import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaSource @@ -11,7 +13,8 @@ sealed class MangaListModel : ListModel { abstract val title: String abstract val coverUrl: String abstract val counter: Int - abstract val progress: Float + abstract val isFavorite: Boolean + abstract val progress: ReadingProgress? val source: MangaSource get() = manga.source @@ -20,12 +23,12 @@ sealed class MangaListModel : ListModel { return other is MangaListModel && other.javaClass == javaClass && id == other.id } - override fun getChangePayload(previousState: ListModel): Any? { - return when { - previousState !is MangaListModel -> super.getChangePayload(previousState) - progress != previousState.progress -> ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED - counter != previousState.counter -> ListModelDiffCallback.PAYLOAD_ANYTHING_CHANGED - else -> null - } + override fun getChangePayload(previousState: ListModel): Any? = when { + previousState !is MangaListModel || previousState.manga != manga -> null + + previousState.progress != progress -> PAYLOAD_PROGRESS_CHANGED + previousState.isFavorite != isFavorite || previousState.counter != counter -> PAYLOAD_ANYTHING_CHANGED + + else -> null } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt index a16d989c1..ae49778b5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt @@ -79,7 +79,7 @@ open class RemoteListViewModel @Inject constructor( override val content = combine( mangaList.map { it?.skipNsfwIfNeeded() }, - listMode, + observeListModeWithTriggers(), listError, hasNextPage, ) { list, mode, error, hasNext -> diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchViewModel.kt index 8409551a5..0bd53ef68 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchViewModel.kt @@ -50,7 +50,7 @@ class SearchViewModel @Inject constructor( override val content = combine( mangaList.map { it?.skipNsfwIfNeeded() }, - listMode, + observeListModeWithTriggers(), listError, hasNextPage, ) { list, mode, error, hasNext -> diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/AppearanceSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/AppearanceSettingsFragment.kt index e0c96fb31..f00a95fa4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/AppearanceSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/AppearanceSettingsFragment.kt @@ -14,6 +14,7 @@ import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ListMode +import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode import org.koitharu.kotatsu.core.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.ui.util.ActivityRecreationHandle import org.koitharu.kotatsu.core.util.LocaleComparator @@ -44,6 +45,10 @@ class AppearanceSettingsFragment : entryValues = ListMode.entries.names() setDefaultValueCompat(ListMode.GRID.name) } + findPreference(AppSettings.KEY_PROGRESS_INDICATORS)?.run { + entryValues = ProgressIndicatorMode.entries.names() + setDefaultValueCompat(ProgressIndicatorMode.PERCENT_READ.name) + } findPreference(AppSettings.KEY_APP_LOCALE)?.run { initLocalePicker(this) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt index 927015c9d..9d3d40eec 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt @@ -36,7 +36,7 @@ class SuggestionsViewModel @Inject constructor( override val content = combine( repository.observeAll(), - listMode, + observeListModeWithTriggers(), ) { list, mode -> when { list.isEmpty() -> listOf( diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesViewModel.kt index 78a7a7fc1..e1ab3c5ed 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesViewModel.kt @@ -39,7 +39,7 @@ class UpdatesViewModel @Inject constructor( override val content = combine( repository.observeUpdatedManga(), settings.observeAsFlow(AppSettings.KEY_UPDATED_GROUPING) { isUpdatedGroupingEnabled }, - listMode, + observeListModeWithTriggers(), ) { mangaList, grouping, mode -> when { mangaList.isEmpty() -> listOf( diff --git a/app/src/main/res/layout/item_manga_grid.xml b/app/src/main/res/layout/item_manga_grid.xml index a0e7a2c25..0828eeefa 100644 --- a/app/src/main/res/layout/item_manga_grid.xml +++ b/app/src/main/res/layout/item_manga_grid.xml @@ -5,11 +5,11 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/custom_selectable_item_background" android:layout_margin="2dp" - android:padding="6dp" + android:background="@drawable/custom_selectable_item_background" android:clipChildren="false" android:orientation="vertical" + android:padding="6dp" tools:layout_width="140dp"> + + @string/pages @string/webtoon + + @string/disabled + @string/percent_read + @string/percent_left + @string/chapters_read + @string/chapters_left + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 08495a58b..ba082d4bb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -664,4 +664,8 @@ Sources unpinned Sources pinned Recent sources + Percent read + Percent left + Chapters read + Chapters left diff --git a/app/src/main/res/xml/pref_appearance.xml b/app/src/main/res/xml/pref_appearance.xml index 1ab9e5660..8acb08a2e 100644 --- a/app/src/main/res/xml/pref_appearance.xml +++ b/app/src/main/res/xml/pref_appearance.xml @@ -38,11 +38,11 @@ android:valueTo="150" app:defaultValue="100" /> - +