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 1710814df1..a7cc20707c 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) } @@ -588,7 +593,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()) @@ -793,7 +798,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) } @@ -811,7 +815,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) } } /** @@ -898,7 +902,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 ac6a5a07db..5cd95e5a63 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 + + updateChapter.await( + ChapterUpdate( + id = readerChapter.chapter.id!!, + read = readerChapter.chapter.read, + bookmark = readerChapter.chapter.bookmark, + lastPageRead = readerChapter.chapter.last_page_read.toLong(), + ), + ) + + if (readerChapter.pages?.lastIndex == pageIndex) { + readerChapter.chapter.read = true + updateTrackChapterRead(readerChapter) + deleteChapterIfNeeded(readerChapter) } } } - /** - * Saves this [readerChapter] progress (last read page and whether it's read) - * if incognito mode isn't on. - */ - private suspend fun saveChapterProgress(readerChapter: ReaderChapter) { - if (incognitoMode) return + fun restartReadTimer() { + chapterReadStartTime = Date().time + } - 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(), - ), - ) + fun flushReadTimer() { + viewModelScope.launchNonCancellable { + updateHistory() + } } /** - * Saves this [readerChapter] last read history if incognito mode isn't on. + * Saves the chapter 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 endTime = Date() val sessionReadDuration = chapterReadStartTime?.let { endTime.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. */