Save reading percent to database

This commit is contained in:
Koitharu
2022-06-27 16:06:25 +03:00
parent 33bccd10fe
commit b82b46f7d7
12 changed files with 66 additions and 21 deletions

View File

@@ -25,4 +25,5 @@ class BookmarkEntity(
@ColumnInfo(name = "scroll") val scroll: Int,
@ColumnInfo(name = "image") val imageUrl: String,
@ColumnInfo(name = "created_at") val createdAt: Long,
@ColumnInfo(name = "percent") val percent: Float,
)

View File

@@ -18,6 +18,7 @@ fun BookmarkEntity.toBookmark(manga: Manga) = Bookmark(
scroll = scroll,
imageUrl = imageUrl,
createdAt = Date(createdAt),
percent = percent,
)
fun Bookmark.toEntity() = BookmarkEntity(
@@ -28,4 +29,5 @@ fun Bookmark.toEntity() = BookmarkEntity(
scroll = scroll,
imageUrl = imageUrl,
createdAt = createdAt.time,
percent = percent,
)

View File

@@ -11,6 +11,7 @@ class Bookmark(
val scroll: Int,
val imageUrl: String,
val createdAt: Date,
val percent: Float,
) {
override fun equals(other: Any?): Boolean {
@@ -26,6 +27,7 @@ class Bookmark(
if (scroll != other.scroll) return false
if (imageUrl != other.imageUrl) return false
if (createdAt != other.createdAt) return false
if (percent != other.percent) return false
return true
}
@@ -38,6 +40,7 @@ class Bookmark(
result = 31 * result + scroll
result = 31 * result + imageUrl.hashCode()
result = 31 * result + createdAt.hashCode()
result = 31 * result + percent.hashCode()
return result
}
}

View File

@@ -111,6 +111,7 @@ class BackupRepository(private val db: MangaDatabase) {
jo.put("chapter_id", chapterId)
jo.put("page", page)
jo.put("scroll", scroll)
jo.put("percent", percent)
return jo
}

View File

@@ -9,10 +9,7 @@ import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity
import org.koitharu.kotatsu.favourites.data.FavouriteEntity
import org.koitharu.kotatsu.history.data.HistoryEntity
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.util.json.JSONIterator
import org.koitharu.kotatsu.parsers.util.json.getBooleanOrDefault
import org.koitharu.kotatsu.parsers.util.json.getStringOrNull
import org.koitharu.kotatsu.parsers.util.json.mapJSON
import org.koitharu.kotatsu.parsers.util.json.*
class RestoreRepository(private val db: MangaDatabase) {
@@ -95,7 +92,8 @@ class RestoreRepository(private val db: MangaDatabase) {
updatedAt = json.getLong("updated_at"),
chapterId = json.getLong("chapter_id"),
page = json.getInt("page"),
scroll = json.getDouble("scroll").toFloat()
scroll = json.getDouble("scroll").toFloat(),
percent = json.getFloatOrDefault("percent", -1f),
)
private fun parseCategory(json: JSONObject) = FavouriteCategoryEntity(

View File

@@ -6,8 +6,14 @@ import androidx.room.Room
import androidx.room.RoomDatabase
import org.koitharu.kotatsu.bookmarks.data.BookmarkEntity
import org.koitharu.kotatsu.bookmarks.data.BookmarksDao
import org.koitharu.kotatsu.core.db.dao.*
import org.koitharu.kotatsu.core.db.entity.*
import org.koitharu.kotatsu.core.db.dao.MangaDao
import org.koitharu.kotatsu.core.db.dao.PreferencesDao
import org.koitharu.kotatsu.core.db.dao.TagsDao
import org.koitharu.kotatsu.core.db.dao.TrackLogsDao
import org.koitharu.kotatsu.core.db.entity.MangaEntity
import org.koitharu.kotatsu.core.db.entity.MangaPrefsEntity
import org.koitharu.kotatsu.core.db.entity.MangaTagsEntity
import org.koitharu.kotatsu.core.db.entity.TagEntity
import org.koitharu.kotatsu.core.db.migrations.*
import org.koitharu.kotatsu.favourites.data.FavouriteCategoriesDao
import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity
@@ -27,7 +33,7 @@ import org.koitharu.kotatsu.tracker.data.TracksDao
FavouriteCategoryEntity::class, FavouriteEntity::class, MangaPrefsEntity::class,
TrackEntity::class, TrackLogEntity::class, SuggestionEntity::class, BookmarkEntity::class,
],
version = 11,
version = 12,
)
abstract class MangaDatabase : RoomDatabase() {
@@ -67,6 +73,7 @@ fun MangaDatabase(context: Context): MangaDatabase = Room.databaseBuilder(
Migration8To9(),
Migration9To10(),
Migration10To11(),
Migration11To12(),
).addCallback(
DatabasePrePopulateCallback(context.resources)
).build()

View File

@@ -0,0 +1,12 @@
package org.koitharu.kotatsu.core.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration11To12 : Migration(11, 12) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE history ADD COLUMN `percent` REAL NOT NULL DEFAULT -1")
database.execSQL("ALTER TABLE bookmarks ADD COLUMN `percent` REAL NOT NULL DEFAULT -1")
}
}

View File

@@ -11,4 +11,5 @@ data class MangaHistory(
val chapterId: Long,
val page: Int,
val scroll: Int,
val percent: Float,
) : Parcelable

View File

@@ -1,12 +1,13 @@
package org.koitharu.kotatsu.history.data
import java.util.*
import org.koitharu.kotatsu.core.model.MangaHistory
import java.util.*
fun HistoryEntity.toMangaHistory() = MangaHistory(
createdAt = Date(createdAt),
updatedAt = Date(updatedAt),
chapterId = chapterId,
page = page,
scroll = scroll.toInt()
scroll = scroll.toInt(),
percent = percent,
)

View File

@@ -13,16 +13,17 @@ import org.koitharu.kotatsu.core.db.entity.MangaEntity
entity = MangaEntity::class,
parentColumns = ["manga_id"],
childColumns = ["manga_id"],
onDelete = ForeignKey.CASCADE
onDelete = ForeignKey.CASCADE,
)
]
)
class HistoryEntity(
@PrimaryKey(autoGenerate = false)
@ColumnInfo(name = "manga_id") val mangaId: Long,
@ColumnInfo(name = "created_at") val createdAt: Long = System.currentTimeMillis(),
@ColumnInfo(name = "created_at") val createdAt: Long,
@ColumnInfo(name = "updated_at") val updatedAt: Long,
@ColumnInfo(name = "chapter_id") val chapterId: Long,
@ColumnInfo(name = "page") val page: Int,
@ColumnInfo(name = "scroll") val scroll: Float,
@ColumnInfo(name = "percent") val percent: Float,
)

