mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
Save read duration (#1360)
* Save read duration * Set readStartTime when switching chapters in a single reader session
This commit is contained in:
parent
26dae149a7
commit
0cf6393ad6
12 changed files with 65 additions and 105 deletions
|
@ -184,7 +184,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
|
|||
if (historyForManga.isNotEmpty()) {
|
||||
val history = historyForManga.mapNotNull { history ->
|
||||
val url = databaseHelper.getChapter(history.chapter_id).executeAsBlocking()?.url
|
||||
url?.let { BackupHistory(url, history.last_read) }
|
||||
url?.let { BackupHistory(url, history.last_read, history.time_read) }
|
||||
}
|
||||
if (history.isNotEmpty()) {
|
||||
mangaObject.history = history
|
||||
|
@ -283,12 +283,13 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
|
|||
internal fun restoreHistoryForManga(history: List<BackupHistory>) {
|
||||
// List containing history to be updated
|
||||
val historyToBeUpdated = ArrayList<History>(history.size)
|
||||
for ((url, lastRead) in history) {
|
||||
for ((url, lastRead, readDuration) in history) {
|
||||
val dbHistory = databaseHelper.getHistoryByChapterUrl(url).executeAsBlocking()
|
||||
// Check if history already in database and update
|
||||
if (dbHistory != null) {
|
||||
dbHistory.apply {
|
||||
last_read = max(lastRead, dbHistory.last_read)
|
||||
time_read = max(readDuration, dbHistory.time_read)
|
||||
}
|
||||
historyToBeUpdated.add(dbHistory)
|
||||
} else {
|
||||
|
@ -296,6 +297,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
|
|||
databaseHelper.getChapter(url).executeAsBlocking()?.let {
|
||||
val historyToAdd = History.create(it).apply {
|
||||
last_read = lastRead
|
||||
time_read = readDuration
|
||||
}
|
||||
historyToBeUpdated.add(historyToAdd)
|
||||
}
|
||||
|
|
|
@ -66,7 +66,8 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBa
|
|||
val manga = backupManga.getMangaImpl()
|
||||
val chapters = backupManga.getChaptersImpl()
|
||||
val categories = backupManga.categories
|
||||
val history = backupManga.brokenHistory.map { BackupHistory(it.url, it.lastRead) } + backupManga.history
|
||||
val history =
|
||||
backupManga.brokenHistory.map { BackupHistory(it.url, it.lastRead, it.readDuration) } + backupManga.history
|
||||
val tracks = backupManga.getTrackingImpl()
|
||||
val customManga = backupManga.getCustomMangaInfo()
|
||||
|
||||
|
|
|
@ -7,10 +7,12 @@ import kotlinx.serialization.protobuf.ProtoNumber
|
|||
data class BrokenBackupHistory(
|
||||
@ProtoNumber(0) var url: String,
|
||||
@ProtoNumber(1) var lastRead: Long,
|
||||
@ProtoNumber(2) var readDuration: Long = 0,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BackupHistory(
|
||||
@ProtoNumber(1) var url: String,
|
||||
@ProtoNumber(2) var lastRead: Long,
|
||||
@ProtoNumber(3) var readDuration: Long = 0,
|
||||
)
|
||||
|
|
|
@ -23,7 +23,7 @@ interface History : Serializable {
|
|||
var last_read: Long
|
||||
|
||||
/**
|
||||
* Total time chapter was read - todo not yet implemented
|
||||
* Total time chapter was read
|
||||
*/
|
||||
var time_read: Long
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ class HistoryImpl : History {
|
|||
override var last_read: Long = 0
|
||||
|
||||
/**
|
||||
* Total time chapter was read - todo not yet implemented
|
||||
* Total time chapter was read
|
||||
*/
|
||||
override var time_read: Long = 0
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import com.pushtorefresh.storio.sqlite.queries.RawQuery
|
|||
import eu.kanade.tachiyomi.data.database.DbProvider
|
||||
import eu.kanade.tachiyomi.data.database.models.History
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.HistoryLastReadPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.HistoryUpsertResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaChapterHistoryGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.tables.HistoryTable
|
||||
|
@ -122,9 +121,9 @@ interface HistoryQueries : DbProvider {
|
|||
* Inserts history object if not yet in database
|
||||
* @param history history object
|
||||
*/
|
||||
fun updateHistoryLastRead(history: History) = db.put()
|
||||
fun upsertHistoryLastRead(history: History) = db.put()
|
||||
.`object`(history)
|
||||
.withPutResolver(HistoryLastReadPutResolver())
|
||||
.withPutResolver(HistoryUpsertResolver())
|
||||
.prepare()
|
||||
|
||||
/**
|
||||
|
@ -137,16 +136,6 @@ interface HistoryQueries : DbProvider {
|
|||
.withPutResolver(HistoryUpsertResolver())
|
||||
.prepare()
|
||||
|
||||
/**
|
||||
* Updates the history last read.
|
||||
* Inserts history object if not yet in database
|
||||
* @param historyList history object list
|
||||
*/
|
||||
fun updateHistoryLastRead(historyList: List<History>) = db.put()
|
||||
.objects(historyList)
|
||||
.withPutResolver(HistoryLastReadPutResolver())
|
||||
.prepare()
|
||||
|
||||
fun deleteHistory() = db.delete()
|
||||
.byQuery(
|
||||
DeleteQuery.builder()
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
package eu.kanade.tachiyomi.data.database.resolvers
|
||||
|
||||
import androidx.annotation.NonNull
|
||||
import androidx.core.content.contentValuesOf
|
||||
import com.pushtorefresh.storio.sqlite.StorIOSQLite
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
|
||||
import com.pushtorefresh.storio.sqlite.queries.Query
|
||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
||||
import eu.kanade.tachiyomi.data.database.inTransactionReturn
|
||||
import eu.kanade.tachiyomi.data.database.mappers.HistoryPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.models.History
|
||||
import eu.kanade.tachiyomi.data.database.tables.HistoryTable
|
||||
|
||||
class HistoryLastReadPutResolver : HistoryPutResolver() {
|
||||
|
||||
/**
|
||||
* Updates last_read time of chapter
|
||||
*/
|
||||
override fun performPut(@NonNull db: StorIOSQLite, @NonNull history: History): PutResult = db.inTransactionReturn {
|
||||
val updateQuery = mapToUpdateQuery(history)
|
||||
|
||||
val cursor = db.lowLevel().query(
|
||||
Query.builder()
|
||||
.table(updateQuery.table())
|
||||
.where(updateQuery.where())
|
||||
.whereArgs(updateQuery.whereArgs())
|
||||
.build(),
|
||||
)
|
||||
|
||||
val putResult = cursor.use { putCursor ->
|
||||
if (putCursor.count == 0) {
|
||||
val insertQuery = mapToInsertQuery(history)
|
||||
val insertedId = db.lowLevel().insert(insertQuery, mapToContentValues(history))
|
||||
PutResult.newInsertResult(insertedId, insertQuery.table())
|
||||
} else {
|
||||
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, mapToUpdateContentValues(history))
|
||||
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
|
||||
}
|
||||
}
|
||||
|
||||
putResult
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates update query
|
||||
* @param obj history object
|
||||
*/
|
||||
override fun mapToUpdateQuery(obj: History) = UpdateQuery.builder()
|
||||
.table(HistoryTable.TABLE)
|
||||
.where("${HistoryTable.COL_CHAPTER_ID} = ?")
|
||||
.whereArgs(obj.chapter_id)
|
||||
.build()
|
||||
|
||||
/**
|
||||
* Create content query
|
||||
* @param history object
|
||||
*/
|
||||
private fun mapToUpdateContentValues(history: History) =
|
||||
contentValuesOf(
|
||||
HistoryTable.COL_LAST_READ to history.last_read,
|
||||
)
|
||||
}
|
|
@ -48,5 +48,6 @@ class HistoryUpsertResolver : HistoryPutResolver() {
|
|||
private fun mapToUpdateContentValues(history: History) =
|
||||
contentValuesOf(
|
||||
HistoryTable.COL_LAST_READ to history.last_read,
|
||||
HistoryTable.COL_TIME_READ to history.time_read,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -164,7 +164,10 @@ class MigrationProcessAdapter(
|
|||
prevHistoryList.find { it.chapter_id == prevChapter.id }
|
||||
?.let { prevHistory ->
|
||||
val history = History.create(chapter)
|
||||
.apply { last_read = prevHistory.last_read }
|
||||
.apply {
|
||||
last_read = prevHistory.last_read
|
||||
time_read = prevHistory.time_read
|
||||
}
|
||||
historyList.add(history)
|
||||
}
|
||||
} else if (chapter.chapter_number <= maxChapterRead) {
|
||||
|
@ -173,7 +176,7 @@ class MigrationProcessAdapter(
|
|||
}
|
||||
}
|
||||
db.insertChapters(dbChapters).executeAsBlocking()
|
||||
db.updateHistoryLastRead(historyList).executeAsBlocking()
|
||||
db.upsertHistoryLastRead(historyList).executeAsBlocking()
|
||||
}
|
||||
// Update categories
|
||||
if (MigrationFlags.hasCategories(flags)) {
|
||||
|
|
|
@ -1114,10 +1114,15 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
|||
}
|
||||
|
||||
override fun onPause() {
|
||||
presenter.saveProgress()
|
||||
presenter.saveCurrentChapterReadingProgress()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
presenter.setReadStartTime()
|
||||
}
|
||||
|
||||
fun reloadChapters(doublePages: Boolean, force: Boolean = false) {
|
||||
val pViewer = viewer as? PagerViewer ?: return
|
||||
pViewer.updateShifting()
|
||||
|
|
|
@ -94,6 +94,11 @@ class ReaderPresenter(
|
|||
*/
|
||||
private var loader: ChapterLoader? = null
|
||||
|
||||
/**
|
||||
* The time the chapter was started reading
|
||||
*/
|
||||
private var chapterReadStartTime: Long? = null
|
||||
|
||||
/**
|
||||
* Subscription to prevent setting chapters as active from multiple threads.
|
||||
*/
|
||||
|
@ -184,8 +189,7 @@ class ReaderPresenter(
|
|||
val isChapterDownloaded = currentChapters.currChapter.pageLoader is DownloadPageLoader
|
||||
currentChapters.unref()
|
||||
val currentChapter = currentChapters.currChapter
|
||||
saveChapterProgress(currentChapter)
|
||||
saveChapterHistory(currentChapter)
|
||||
saveReadingProgress(currentChapter)
|
||||
val currentChapterPageCount = currentChapter.chapter.last_page_read + currentChapter.chapter.pages_left
|
||||
if ((currentChapter.chapter.last_page_read + 1.0) / currentChapterPageCount > 0.33 || isAnyPrevChapterDownloaded) {
|
||||
downloadNextChapters(isChapterDownloaded)
|
||||
|
@ -425,7 +429,7 @@ class ReaderPresenter(
|
|||
fun loadChapter(chapter: Chapter) {
|
||||
val loader = loader ?: return
|
||||
|
||||
viewerChaptersRelay.value?.currChapter?.let(::onChapterChanged)
|
||||
viewerChaptersRelay.value?.currChapter?.let(::saveReadingProgress)
|
||||
|
||||
Timber.d("Loading ${chapter.url}")
|
||||
|
||||
|
@ -532,7 +536,8 @@ class ReaderPresenter(
|
|||
|
||||
if (selectedChapter != currentChapters.currChapter) {
|
||||
Timber.d("Setting ${selectedChapter.chapter.url} as active")
|
||||
onChapterChanged(currentChapters.currChapter)
|
||||
saveReadingProgress(currentChapters.currChapter)
|
||||
setReadStartTime()
|
||||
loadNewChapter(selectedChapter)
|
||||
}
|
||||
}
|
||||
|
@ -592,39 +597,49 @@ class ReaderPresenter(
|
|||
}
|
||||
|
||||
/**
|
||||
* Called when a chapter changed from [fromChapter] to [toChapter]. It updates [fromChapter]
|
||||
* on the database.
|
||||
* Called when reader chapter is changed in reader or when activity is paused.
|
||||
*/
|
||||
private fun onChapterChanged(fromChapter: ReaderChapter) {
|
||||
saveChapterProgress(fromChapter)
|
||||
saveChapterHistory(fromChapter)
|
||||
private fun saveReadingProgress(readerChapter: ReaderChapter) {
|
||||
saveChapterProgress(readerChapter)
|
||||
saveChapterHistory(readerChapter)
|
||||
}
|
||||
|
||||
fun saveProgress() = getCurrentChapter()?.let { onChapterChanged(it) }
|
||||
fun saveCurrentChapterReadingProgress() = getCurrentChapter()?.let { saveReadingProgress(it) }
|
||||
|
||||
/**
|
||||
* Saves this [chapter] progress (last read page and whether it's read).
|
||||
* Saves this [readerChapter] progress (last read page and whether it's read).
|
||||
* If incognito mode isn't on or has at least 1 tracker
|
||||
*/
|
||||
private fun saveChapterProgress(chapter: ReaderChapter) {
|
||||
db.getChapter(chapter.chapter.id!!).executeAsBlocking()?.let { dbChapter ->
|
||||
chapter.chapter.bookmark = dbChapter.bookmark
|
||||
private fun saveChapterProgress(readerChapter: ReaderChapter) {
|
||||
db.getChapter(readerChapter.chapter.id!!).executeAsBlocking()?.let { dbChapter ->
|
||||
readerChapter.chapter.bookmark = dbChapter.bookmark
|
||||
}
|
||||
if (!preferences.incognitoMode().get() || hasTrackers) {
|
||||
db.updateChapterProgress(chapter.chapter).executeAsBlocking()
|
||||
db.updateChapterProgress(readerChapter.chapter).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this [chapter] last read history.
|
||||
* Saves this [readerChapter] last read history.
|
||||
*/
|
||||
private fun saveChapterHistory(chapter: ReaderChapter) {
|
||||
private fun saveChapterHistory(readerChapter: ReaderChapter) {
|
||||
if (!preferences.incognitoMode().get()) {
|
||||
val history = History.create(chapter.chapter).apply { last_read = Date().time }
|
||||
db.updateHistoryLastRead(history).executeAsBlocking()
|
||||
val readAt = Date().time
|
||||
val sessionReadDuration = chapterReadStartTime?.let { readAt - it } ?: 0
|
||||
val oldTimeRead = db.getHistoryByChapterUrl(readerChapter.chapter.url).executeAsBlocking()?.time_read ?: 0
|
||||
val history = History.create(readerChapter.chapter).apply {
|
||||
last_read = readAt
|
||||
time_read = sessionReadDuration + oldTimeRead
|
||||
}
|
||||
db.upsertHistoryLastRead(history).executeAsBlocking()
|
||||
chapterReadStartTime = null
|
||||
}
|
||||
}
|
||||
|
||||
fun setReadStartTime() {
|
||||
chapterReadStartTime = Date().time
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the activity to preload the given [chapter].
|
||||
*/
|
||||
|
|
|
@ -488,7 +488,8 @@ class RecentsPresenter(
|
|||
*/
|
||||
fun removeFromHistory(history: History) {
|
||||
history.last_read = 0L
|
||||
db.updateHistoryLastRead(history).executeAsBlocking()
|
||||
history.time_read = 0L
|
||||
db.upsertHistoryLastRead(history).executeAsBlocking()
|
||||
getRecents()
|
||||
}
|
||||
|
||||
|
@ -498,8 +499,11 @@ class RecentsPresenter(
|
|||
*/
|
||||
fun removeAllFromHistory(mangaId: Long) {
|
||||
val history = db.getHistoryByMangaId(mangaId).executeAsBlocking()
|
||||
history.forEach { it.last_read = 0L }
|
||||
db.updateHistoryLastRead(history).executeAsBlocking()
|
||||
history.forEach {
|
||||
it.last_read = 0L
|
||||
it.time_read = 0L
|
||||
}
|
||||
db.upsertHistoryLastRead(history).executeAsBlocking()
|
||||
getRecents()
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue