From 85b18d118beae4202c5238f0e93d8d3ab99e0520 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 28 Mar 2020 18:49:01 +0200 Subject: [PATCH] Store page scroll position in history --- .../java/org/koitharu/kotatsu/KotatsuApp.kt | 3 ++- .../koitharu/kotatsu/core/db/HistoryDao.kt | 6 ++--- .../koitharu/kotatsu/core/db/MangaDatabase.kt | 2 +- .../kotatsu/core/db/entity/HistoryEntity.kt | 6 +++-- .../core/db/migrations/Migration2To3.kt | 11 ++++++++ .../kotatsu/core/model/MangaHistory.kt | 3 ++- .../domain/history/HistoryRepository.kt | 8 +++--- .../koitharu/kotatsu/ui/main/MainPresenter.kt | 2 +- .../kotatsu/ui/reader/ReaderActivity.kt | 13 ++++++---- .../kotatsu/ui/reader/ReaderListener.kt | 2 +- .../kotatsu/ui/reader/ReaderPresenter.kt | 5 ++-- .../koitharu/kotatsu/ui/reader/ReaderState.kt | 3 ++- .../kotatsu/ui/reader/base/AbstractReader.kt | 12 +++++++-- .../ui/reader/standard/PagerReaderFragment.kt | 6 ++++- .../kotatsu/ui/reader/wetoon/WebtoonHolder.kt | 26 ++++++++++++++++--- .../ui/reader/wetoon/WebtoonReaderFragment.kt | 19 ++++++++++++-- 16 files changed, 96 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration2To3.kt diff --git a/app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt b/app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt index 02587de67..ef5a70858 100644 --- a/app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt +++ b/app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt @@ -15,6 +15,7 @@ import org.koin.core.context.startKoin import org.koin.dsl.module import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.migrations.Migration1To2 +import org.koitharu.kotatsu.core.db.migrations.Migration2To3 import org.koitharu.kotatsu.core.local.CbzFetcher import org.koitharu.kotatsu.core.local.PagesCache import org.koitharu.kotatsu.core.local.cookies.PersistentCookieJar @@ -110,5 +111,5 @@ class KotatsuApp : Application() { applicationContext, MangaDatabase::class.java, "kotatsu-db" - ).addMigrations(Migration1To2) + ).addMigrations(Migration1To2, Migration2To3) } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/HistoryDao.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/HistoryDao.kt index e218e7481..52931b071 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/HistoryDao.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/HistoryDao.kt @@ -24,13 +24,13 @@ abstract class HistoryDao { @Insert(onConflict = OnConflictStrategy.IGNORE) abstract suspend fun insert(entity: HistoryEntity): Long - @Query("UPDATE history SET page = :page, chapter_id = :chapterId, updated_at = :updatedAt WHERE manga_id = :mangaId") - abstract suspend fun update(mangaId: Long, page: Int, chapterId: Long, updatedAt: Long): Int + @Query("UPDATE history SET page = :page, chapter_id = :chapterId, scroll = :scroll, updated_at = :updatedAt WHERE manga_id = :mangaId") + abstract suspend fun update(mangaId: Long, page: Int, chapterId: Long, scroll: Float, updatedAt: Long): Int @Query("DELETE FROM history WHERE manga_id = :mangaId") abstract suspend fun delete(mangaId: Long) - suspend fun update(entity: HistoryEntity) = update(entity.mangaId, entity.page, entity.chapterId, entity.updatedAt) + suspend fun update(entity: HistoryEntity) = update(entity.mangaId, entity.page, entity.chapterId, entity.scroll, entity.updatedAt) @Transaction open suspend fun upsert(entity: HistoryEntity) { diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/MangaDatabase.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/MangaDatabase.kt index 6bf20b230..215684b8f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/MangaDatabase.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/MangaDatabase.kt @@ -8,7 +8,7 @@ import org.koitharu.kotatsu.core.db.entity.* entities = [ MangaEntity::class, TagEntity::class, HistoryEntity::class, MangaTagsEntity::class, FavouriteCategoryEntity::class, FavouriteEntity::class, MangaPrefsEntity::class - ], version = 2 + ], version = 3 ) abstract class MangaDatabase : RoomDatabase() { diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/entity/HistoryEntity.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/entity/HistoryEntity.kt index cd986b3f3..31e2ffc33 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/entity/HistoryEntity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/entity/HistoryEntity.kt @@ -23,13 +23,15 @@ data class HistoryEntity( @ColumnInfo(name = "created_at") val createdAt: Long = System.currentTimeMillis(), @ColumnInfo(name = "updated_at") val updatedAt: 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 ) { fun toMangaHistory() = MangaHistory( createdAt = Date(createdAt), updatedAt = Date(updatedAt), chapterId = chapterId, - page = page + page = page, + scroll = scroll ) } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration2To3.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration2To3.kt new file mode 100644 index 000000000..61c2a63a7 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration2To3.kt @@ -0,0 +1,11 @@ +package org.koitharu.kotatsu.core.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +object Migration2To3 : Migration(2, 3) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE history ADD COLUMN scroll REAL NOT NULL DEFAULT 0") + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/model/MangaHistory.kt b/app/src/main/java/org/koitharu/kotatsu/core/model/MangaHistory.kt index 82219dbef..001d7209c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/model/MangaHistory.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/model/MangaHistory.kt @@ -9,5 +9,6 @@ data class MangaHistory( val createdAt: Date, val updatedAt: Date, val chapterId: Long, - val page: Int + val page: Int, + val scroll: Float ) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/domain/history/HistoryRepository.kt b/app/src/main/java/org/koitharu/kotatsu/domain/history/HistoryRepository.kt index 806707785..a7f638cf0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/domain/history/HistoryRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/domain/history/HistoryRepository.kt @@ -21,7 +21,7 @@ class HistoryRepository : KoinComponent { return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) } } - suspend fun addOrUpdate(manga: Manga, chapterId: Long, page: Int) { + suspend fun addOrUpdate(manga: Manga, chapterId: Long, page: Int, scroll: Float) { val tags = manga.tags.map(TagEntity.Companion::fromMangaTag) db.tagsDao().upsert(tags) db.mangaDao().upsert(MangaEntity.from(manga), tags) @@ -31,7 +31,8 @@ class HistoryRepository : KoinComponent { createdAt = System.currentTimeMillis(), updatedAt = System.currentTimeMillis(), chapterId = chapterId, - page = page + page = page, + scroll = scroll ) ) notifyHistoryChanged() @@ -43,7 +44,8 @@ class HistoryRepository : KoinComponent { createdAt = Date(it.createdAt), updatedAt = Date(it.updatedAt), chapterId = it.chapterId, - page = it.page + page = it.page, + scroll = it.scroll ) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/MainPresenter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/MainPresenter.kt index 954736685..1fe403099 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/MainPresenter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/MainPresenter.kt @@ -26,7 +26,7 @@ class MainPresenter : BasePresenter() { val history = repo.getOne(manga) ?: throw EmptyHistoryException() ReaderState( MangaProviderFactory.create(manga.source).getDetails(manga), - history.chapterId, history.page + history.chapterId, history.page, history.scroll ) } viewState.onOpenReader(state) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt index 1fbfb6473..8dcfbb64f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt @@ -195,8 +195,8 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh else -> super.onOptionsItemSelected(item) } - override fun saveState(chapterId: Long, page: Int) { - state = state.copy(chapterId = chapterId, page = page) + override fun saveState(chapterId: Long, page: Int, scroll: Float) { + state = state.copy(chapterId = chapterId, page = page, scroll = scroll) ReaderPresenter.saveState(state) } @@ -293,7 +293,8 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh override fun onChapterChanged(chapter: MangaChapter) { state = state.copy( chapterId = chapter.id, - page = 0 + page = 0, + scroll = 0f ) reader?.updateState(chapterId = chapter.id) } @@ -371,7 +372,8 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh manga = manga, chapterId = if (chapterId == -1L) manga.chapters?.firstOrNull()?.id ?: -1 else chapterId, - page = 0 + page = 0, + scroll = 0f ) ) @@ -383,7 +385,8 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh context, ReaderState( manga = manga, chapterId = history.chapterId, - page = history.page + page = history.page, + scroll = history.scroll ) ) } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderListener.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderListener.kt index 5ca6b167c..fe21280f1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderListener.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderListener.kt @@ -7,5 +7,5 @@ interface ReaderListener : BaseMvpView { fun onPageChanged(chapter: MangaChapter, page: Int, total: Int) - fun saveState(chapterId: Long, page: Int) + fun saveState(chapterId: Long, page: Int, scroll: Float) } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderPresenter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderPresenter.kt index 51b65577a..293a5e699 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderPresenter.kt @@ -1,7 +1,6 @@ package org.koitharu.kotatsu.ui.reader import android.content.ContentResolver -import android.util.Log import android.webkit.URLUtil import kotlinx.coroutines.* import moxy.InjectViewState @@ -9,7 +8,6 @@ import moxy.presenterScope import okhttp3.OkHttpClient import okhttp3.Request import org.koin.core.get -import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.MangaPage import org.koitharu.kotatsu.core.prefs.ReaderMode @@ -103,7 +101,8 @@ class ReaderPresenter : BasePresenter() { HistoryRepository().addOrUpdate( manga = state.manga, chapterId = state.chapterId, - page = state.page + page = state.page, + scroll = state.scroll ) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderState.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderState.kt index cd5f07996..4784f3507 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderState.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderState.kt @@ -10,7 +10,8 @@ import org.koitharu.kotatsu.core.model.MangaChapter data class ReaderState( val manga: Manga, val chapterId: Long, - val page: Int + val page: Int, + val scroll: Float ) : Parcelable { @IgnoredOnParcel diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/base/AbstractReader.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/base/AbstractReader.kt index 3e9fe9760..235aa7720 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/base/AbstractReader.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/base/AbstractReader.kt @@ -58,6 +58,9 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout pages.addLast(state.chapterId, it) adapter?.notifyDataSetChanged() setCurrentItem(state.page, false) + if (state.scroll != 0f) { + restorePageScroll(state.page, state.scroll) + } } } @@ -67,7 +70,8 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout ARG_STATE, ReaderState( manga = manga, chapterId = pages.findGroupByIndex(getCurrentItem()) ?: return, - page = pages.getRelativeIndex(getCurrentItem()) + page = pages.getRelativeIndex(getCurrentItem()), + scroll = getCurrentPageScroll() ) ) } @@ -174,7 +178,7 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout val chapterId = pages.findGroupByIndex(getCurrentItem()) ?: return val page = pages.getRelativeIndex(getCurrentItem()) if (page != -1) { - readerListener?.saveState(chapterId, page) + readerListener?.saveState(chapterId, page, getCurrentPageScroll()) } Log.i(TAG, "saveState(chapterId=$chapterId, page=$page)") } @@ -217,6 +221,10 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout protected abstract fun getCurrentItem(): Int + protected abstract fun getCurrentPageScroll(): Float + + protected abstract fun restorePageScroll(position: Int, scroll: Float) + protected abstract fun setCurrentItem(position: Int, isSmooth: Boolean) protected abstract fun onCreateAdapter(dataSet: GroupedList): BaseReaderAdapter diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PagerReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PagerReaderFragment.kt index 1aba85851..934966e6f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PagerReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PagerReaderFragment.kt @@ -5,10 +5,10 @@ import android.view.View import kotlinx.android.synthetic.main.fragment_reader_standard.* import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.MangaPage +import org.koitharu.kotatsu.ui.reader.ReaderState import org.koitharu.kotatsu.ui.reader.base.AbstractReader import org.koitharu.kotatsu.ui.reader.base.BaseReaderAdapter import org.koitharu.kotatsu.ui.reader.base.GroupedList -import org.koitharu.kotatsu.ui.reader.ReaderState import org.koitharu.kotatsu.utils.ext.doOnPageChanged import org.koitharu.kotatsu.utils.ext.withArgs @@ -43,6 +43,10 @@ class PagerReaderFragment : AbstractReader(R.layout.fragment_reader_standard) { pager.setCurrentItem(position, isSmooth) } + override fun getCurrentPageScroll() = 0f + + override fun restorePageScroll(position: Int, scroll: Float) = Unit + companion object { fun newInstance(state: ReaderState) = PagerReaderFragment().withArgs(1) { diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonHolder.kt index 74e8fa729..bb1c79667 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonHolder.kt @@ -20,6 +20,7 @@ class WebtoonHolder(parent: ViewGroup, private val loader: PageLoader) : SubsamplingScaleImageView.OnImageEventListener, CoroutineScope by loader { private var job: Job? = null + private var scrollToRestore = 0f init { ssiv.setOnImageEventListener(this) @@ -34,6 +35,7 @@ class WebtoonHolder(parent: ViewGroup, private val loader: PageLoader) : private fun doLoad(data: MangaPage, force: Boolean) { job?.cancel() + scrollToRestore = 0f job = launch { layout_error.isVisible = false progressBar.isVisible = true @@ -51,6 +53,22 @@ class WebtoonHolder(parent: ViewGroup, private val loader: PageLoader) : } } + fun getScrollY() = ssiv.center?.y ?: 0f + + fun restoreScroll(scroll: Float) { + if (ssiv.isReady) { + ssiv.setScaleAndCenter( + ssiv.scale, + PointF( + ssiv.sWidth / 2f, + scroll + ) + ) + } else { + scrollToRestore = scroll + } + } + override fun onReady() { ssiv.maxScale = 2f * ssiv.width / ssiv.sWidth.toFloat() ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM) @@ -59,10 +77,10 @@ class WebtoonHolder(parent: ViewGroup, private val loader: PageLoader) : ssiv.minScale, PointF( ssiv.sWidth / 2f, - if (itemView.top < 0) { - ssiv.sHeight.toFloat() - } else { - 0f + when { + scrollToRestore != 0f -> scrollToRestore + itemView.top < 0 -> ssiv.sHeight.toFloat() + else -> 0f } ) ) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonReaderFragment.kt index 015e69276..3d66f4853 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonReaderFragment.kt @@ -11,7 +11,6 @@ import org.koitharu.kotatsu.ui.reader.ReaderState import org.koitharu.kotatsu.ui.reader.base.AbstractReader import org.koitharu.kotatsu.ui.reader.base.BaseReaderAdapter import org.koitharu.kotatsu.ui.reader.base.GroupedList -import org.koitharu.kotatsu.ui.reader.standard.PagerReaderFragment import org.koitharu.kotatsu.utils.ext.doOnCurrentItemChanged import org.koitharu.kotatsu.utils.ext.findMiddleVisibleItemPosition import org.koitharu.kotatsu.utils.ext.firstItem @@ -56,7 +55,23 @@ class WebtoonReaderFragment : AbstractReader(R.layout.fragment_reader_webtoon) { } override fun switchPageBy(delta: Int) { - recyclerView.smoothScrollBy(0, (recyclerView.height * 0.9).toInt() * delta, scrollInterpolator) + recyclerView.smoothScrollBy( + 0, + (recyclerView.height * 0.9).toInt() * delta, + scrollInterpolator + ) + } + + override fun getCurrentPageScroll(): Float { + return (recyclerView.findViewHolderForAdapterPosition(getCurrentItem()) as? WebtoonHolder) + ?.getScrollY() ?: 0f + } + + override fun restorePageScroll(position: Int, scroll: Float) { + recyclerView.post { + val holder = recyclerView.findViewHolderForAdapterPosition(position) ?: return@post + (holder as WebtoonHolder).restoreScroll(scroll) + } } companion object {