Save reading percent to database
This commit is contained in:
@@ -25,4 +25,5 @@ class BookmarkEntity(
|
|||||||
@ColumnInfo(name = "scroll") val scroll: Int,
|
@ColumnInfo(name = "scroll") val scroll: Int,
|
||||||
@ColumnInfo(name = "image") val imageUrl: String,
|
@ColumnInfo(name = "image") val imageUrl: String,
|
||||||
@ColumnInfo(name = "created_at") val createdAt: Long,
|
@ColumnInfo(name = "created_at") val createdAt: Long,
|
||||||
|
@ColumnInfo(name = "percent") val percent: Float,
|
||||||
)
|
)
|
||||||
@@ -18,6 +18,7 @@ fun BookmarkEntity.toBookmark(manga: Manga) = Bookmark(
|
|||||||
scroll = scroll,
|
scroll = scroll,
|
||||||
imageUrl = imageUrl,
|
imageUrl = imageUrl,
|
||||||
createdAt = Date(createdAt),
|
createdAt = Date(createdAt),
|
||||||
|
percent = percent,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun Bookmark.toEntity() = BookmarkEntity(
|
fun Bookmark.toEntity() = BookmarkEntity(
|
||||||
@@ -28,4 +29,5 @@ fun Bookmark.toEntity() = BookmarkEntity(
|
|||||||
scroll = scroll,
|
scroll = scroll,
|
||||||
imageUrl = imageUrl,
|
imageUrl = imageUrl,
|
||||||
createdAt = createdAt.time,
|
createdAt = createdAt.time,
|
||||||
|
percent = percent,
|
||||||
)
|
)
|
||||||
@@ -11,6 +11,7 @@ class Bookmark(
|
|||||||
val scroll: Int,
|
val scroll: Int,
|
||||||
val imageUrl: String,
|
val imageUrl: String,
|
||||||
val createdAt: Date,
|
val createdAt: Date,
|
||||||
|
val percent: Float,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
@@ -26,6 +27,7 @@ class Bookmark(
|
|||||||
if (scroll != other.scroll) return false
|
if (scroll != other.scroll) return false
|
||||||
if (imageUrl != other.imageUrl) return false
|
if (imageUrl != other.imageUrl) return false
|
||||||
if (createdAt != other.createdAt) return false
|
if (createdAt != other.createdAt) return false
|
||||||
|
if (percent != other.percent) return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -38,6 +40,7 @@ class Bookmark(
|
|||||||
result = 31 * result + scroll
|
result = 31 * result + scroll
|
||||||
result = 31 * result + imageUrl.hashCode()
|
result = 31 * result + imageUrl.hashCode()
|
||||||
result = 31 * result + createdAt.hashCode()
|
result = 31 * result + createdAt.hashCode()
|
||||||
|
result = 31 * result + percent.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,6 +111,7 @@ class BackupRepository(private val db: MangaDatabase) {
|
|||||||
jo.put("chapter_id", chapterId)
|
jo.put("chapter_id", chapterId)
|
||||||
jo.put("page", page)
|
jo.put("page", page)
|
||||||
jo.put("scroll", scroll)
|
jo.put("scroll", scroll)
|
||||||
|
jo.put("percent", percent)
|
||||||
return jo
|
return jo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,7 @@ import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity
|
|||||||
import org.koitharu.kotatsu.favourites.data.FavouriteEntity
|
import org.koitharu.kotatsu.favourites.data.FavouriteEntity
|
||||||
import org.koitharu.kotatsu.history.data.HistoryEntity
|
import org.koitharu.kotatsu.history.data.HistoryEntity
|
||||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||||
import org.koitharu.kotatsu.parsers.util.json.JSONIterator
|
import org.koitharu.kotatsu.parsers.util.json.*
|
||||||
import org.koitharu.kotatsu.parsers.util.json.getBooleanOrDefault
|
|
||||||
import org.koitharu.kotatsu.parsers.util.json.getStringOrNull
|
|
||||||
import org.koitharu.kotatsu.parsers.util.json.mapJSON
|
|
||||||
|
|
||||||
class RestoreRepository(private val db: MangaDatabase) {
|
class RestoreRepository(private val db: MangaDatabase) {
|
||||||
|
|
||||||
@@ -95,7 +92,8 @@ class RestoreRepository(private val db: MangaDatabase) {
|
|||||||
updatedAt = json.getLong("updated_at"),
|
updatedAt = json.getLong("updated_at"),
|
||||||
chapterId = json.getLong("chapter_id"),
|
chapterId = json.getLong("chapter_id"),
|
||||||
page = json.getInt("page"),
|
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(
|
private fun parseCategory(json: JSONObject) = FavouriteCategoryEntity(
|
||||||
|
|||||||
@@ -6,8 +6,14 @@ import androidx.room.Room
|
|||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import org.koitharu.kotatsu.bookmarks.data.BookmarkEntity
|
import org.koitharu.kotatsu.bookmarks.data.BookmarkEntity
|
||||||
import org.koitharu.kotatsu.bookmarks.data.BookmarksDao
|
import org.koitharu.kotatsu.bookmarks.data.BookmarksDao
|
||||||
import org.koitharu.kotatsu.core.db.dao.*
|
import org.koitharu.kotatsu.core.db.dao.MangaDao
|
||||||
import org.koitharu.kotatsu.core.db.entity.*
|
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.core.db.migrations.*
|
||||||
import org.koitharu.kotatsu.favourites.data.FavouriteCategoriesDao
|
import org.koitharu.kotatsu.favourites.data.FavouriteCategoriesDao
|
||||||
import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity
|
import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity
|
||||||
@@ -27,7 +33,7 @@ import org.koitharu.kotatsu.tracker.data.TracksDao
|
|||||||
FavouriteCategoryEntity::class, FavouriteEntity::class, MangaPrefsEntity::class,
|
FavouriteCategoryEntity::class, FavouriteEntity::class, MangaPrefsEntity::class,
|
||||||
TrackEntity::class, TrackLogEntity::class, SuggestionEntity::class, BookmarkEntity::class,
|
TrackEntity::class, TrackLogEntity::class, SuggestionEntity::class, BookmarkEntity::class,
|
||||||
],
|
],
|
||||||
version = 11,
|
version = 12,
|
||||||
)
|
)
|
||||||
abstract class MangaDatabase : RoomDatabase() {
|
abstract class MangaDatabase : RoomDatabase() {
|
||||||
|
|
||||||
@@ -67,6 +73,7 @@ fun MangaDatabase(context: Context): MangaDatabase = Room.databaseBuilder(
|
|||||||
Migration8To9(),
|
Migration8To9(),
|
||||||
Migration9To10(),
|
Migration9To10(),
|
||||||
Migration10To11(),
|
Migration10To11(),
|
||||||
|
Migration11To12(),
|
||||||
).addCallback(
|
).addCallback(
|
||||||
DatabasePrePopulateCallback(context.resources)
|
DatabasePrePopulateCallback(context.resources)
|
||||||
).build()
|
).build()
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,4 +11,5 @@ data class MangaHistory(
|
|||||||
val chapterId: Long,
|
val chapterId: Long,
|
||||||
val page: Int,
|
val page: Int,
|
||||||
val scroll: Int,
|
val scroll: Int,
|
||||||
|
val percent: Float,
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
package org.koitharu.kotatsu.history.data
|
package org.koitharu.kotatsu.history.data
|
||||||
|
|
||||||
import java.util.*
|
|
||||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
fun HistoryEntity.toMangaHistory() = MangaHistory(
|
fun HistoryEntity.toMangaHistory() = MangaHistory(
|
||||||
createdAt = Date(createdAt),
|
createdAt = Date(createdAt),
|
||||||
updatedAt = Date(updatedAt),
|
updatedAt = Date(updatedAt),
|
||||||
chapterId = chapterId,
|
chapterId = chapterId,
|
||||||
page = page,
|
page = page,
|
||||||
scroll = scroll.toInt()
|
scroll = scroll.toInt(),
|
||||||
|
percent = percent,
|
||||||
)
|
)
|
||||||
@@ -13,16 +13,17 @@ import org.koitharu.kotatsu.core.db.entity.MangaEntity
|
|||||||
entity = MangaEntity::class,
|
entity = MangaEntity::class,
|
||||||
parentColumns = ["manga_id"],
|
parentColumns = ["manga_id"],
|
||||||
childColumns = ["manga_id"],
|
childColumns = ["manga_id"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
class HistoryEntity(
|
class HistoryEntity(
|
||||||
@PrimaryKey(autoGenerate = false)
|
@PrimaryKey(autoGenerate = false)
|
||||||
@ColumnInfo(name = "manga_id") val mangaId: Long,
|
@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 = "updated_at") val updatedAt: Long,
|
||||||
@ColumnInfo(name = "chapter_id") val chapterId: Long,
|
@ColumnInfo(name = "chapter_id") val chapterId: Long,
|
||||||
@ColumnInfo(name = "page") val page: Int,
|
@ColumnInfo(name = "page") val page: Int,
|
||||||
@ColumnInfo(name = "scroll") val scroll: Float,
|
@ColumnInfo(name = "scroll") val scroll: Float,
|
||||||
|
@ColumnInfo(name = "percent") val percent: Float,
|
||||||
)
|
)
|
||||||
@@ -59,7 +59,7 @@ class HistoryRepository(
|
|||||||
.distinctUntilChanged()
|
.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) {
|
if (manga.isNsfw && settings.isHistoryExcludeNsfw) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -75,6 +75,7 @@ class HistoryRepository(
|
|||||||
chapterId = chapterId,
|
chapterId = chapterId,
|
||||||
page = page,
|
page = page,
|
||||||
scroll = scroll.toFloat(), // we migrate to int, but decide to not update database
|
scroll = scroll.toFloat(), // we migrate to int, but decide to not update database
|
||||||
|
percent = percent,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
trackingRepository.syncWithHistory(manga, chapterId)
|
trackingRepository.syncWithHistory(manga, chapterId)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import androidx.activity.result.ActivityResultLauncher
|
|||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import java.util.*
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import org.koitharu.kotatsu.R
|
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.asLiveDataDistinct
|
||||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||||
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
|
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
private const val BOUNDS_PAGE_OFFSET = 2
|
private const val BOUNDS_PAGE_OFFSET = 2
|
||||||
private const val PAGES_TRIM_THRESHOLD = 120
|
private const val PAGES_TRIM_THRESHOLD = 120
|
||||||
@@ -135,13 +135,16 @@ class ReaderViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO check performance
|
||||||
fun saveCurrentState(state: ReaderState? = null) {
|
fun saveCurrentState(state: ReaderState? = null) {
|
||||||
if (state != null) {
|
if (state != null) {
|
||||||
currentState.value = state
|
currentState.value = state
|
||||||
}
|
}
|
||||||
|
val readerState = state ?: currentState.value ?: return
|
||||||
historyRepository.saveStateAsync(
|
historyRepository.saveStateAsync(
|
||||||
mangaData.value ?: return,
|
manga = mangaData.value ?: return,
|
||||||
state ?: currentState.value ?: return
|
state = readerState,
|
||||||
|
percent = computePercent(readerState.chapterId, readerState.page)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +226,7 @@ class ReaderViewModel(
|
|||||||
if (bookmarkJob?.isActive == true) {
|
if (bookmarkJob?.isActive == true) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bookmarkJob = launchJob {
|
bookmarkJob = launchJob(Dispatchers.Default) {
|
||||||
loadingJob?.join()
|
loadingJob?.join()
|
||||||
val state = checkNotNull(currentState.value)
|
val state = checkNotNull(currentState.value)
|
||||||
val page = checkNotNull(getCurrentPage()) { "Page not found" }
|
val page = checkNotNull(getCurrentPage()) { "Page not found" }
|
||||||
@@ -235,9 +238,10 @@ class ReaderViewModel(
|
|||||||
scroll = state.scroll,
|
scroll = state.scroll,
|
||||||
imageUrl = page.preview ?: pageLoader.getPageUrl(page),
|
imageUrl = page.preview ?: pageLoader.getPageUrl(page),
|
||||||
createdAt = Date(),
|
createdAt = Date(),
|
||||||
|
percent = computePercent(state.chapterId, state.page),
|
||||||
)
|
)
|
||||||
bookmarksRepository.addBookmark(bookmark)
|
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)
|
val pages = loadChapter(requireNotNull(currentState.value).chapterId)
|
||||||
// save state
|
// save state
|
||||||
currentState.value?.let {
|
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()
|
shortcutsRepository.updateShortcuts()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,20 +369,32 @@ class ReaderViewModel(
|
|||||||
it.printStackTraceDebug()
|
it.printStackTraceDebug()
|
||||||
}.getOrDefault(defaultMode)
|
}.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
|
* This function is not a member of the ReaderViewModel
|
||||||
* because it should work independently of the ViewModel's lifecycle.
|
* 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) {
|
return processLifecycleScope.launch(Dispatchers.Default) {
|
||||||
runCatching {
|
runCatching {
|
||||||
addOrUpdate(
|
addOrUpdate(
|
||||||
manga = manga,
|
manga = manga,
|
||||||
chapterId = state.chapterId,
|
chapterId = state.chapterId,
|
||||||
page = state.page,
|
page = state.page,
|
||||||
scroll = state.scroll
|
scroll = state.scroll,
|
||||||
|
percent = percent,
|
||||||
)
|
)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
it.printStackTraceDebug()
|
it.printStackTraceDebug()
|
||||||
|
|||||||
Reference in New Issue
Block a user