Refactor reader progress/history logic
(cherry picked from commit 226272f686ccf4dea1cc4e81b0c305749d888231) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
This commit is contained in:
parent
e1fc81cc66
commit
9bacd14f4d
@ -296,13 +296,18 @@ class ReaderActivity : BaseActivity() {
|
|||||||
readingModeToast?.cancel()
|
readingModeToast?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
viewModel.flushReadTimer()
|
||||||
|
super.onPause()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set menu visibility again on activity resume to apply immersive mode again if needed.
|
* Set menu visibility again on activity resume to apply immersive mode again if needed.
|
||||||
* Helps with rotations.
|
* Helps with rotations.
|
||||||
*/
|
*/
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
viewModel.setReadStartTime()
|
viewModel.restartReadTimer()
|
||||||
setMenuVisibility(viewModel.state.value.menuVisible, animate = false)
|
setMenuVisibility(viewModel.state.value.menuVisible, animate = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1005,7 +1010,7 @@ class ReaderActivity : BaseActivity() {
|
|||||||
* Sets the visibility of the menu according to [visible] and with an optional parameter to
|
* Sets the visibility of the menu according to [visible] and with an optional parameter to
|
||||||
* [animate] the views.
|
* [animate] the views.
|
||||||
*/
|
*/
|
||||||
fun setMenuVisibility(visible: Boolean, animate: Boolean = true) {
|
private fun setMenuVisibility(visible: Boolean, animate: Boolean = true) {
|
||||||
viewModel.showMenus(visible)
|
viewModel.showMenus(visible)
|
||||||
if (visible) {
|
if (visible) {
|
||||||
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
|
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
|
||||||
@ -1301,13 +1306,13 @@ class ReaderActivity : BaseActivity() {
|
|||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun onPageSelected(page: ReaderPage, hasExtraPage: Boolean = false) {
|
fun onPageSelected(page: ReaderPage, hasExtraPage: Boolean = false) {
|
||||||
// SY -->
|
// SY -->
|
||||||
val currentPage = if (hasExtraPage) {
|
val currentPageText = if (hasExtraPage) {
|
||||||
val invertDoublePage = (viewModel.state.value.viewer as? PagerViewer)?.config?.invertDoublePages ?: false
|
val invertDoublePage = (viewModel.state.value.viewer as? PagerViewer)?.config?.invertDoublePages ?: false
|
||||||
if (resources.isLTR xor invertDoublePage) "${page.number}-${page.number + 1}" else "${page.number + 1}-${page.number}"
|
if (resources.isLTR xor invertDoublePage) "${page.number}-${page.number + 1}" else "${page.number + 1}-${page.number}"
|
||||||
} else {
|
} else {
|
||||||
"${page.number}"
|
"${page.number}"
|
||||||
}
|
}
|
||||||
viewModel.onPageSelected(page, hasExtraPage, currentPage)
|
viewModel.onPageSelected(page, currentPageText)
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1326,7 +1331,7 @@ class ReaderActivity : BaseActivity() {
|
|||||||
* the viewer is reaching the beginning or end of a chapter or the transition page is active.
|
* the viewer is reaching the beginning or end of a chapter or the transition page is active.
|
||||||
*/
|
*/
|
||||||
fun requestPreloadChapter(chapter: ReaderChapter) {
|
fun requestPreloadChapter(chapter: ReaderChapter) {
|
||||||
lifecycleScope.launchIO { viewModel.preloadChapter(chapter) }
|
lifecycleScope.launchIO { viewModel.preload(chapter) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1421,7 +1426,7 @@ class ReaderActivity : BaseActivity() {
|
|||||||
/**
|
/**
|
||||||
* Updates viewer inset depending on fullscreen reader preferences.
|
* Updates viewer inset depending on fullscreen reader preferences.
|
||||||
*/
|
*/
|
||||||
fun updateViewerInset(fullscreen: Boolean) {
|
private fun updateViewerInset(fullscreen: Boolean) {
|
||||||
viewModel.state.value.viewer?.getView()?.applyInsetter {
|
viewModel.state.value.viewer?.getView()?.applyInsetter {
|
||||||
if (!fullscreen) {
|
if (!fullscreen) {
|
||||||
type(navigationBars = true, statusBars = true) {
|
type(navigationBars = true, statusBars = true) {
|
||||||
|
@ -75,7 +75,6 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.receiveAsFlow
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.util.lang.launchIO
|
import tachiyomi.core.util.lang.launchIO
|
||||||
@ -410,12 +409,15 @@ class ReaderViewModel(
|
|||||||
* Called when the user changed to the given [chapter] when changing pages from the viewer.
|
* 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.
|
* 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
|
val loader = loader ?: return
|
||||||
|
|
||||||
|
viewModelScope.launchIO {
|
||||||
logcat { "Loading ${chapter.chapter.url}" }
|
logcat { "Loading ${chapter.chapter.url}" }
|
||||||
|
|
||||||
withIOContext {
|
flushReadTimer()
|
||||||
|
restartReadTimer()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
loadChapter(loader, chapter)
|
loadChapter(loader, chapter)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
@ -459,7 +461,7 @@ class ReaderViewModel(
|
|||||||
* Called when the viewers decide it's a good time to preload a [chapter] and improve the UX so
|
* 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.
|
* 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) {
|
if (chapter.state is ReaderChapter.State.Loaded || chapter.state == ReaderChapter.State.Loading) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -498,9 +500,7 @@ class ReaderViewModel(
|
|||||||
|
|
||||||
fun onViewerLoaded(viewer: Viewer?) {
|
fun onViewerLoaded(viewer: Viewer?) {
|
||||||
mutableState.update {
|
mutableState.update {
|
||||||
it.copy(
|
it.copy(viewer = viewer)
|
||||||
viewer = viewer,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,53 +509,29 @@ class ReaderViewModel(
|
|||||||
* read, update tracking services, enqueue downloaded chapter deletion, and updating the active chapter if this
|
* read, update tracking services, enqueue downloaded chapter deletion, and updating the active chapter if this
|
||||||
* [page]'s chapter is different from the currently active.
|
* [page]'s chapter is different from the currently active.
|
||||||
*/
|
*/
|
||||||
fun onPageSelected(page: ReaderPage, hasExtraPage: Boolean, currentPage: String) {
|
fun onPageSelected(page: ReaderPage, currentPageText: String) {
|
||||||
// InsertPage and StencilPage doesn't change page progress
|
// InsertPage and StencilPage doesn't change page progress
|
||||||
if (page is InsertPage || page is StencilPage) {
|
if (page is InsertPage || page is StencilPage) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentChapters = state.value.viewerChapters ?: return
|
// SY -->
|
||||||
val pages = page.chapter.pages ?: return
|
mutableState.update { it.copy(currentPageText = currentPageText) }
|
||||||
|
// SY <--
|
||||||
|
|
||||||
val selectedChapter = page.chapter
|
val selectedChapter = page.chapter
|
||||||
|
val pages = selectedChapter.pages ?: return
|
||||||
|
|
||||||
// Save last page read and mark as read if needed
|
// Save last page read and mark as read if needed
|
||||||
saveReadingProgress()
|
|
||||||
mutableState.update {
|
|
||||||
it.copy(
|
|
||||||
currentPage = page.number,
|
|
||||||
// SY -->
|
|
||||||
currentPageText = currentPage,
|
|
||||||
// SY <--
|
|
||||||
)
|
|
||||||
}
|
|
||||||
selectedChapter.chapter.last_page_read = page.index
|
|
||||||
if (
|
|
||||||
(selectedChapter.pages?.lastIndex == page.index && !incognitoMode) ||
|
|
||||||
(hasExtraPage && selectedChapter.pages?.lastIndex?.minus(1) == page.index && !incognitoMode)
|
|
||||||
) {
|
|
||||||
selectedChapter.chapter.read = true
|
|
||||||
// SY -->
|
|
||||||
if (manga?.isEhBasedManga() == true) {
|
|
||||||
viewModelScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
chapterList
|
updateChapterProgress(page.index)
|
||||||
.filter { it.chapter.source_order > selectedChapter.chapter.source_order }
|
|
||||||
.onEach {
|
|
||||||
it.chapter.read = true
|
|
||||||
saveChapterProgress(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
updateTrackChapterRead(selectedChapter)
|
|
||||||
deleteChapterIfNeeded(selectedChapter)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedChapter != currentChapters.currChapter) {
|
if (selectedChapter != getCurrentChapter()) {
|
||||||
logcat { "Setting ${selectedChapter.chapter.url} as active" }
|
logcat { "Setting ${selectedChapter.chapter.url} as active" }
|
||||||
setReadStartTime()
|
loadNewChapter(selectedChapter)
|
||||||
viewModelScope.launch { loadNewChapter(selectedChapter) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val inDownloadRange = page.number.toDouble() / pages.size > 0.25
|
val inDownloadRange = page.number.toDouble() / pages.size > 0.25
|
||||||
if (inDownloadRange) {
|
if (inDownloadRange) {
|
||||||
downloadNextChapters()
|
downloadNextChapters()
|
||||||
@ -627,45 +603,69 @@ 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)
|
||||||
*/
|
|
||||||
private fun saveReadingProgress() {
|
|
||||||
getCurrentChapter()?.let {
|
|
||||||
viewModelScope.launchNonCancellable {
|
|
||||||
saveChapterProgress(it)
|
|
||||||
saveChapterHistory(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves this [readerChapter] progress (last read page and whether it's read)
|
|
||||||
* if incognito mode isn't on.
|
* if incognito mode isn't on.
|
||||||
*/
|
*/
|
||||||
private suspend fun saveChapterProgress(readerChapter: ReaderChapter) {
|
private suspend fun updateChapterProgress(pageIndex: Int) {
|
||||||
// SY -->
|
val readerChapter = getCurrentChapter() ?: return
|
||||||
readerChapter.requestedPage = readerChapter.chapter.last_page_read
|
|
||||||
// SY <--
|
mutableState.update {
|
||||||
if (incognitoMode) return
|
it.copy(currentPage = pageIndex + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!incognitoMode) {
|
||||||
|
readerChapter.requestedPage = pageIndex
|
||||||
|
readerChapter.chapter.last_page_read = pageIndex
|
||||||
|
|
||||||
val chapter = readerChapter.chapter
|
|
||||||
readerChapter.requestedPage = chapter.last_page_read
|
|
||||||
updateChapter.await(
|
updateChapter.await(
|
||||||
ChapterUpdate(
|
ChapterUpdate(
|
||||||
id = chapter.id!!,
|
id = readerChapter.chapter.id!!,
|
||||||
read = chapter.read,
|
read = readerChapter.chapter.read,
|
||||||
bookmark = chapter.bookmark,
|
bookmark = readerChapter.chapter.bookmark,
|
||||||
lastPageRead = chapter.last_page_read.toLong(),
|
lastPageRead = readerChapter.chapter.last_page_read.toLong(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (readerChapter.pages?.lastIndex == pageIndex) {
|
||||||
|
readerChapter.chapter.read = true
|
||||||
|
// SY -->
|
||||||
|
if (manga?.isEhBasedManga() == true) {
|
||||||
|
viewModelScope.launchNonCancellable {
|
||||||
|
val chapterUpdates = chapterList
|
||||||
|
.filter { it.chapter.source_order > readerChapter.chapter.source_order }
|
||||||
|
.map { chapter ->
|
||||||
|
ChapterUpdate(
|
||||||
|
id = chapter.chapter.id!!,
|
||||||
|
read = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
updateChapter.awaitAll(chapterUpdates)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// SY <--
|
||||||
|
updateTrackChapterRead(readerChapter)
|
||||||
|
deleteChapterIfNeeded(readerChapter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun restartReadTimer() {
|
||||||
|
chapterReadStartTime = Date().time
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
if (incognitoMode) return
|
||||||
|
|
||||||
|
val readerChapter = getCurrentChapter() ?: return
|
||||||
val chapterId = readerChapter.chapter.id!!
|
val chapterId = readerChapter.chapter.id!!
|
||||||
val endTime = Date()
|
val endTime = Date()
|
||||||
val sessionReadDuration = chapterReadStartTime?.let { endTime.time - it } ?: 0
|
val sessionReadDuration = chapterReadStartTime?.let { endTime.time - it } ?: 0
|
||||||
@ -674,17 +674,6 @@ class ReaderViewModel(
|
|||||||
chapterReadStartTime = null
|
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.
|
* Called from the activity to load and set the next chapter as active.
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user