From 259c8459121e4262a2350d539db8d31d2724e518 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 26 Apr 2023 09:45:01 +0300 Subject: [PATCH] Fix chapters counter --- .../org/koitharu/kotatsu/core/model/Manga.kt | 15 +++++- .../kotatsu/details/ui/DetailsFragment.kt | 8 ++- .../kotatsu/details/ui/DetailsViewModel.kt | 7 +-- .../kotatsu/details/ui/model/HistoryInfo.kt | 9 +++- .../kotatsu/reader/domain/PageLoader.kt | 2 + .../kotatsu/reader/ui/ReaderInfoBarView.kt | 11 ++-- .../kotatsu/reader/ui/ReaderViewModel.kt | 53 +++++++++++-------- .../kotatsu/reader/ui/pager/ReaderUiState.kt | 8 +-- 8 files changed, 68 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/model/Manga.kt b/app/src/main/java/org/koitharu/kotatsu/core/model/Manga.kt index 3097f0ac3..d1c3f849e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/model/Manga.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/model/Manga.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.core.model import androidx.core.os.LocaleListCompat +import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.parsers.util.toTitleCase @@ -8,6 +9,18 @@ import org.koitharu.kotatsu.utils.ext.iterator fun Collection.ids() = mapToSet { it.id } +fun Collection.countChaptersByBranch(): Int { + if (size <= 1) { + return size + } + val acc = HashMap() + for (item in this) { + val branch = item.chapter.branch + acc[branch] = (acc[branch] ?: 0) + 1 + } + return acc.values.max() +} + fun Manga.getPreferredBranch(history: MangaHistory?): String? { val ch = chapters if (ch.isNullOrEmpty()) { @@ -31,4 +44,4 @@ fun Manga.getPreferredBranch(history: MangaHistory?): String? { } } return groups.maxByOrNull { it.value.size }?.key -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt index 724bc8250..e0eccd683 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt @@ -27,6 +27,7 @@ import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration import org.koitharu.kotatsu.base.ui.widgets.ChipsView import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter +import org.koitharu.kotatsu.core.model.countChaptersByBranch import org.koitharu.kotatsu.core.parser.MangaTagHighlighter import org.koitharu.kotatsu.databinding.FragmentDetailsBinding import org.koitharu.kotatsu.details.ui.model.ChapterListItem @@ -177,12 +178,9 @@ class DetailsFragment : if (chapters.isNullOrEmpty()) { infoLayout.textViewChapters.isVisible = false } else { + val count = chapters.countChaptersByBranch() infoLayout.textViewChapters.isVisible = true - infoLayout.textViewChapters.text = resources.getQuantityString( - R.plurals.chapters, - chapters.size, - chapters.size, - ) + infoLayout.textViewChapters.text = resources.getQuantityString(R.plurals.chapters, count, count) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt index a804a3166..2a338a2fc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt @@ -95,13 +95,14 @@ class DetailsViewModel @Inject constructor( val historyInfo: LiveData = combine( delegate.manga, + delegate.selectedBranch, history, historyRepository.observeShouldSkip(delegate.manga), - ) { m, h, im -> - HistoryInfo(m, h, im) + ) { m, b, h, im -> + HistoryInfo(m, b, h, im) }.asFlowLiveData( context = viewModelScope.coroutineContext + Dispatchers.Default, - defaultValue = HistoryInfo(null, null, false), + defaultValue = HistoryInfo(null, null, null, false), ) val bookmarks = delegate.manga.flatMapLatest { diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/model/HistoryInfo.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/model/HistoryInfo.kt index ae314be62..2ba871400 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/model/HistoryInfo.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/model/HistoryInfo.kt @@ -36,8 +36,13 @@ class HistoryInfo( } } -fun HistoryInfo(manga: Manga?, history: MangaHistory?, isIncognitoMode: Boolean): HistoryInfo { - val chapters = manga?.chapters +fun HistoryInfo( + manga: Manga?, + branch: String?, + history: MangaHistory?, + isIncognitoMode: Boolean +): HistoryInfo { + val chapters = manga?.getChapters(branch) return HistoryInfo( totalChapters = chapters?.size ?: -1, currentChapter = if (history != null && !chapters.isNullOrEmpty()) { diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt b/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt index aac110c48..c1403fd0d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/domain/PageLoader.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.reader.domain import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri +import androidx.annotation.AnyThread import androidx.collection.LongSparseArray import androidx.collection.set import dagger.hilt.android.ActivityRetainedLifecycle @@ -76,6 +77,7 @@ class PageLoader @Inject constructor( return repository is RemoteMangaRepository && settings.isPagesPreloadEnabled() } + @AnyThread fun prefetch(pages: List) = loaderScope.launch { prefetchLock.withLock { for (page in pages.asReversed()) { diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt index 3407866e6..02e7f91ad 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt @@ -15,9 +15,6 @@ import androidx.annotation.AttrRes import androidx.core.graphics.ColorUtils import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat -import com.google.android.material.R as materialR -import java.text.SimpleDateFormat -import java.util.* import org.koitharu.kotatsu.R import org.koitharu.kotatsu.parsers.util.format import org.koitharu.kotatsu.reader.ui.pager.ReaderUiState @@ -25,6 +22,9 @@ import org.koitharu.kotatsu.utils.ext.getThemeColor import org.koitharu.kotatsu.utils.ext.measureDimension import org.koitharu.kotatsu.utils.ext.printStackTraceDebug import org.koitharu.kotatsu.utils.ext.resolveDp +import java.text.SimpleDateFormat +import java.util.Date +import com.google.android.material.R as materialR class ReaderInfoBarView @JvmOverloads constructor( context: Context, @@ -121,15 +121,14 @@ class ReaderInfoBarView @JvmOverloads constructor( fun update(state: ReaderUiState?) { text = if (state != null) { - val percent = state.computePercent() context.getString( R.string.reader_info_pattern, state.chapterNumber, state.chaptersTotal, state.currentPage + 1, state.totalPages, - ) + if (percent in 0f..1f) { - " " + context.getString(R.string.percent_string_pattern, (percent * 100).format()) + ) + if (state.percent in 0f..1f) { + " " + context.getString(R.string.percent_string_pattern, (state.percent * 100).format()) } else { "" } diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt index 4f17d170b..0ec298e2e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt @@ -4,6 +4,8 @@ import android.net.Uri import android.util.LongSparseArray import androidx.activity.result.ActivityResultLauncher import androidx.annotation.AnyThread +import androidx.annotation.MainThread +import androidx.annotation.WorkerThread import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle @@ -13,6 +15,7 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancelAndJoin +import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine @@ -80,6 +83,7 @@ class ReaderViewModel @Inject constructor( private var loadingJob: Job? = null private var pageSaveJob: Job? = null private var bookmarkJob: Job? = null + private var stateChangeJob: Job? = null private val currentState = MutableStateFlow(savedStateHandle[ReaderActivity.EXTRA_STATE]) private val mangaData = MutableStateFlow(intent.manga) private val chapters: LongSparseArray @@ -143,7 +147,7 @@ class ReaderViewModel @Inject constructor( settings.observe() .onEach { key -> if (key == AppSettings.KEY_READER_SLIDER) notifyStateChanged() - }.launchIn(viewModelScope) + }.launchIn(viewModelScope + Dispatchers.Default) } fun reload() { @@ -234,26 +238,31 @@ class ReaderViewModel @Inject constructor( } } - // TODO move to background? + @MainThread fun onCurrentPageChanged(position: Int) { - val pages = content.value?.pages ?: return - pages.getOrNull(position)?.let { page -> - currentState.update { cs -> - cs?.copy(chapterId = page.chapterId, page = page.index) + val prevJob = stateChangeJob + stateChangeJob = launchJob(Dispatchers.Default) { + prevJob?.cancelAndJoin() + val pages = content.value?.pages ?: return@launchJob + pages.getOrNull(position)?.let { page -> + currentState.update { cs -> + cs?.copy(chapterId = page.chapterId, page = page.index) + } + } + notifyStateChanged() + if (pages.isEmpty() || loadingJob?.isActive == true) { + return@launchJob + } + ensureActive() + if (position <= BOUNDS_PAGE_OFFSET) { + loadPrevNextChapter(pages.first().chapterId, isNext = false) + } + if (position >= pages.lastIndex - BOUNDS_PAGE_OFFSET) { + loadPrevNextChapter(pages.last().chapterId, isNext = true) + } + if (pageLoader.isPrefetchApplicable()) { + pageLoader.prefetch(pages.trySublist(position + 1, position + PREFETCH_LIMIT)) } - } - notifyStateChanged() - if (pages.isEmpty() || loadingJob?.isActive == true) { - return - } - if (position <= BOUNDS_PAGE_OFFSET) { - loadPrevNextChapter(pages.first().chapterId, isNext = false) - } - if (position >= pages.size - BOUNDS_PAGE_OFFSET) { - loadPrevNextChapter(pages.last().chapterId, isNext = true) - } - if (pageLoader.isPrefetchApplicable()) { - pageLoader.prefetch(pages.trySublist(position + 1, position + PREFETCH_LIMIT)) } } @@ -328,6 +337,7 @@ class ReaderViewModel @Inject constructor( } } + @AnyThread private fun loadPrevNextChapter(currentId: Long, isNext: Boolean) { loadingJob = launchLoadingJob(Dispatchers.Default) { chaptersLoader.loadPrevNextChapter(mangaData.requireValue(), currentId, isNext) @@ -365,7 +375,7 @@ class ReaderViewModel @Inject constructor( }.getOrDefault(defaultMode) } - @AnyThread + @WorkerThread private fun notifyStateChanged() { val state = getCurrentState() val chapter = state?.chapterId?.let(chapters::get) @@ -373,10 +383,11 @@ class ReaderViewModel @Inject constructor( mangaName = manga?.title, chapterName = chapter?.name, chapterNumber = chapter?.number ?: 0, - chaptersTotal = chapters.size(), + chaptersTotal = manga?.getChapters(chapter?.branch)?.size ?: 0, totalPages = if (chapter != null) chaptersLoader.getPagesCount(chapter.id) else 0, currentPage = state?.page ?: 0, isSliderEnabled = settings.isReaderSliderEnabled, + percent = if (state != null) computePercent(state.chapterId, state.page) else PROGRESS_NONE, ) uiState.postValue(newState) } diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/ReaderUiState.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/ReaderUiState.kt index 1ffe455ea..e97192ecf 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/ReaderUiState.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/ReaderUiState.kt @@ -7,17 +7,11 @@ data class ReaderUiState( val chaptersTotal: Int, val currentPage: Int, val totalPages: Int, + val percent: Float, private val isSliderEnabled: Boolean, ) { fun isSliderAvailable(): Boolean { return isSliderEnabled && totalPages > 1 && currentPage < totalPages } - - fun computePercent(): Float { - val ppc = 1f / chaptersTotal - val chapterIndex = chapterNumber - 1 - val pagePercent = (currentPage + 1) / totalPages.toFloat() - return ppc * chapterIndex + ppc * pagePercent - } }