View File

@@ -59,7 +59,7 @@ class HistoryRepository(
.distinctUntilChanged()
}
suspend fun addOrUpdate(manga: Manga, chapterId: Long, page: Int, scroll: Int) {
suspend fun addOrUpdate(manga: Manga, chapterId: Long, page: Int, scroll: Int, percent: Float) {
if (manga.isNsfw && settings.isHistoryExcludeNsfw) {
return
}
@@ -75,6 +75,7 @@ class HistoryRepository(
chapterId = chapterId,
page = page,
scroll = scroll.toFloat(), // we migrate to int, but decide to not update database
percent = percent,
)
)
trackingRepository.syncWithHistory(manga, chapterId)

View File

@@ -6,7 +6,6 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import java.util.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import org.koitharu.kotatsu.R
@@ -32,6 +31,7 @@ import org.koitharu.kotatsu.utils.SingleLiveEvent
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
import java.util.*
private const val BOUNDS_PAGE_OFFSET = 2
private const val PAGES_TRIM_THRESHOLD = 120
@@ -135,13 +135,16 @@ class ReaderViewModel(
}
}
// TODO check performance
fun saveCurrentState(state: ReaderState? = null) {
if (state != null) {
currentState.value = state
}
val readerState = state ?: currentState.value ?: return
historyRepository.saveStateAsync(
mangaData.value ?: return,
state ?: currentState.value ?: return
manga = mangaData.value ?: return,
state = readerState,
percent = computePercent(readerState.chapterId, readerState.page)
)
}
@@ -223,7 +226,7 @@ class ReaderViewModel(
if (bookmarkJob?.isActive == true) {
return
}
bookmarkJob = launchJob {
bookmarkJob = launchJob(Dispatchers.Default) {
loadingJob?.join()
val state = checkNotNull(currentState.value)
val page = checkNotNull(getCurrentPage()) { "Page not found" }
@@ -235,9 +238,10 @@ class ReaderViewModel(
scroll = state.scroll,
imageUrl = page.preview ?: pageLoader.getPageUrl(page),
createdAt = Date(),
percent = computePercent(state.chapterId, state.page),
)
bookmarksRepository.addBookmark(bookmark)
onShowToast.call(R.string.bookmark_added)
onShowToast.postCall(R.string.bookmark_added)
}
}
@@ -279,7 +283,8 @@ class ReaderViewModel(
val pages = loadChapter(requireNotNull(currentState.value).chapterId)
// save state
currentState.value?.let {
historyRepository.addOrUpdate(manga, it.chapterId, it.page, it.scroll)
val percent = computePercent(it.chapterId, it.page)
historyRepository.addOrUpdate(manga, it.chapterId, it.page, it.scroll, percent)
shortcutsRepository.updateShortcuts()
}
@@ -364,20 +369,32 @@ class ReaderViewModel(
it.printStackTraceDebug()
}.getOrDefault(defaultMode)
}
private fun computePercent(chapterId: Long, pageIndex: Int): Float {
val chapters = manga?.chapters ?: return -1f
val chaptersCount = chapters.size
val chapterIndex = chapters.indexOfFirst { x -> x.id == chapterId }
val pages = content.value?.pages ?: return -1f
val pagesCount = pages.count { x -> x.chapterId == chapterId }
val chapterPercent = (chapterIndex + 1) / chaptersCount.toFloat()
val pagePercent = (pageIndex + 1) / pagesCount.toFloat()
return pagePercent * chapterPercent // FIXME
}
}
/**
* This function is not a member of the ReaderViewModel
* because it should work independently of the ViewModel's lifecycle.
*/
private fun HistoryRepository.saveStateAsync(manga: Manga, state: ReaderState): Job {
private fun HistoryRepository.saveStateAsync(manga: Manga, state: ReaderState, percent: Float): Job {
return processLifecycleScope.launch(Dispatchers.Default) {
runCatching {
addOrUpdate(
manga = manga,
chapterId = state.chapterId,
page = state.page,
scroll = state.scroll
scroll = state.scroll,
percent = percent,
)
}.onFailure {
it.printStackTraceDebug()