From 0a4d58b5bd4c543f5027a9b5ea591dd75a2651b1 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 26 Feb 2020 20:57:17 +0200 Subject: [PATCH] Fix reader state saving --- .../ui/common/list/BaseRecyclerAdapter.kt | 6 ++ .../ui/common/list/BoundsScrollListener.kt | 4 +- .../common/list/PaginationScrollListener.kt | 2 +- .../kotatsu/ui/reader/BaseReaderFragment.kt | 13 ++- .../kotatsu/ui/reader/ReaderActivity.kt | 79 ++++++++++++------- .../kotatsu/ui/reader/ReaderPresenter.kt | 48 +++++++---- .../koitharu/kotatsu/ui/reader/ReaderView.kt | 9 ++- .../reader/standard/StandardReaderFragment.kt | 22 +++++- .../ui/reader/wetoon/WebtoonReaderFragment.kt | 10 ++- .../org/koitharu/kotatsu/utils/ext/ViewExt.kt | 16 +++- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 12 files changed, 153 insertions(+), 62 deletions(-) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/common/list/BaseRecyclerAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/common/list/BaseRecyclerAdapter.kt index a33641066..b28d08871 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/common/list/BaseRecyclerAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/common/list/BaseRecyclerAdapter.kt @@ -49,6 +49,12 @@ abstract class BaseRecyclerAdapter(private val onItemClickListener: OnRecy onDataSetChanged() } + fun prependData(newData: List) { + dataSet.addAll(0, newData) + notifyItemRangeInserted(0, newData.size) + onDataSetChanged() + } + fun appendItem(newItem: T) { dataSet.add(newItem) notifyItemInserted(dataSet.lastIndex) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/common/list/BoundsScrollListener.kt b/app/src/main/java/org/koitharu/kotatsu/ui/common/list/BoundsScrollListener.kt index 8fc81acc3..6a3290c9c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/common/list/BoundsScrollListener.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/common/list/BoundsScrollListener.kt @@ -13,7 +13,7 @@ abstract class BoundsScrollListener(private val offsetTop: Int, private val offs val layoutManager = (recyclerView.layoutManager as? LinearLayoutManager) ?: return val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() if (firstVisibleItemPosition <= offsetTop) { - onScrolledToTop(recyclerView) + onScrolledToStart(recyclerView) return } val visibleItemCount = layoutManager.childCount @@ -23,7 +23,7 @@ abstract class BoundsScrollListener(private val offsetTop: Int, private val offs } } - abstract fun onScrolledToTop(recyclerView: RecyclerView) + abstract fun onScrolledToStart(recyclerView: RecyclerView) abstract fun onScrolledToEnd(recyclerView: RecyclerView) } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/common/list/PaginationScrollListener.kt b/app/src/main/java/org/koitharu/kotatsu/ui/common/list/PaginationScrollListener.kt index be9c0f53d..18ca9f6aa 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/common/list/PaginationScrollListener.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/common/list/PaginationScrollListener.kt @@ -6,7 +6,7 @@ class PaginationScrollListener(offset: Int, private val callback: Callback) : Bo private var lastTotalCount = 0 - override fun onScrolledToTop(recyclerView: RecyclerView) = Unit + override fun onScrolledToStart(recyclerView: RecyclerView) = Unit override fun onScrolledToEnd(recyclerView: RecyclerView) { val total = recyclerView.adapter?.itemCount ?: 0 diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/BaseReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/BaseReaderFragment.kt index 37269140c..7d7f623b6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/BaseReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/BaseReaderFragment.kt @@ -2,10 +2,16 @@ package org.koitharu.kotatsu.ui.reader import android.net.Uri import androidx.annotation.LayoutRes +import org.koitharu.kotatsu.core.model.MangaChapter import org.koitharu.kotatsu.core.model.MangaPage +import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.ui.common.BaseFragment -abstract class BaseReaderFragment(@LayoutRes contentLayoutId: Int) : BaseFragment(contentLayoutId), ReaderView { +abstract class BaseReaderFragment(@LayoutRes contentLayoutId: Int) : BaseFragment(contentLayoutId), + ReaderView { + + protected val lastState + get() = (activity as? ReaderActivity)?.state abstract val hasItems: Boolean @@ -31,4 +37,9 @@ abstract class BaseReaderFragment(@LayoutRes contentLayoutId: Int) : BaseFragmen * Handled by activity */ override fun onPageSaved(uri: Uri?) = Unit + + + override fun onInitReader(mode: ReaderMode) = Unit + + override fun onChaptersLoader(chapters: List) = Unit } \ No newline at end of file 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 af9ae2302..130827b1d 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 @@ -15,6 +15,7 @@ import androidx.core.view.updatePadding import androidx.fragment.app.commit import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.activity_reader.* +import moxy.MvpDelegate import moxy.ktx.moxyPresenter import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.Manga @@ -37,7 +38,8 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh private val presenter by moxyPresenter(factory = ReaderPresenter.Companion::getInstance) - private lateinit var state: ReaderState + lateinit var state: ReaderState + private set private lateinit var touchHelper: GridTouchHelper @@ -71,25 +73,19 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh insets } - presenter.loadChapter(state) - } - - override fun onInitReader(pages: List, mode: ReaderMode, state: ReaderState) { - val currentReader = reader - when (mode) { - ReaderMode.WEBTOON -> if (currentReader !is WebtoonReaderFragment) { - supportFragmentManager.commit { - replace(R.id.container, WebtoonReaderFragment()) - } - } - else -> if (currentReader !is StandardReaderFragment) { - supportFragmentManager.commit { - replace(R.id.container, StandardReaderFragment()) - } - } + if (savedInstanceState?.containsKey(MvpDelegate.MOXY_DELEGATE_TAGS_KEY) != true) { + presenter.loadChapter(state.manga, state.chapterId) } } + override fun onInitReader(mode: ReaderMode) { + if (reader == null) { + setReader(mode) + } + } + + override fun onPagesLoaded(chapterId: Long, pages: List) = Unit + override fun onPause() { reader?.let { state = state.copy(page = it.currentPageIndex) @@ -176,15 +172,7 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh override fun onGridTouch(area: Int) { when (area) { GridTouchHelper.AREA_CENTER -> { - if (appbar_top.isVisible) { - appbar_top.hideAnimated(Motion.SlideTop) - appbar_bottom.hideAnimated(Motion.SlideBottom) - hideSystemUI() - } else { - appbar_top.showAnimated(Motion.SlideTop) - appbar_bottom.showAnimated(Motion.SlideBottom) - showSystemUI() - } + setUiIsVisible(!appbar_top.isVisible) } GridTouchHelper.AREA_TOP, GridTouchHelper.AREA_LEFT -> { @@ -222,7 +210,7 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh chapterId = chapter.id, page = 0 ) - presenter.loadChapter(state) + presenter.loadChapter(state.manga, chapter.id) } override fun onPageSelected(page: MangaPage) { @@ -239,7 +227,12 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh state = state.copy(page = it.currentPageIndex) } presenter.saveState(state, mode) - recreate() + setReader(mode) + setUiIsVisible(false) + } + + override fun onChaptersLoader(chapters: List) { + state = state.copy(manga = state.manga.copy(chapters = chapters)) } override fun onPageSaved(uri: Uri?) { @@ -259,6 +252,36 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh }.show() } + private fun setUiIsVisible(isUiVisible: Boolean) { + if (appbar_top.isVisible != isUiVisible) { + if (isUiVisible) { + appbar_top.showAnimated(Motion.SlideTop) + appbar_bottom.showAnimated(Motion.SlideBottom) + showSystemUI() + } else { + appbar_top.hideAnimated(Motion.SlideTop) + appbar_bottom.hideAnimated(Motion.SlideBottom) + hideSystemUI() + } + } + } + + private fun setReader(mode: ReaderMode) { + val currentReader = reader + when (mode) { + ReaderMode.WEBTOON -> if (currentReader !is WebtoonReaderFragment) { + supportFragmentManager.commit { + replace(R.id.container, WebtoonReaderFragment()) + } + } + else -> if (currentReader !is StandardReaderFragment) { + supportFragmentManager.commit { + replace(R.id.container, StandardReaderFragment()) + } + } + } + } + companion object { private const val EXTRA_STATE = "state" 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 71922a66f..e228d06dc 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 @@ -11,6 +11,7 @@ import moxy.presenterScope import okhttp3.OkHttpClient import okhttp3.Request 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 import org.koitharu.kotatsu.domain.MangaPreferencesRepository @@ -26,29 +27,42 @@ import org.koitharu.kotatsu.utils.ext.mimeType @InjectViewState class ReaderPresenter : BasePresenter() { - fun loadChapter(state: ReaderState) { + private var isInitialized = false + + fun loadChapter(manga: Manga, chapterId: Long) { presenterScope.launch { viewState.onLoadingStateChanged(isLoading = true) try { - val (pages, mode) = withContext(Dispatchers.IO) { - val repo = MangaProviderFactory.create(state.manga.source) - val chapter = state.chapter ?: repo.getDetails(state.manga).chapters - ?.first { it.id == state.chapterId } - ?: throw RuntimeException("Chapter ${state.chapterId} not found") - var mode = MangaPreferencesRepository().getReaderMode(state.manga.id) - val pages = repo.getPages(chapter) - if (mode == null) { - mode = MangaUtils.determineReaderMode(pages) - if (mode != null) { - MangaPreferencesRepository().saveData( - mangaId = state.manga.id, - mode = mode - ) + withContext(Dispatchers.IO) { + val repo = MangaProviderFactory.create(manga.source) + val chapter = (manga.chapters ?: repo.getDetails(manga).chapters?.also { + withContext(Dispatchers.Main) { + viewState.onChaptersLoader(it) } + })?.find { it.id == chapterId } + ?: throw RuntimeException("Chapter ${chapterId} not found") + val pages = repo.getPages(chapter) + if (!isInitialized) { + val prefs = MangaPreferencesRepository() + var mode = prefs.getReaderMode(manga.id) + if (mode == null) { + mode = MangaUtils.determineReaderMode(pages) + if (mode != null) { + prefs.saveData( + mangaId = manga.id, + mode = mode + ) + } + } + withContext(Dispatchers.Main) { + viewState.onInitReader(mode ?: ReaderMode.UNKNOWN) + } + isInitialized = true + } + withContext(Dispatchers.Main) { + viewState.onPagesLoaded(chapterId, pages) } - pages to (mode ?: ReaderMode.UNKNOWN) } - viewState.onInitReader(pages, mode, state) } catch (e: Exception) { if (BuildConfig.DEBUG) { e.printStackTrace() diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderView.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderView.kt index e5f854db4..64c7d0703 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderView.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderView.kt @@ -4,13 +4,20 @@ import android.net.Uri import moxy.MvpView import moxy.viewstate.strategy.alias.AddToEndSingle import moxy.viewstate.strategy.alias.OneExecution +import org.koitharu.kotatsu.core.model.MangaChapter import org.koitharu.kotatsu.core.model.MangaPage import org.koitharu.kotatsu.core.prefs.ReaderMode interface ReaderView : MvpView { @AddToEndSingle - fun onInitReader(pages: List, mode: ReaderMode, state: ReaderState) + fun onInitReader(mode: ReaderMode) + + @AddToEndSingle + fun onChaptersLoader(chapters: List) + + @AddToEndSingle + fun onPagesLoaded(chapterId: Long, pages: List) @AddToEndSingle fun onLoadingStateChanged(isLoading: Boolean) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/StandardReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/StandardReaderFragment.kt index f0fd3cf54..02e756375 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/StandardReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/StandardReaderFragment.kt @@ -6,17 +6,16 @@ import kotlinx.android.synthetic.main.fragment_reader_standard.* import moxy.ktx.moxyPresenter import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.MangaPage -import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.ui.reader.BaseReaderFragment import org.koitharu.kotatsu.ui.reader.PageLoader import org.koitharu.kotatsu.ui.reader.ReaderPresenter -import org.koitharu.kotatsu.ui.reader.ReaderState class StandardReaderFragment : BaseReaderFragment(R.layout.fragment_reader_standard) { private val presenter by moxyPresenter(factory = ReaderPresenter.Companion::getInstance) private var adapter: PagesAdapter? = null + private var isBusy: Boolean = true private lateinit var loader: PageLoader override fun onCreate(savedInstanceState: Bundle?) { @@ -31,11 +30,21 @@ class StandardReaderFragment : BaseReaderFragment(R.layout.fragment_reader_stand pager.offscreenPageLimit = 2 } - override fun onInitReader(pages: List, mode: ReaderMode, state: ReaderState) { + override fun onDestroyView() { + adapter = null + super.onDestroyView() + } + + override fun onPagesLoaded(chapterId: Long, pages: List) { adapter?.let { it.replaceData(pages) - pager.setCurrentItem(state.page, false) + lastState?.let { state -> + if (chapterId == state.chapterId) { + pager.setCurrentItem(state.page, false) + } + } } + isBusy = false } override fun onDestroy() { @@ -55,4 +64,9 @@ class StandardReaderFragment : BaseReaderFragment(R.layout.fragment_reader_stand override fun setCurrentPage(index: Int, smooth: Boolean) { pager.setCurrentItem(index, smooth) } + + private companion object { + + const val SCROLL_OFFSET = 2 + } } \ No newline at end of file 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 9b1430a34..951e0ee3c 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 @@ -6,11 +6,9 @@ import kotlinx.android.synthetic.main.fragment_reader_webtoon.* import moxy.ktx.moxyPresenter import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.MangaPage -import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.ui.reader.BaseReaderFragment import org.koitharu.kotatsu.ui.reader.PageLoader import org.koitharu.kotatsu.ui.reader.ReaderPresenter -import org.koitharu.kotatsu.ui.reader.ReaderState import org.koitharu.kotatsu.utils.ext.firstItem class WebtoonReaderFragment : BaseReaderFragment(R.layout.fragment_reader_webtoon) { @@ -31,10 +29,14 @@ class WebtoonReaderFragment : BaseReaderFragment(R.layout.fragment_reader_webtoo recyclerView.adapter = adapter } - override fun onInitReader(pages: List, mode: ReaderMode, state: ReaderState) { + override fun onPagesLoaded(chapterId: Long, pages: List) { adapter?.let { it.replaceData(pages) - recyclerView.firstItem = state.page + lastState?.let { state -> + if (chapterId == state.chapterId) { + recyclerView.firstItem = state.page + } + } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt index 54f6eb6eb..1bbd557b5 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt @@ -18,6 +18,7 @@ import androidx.drawerlayout.widget.DrawerLayout import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipGroup import org.koitharu.kotatsu.ui.common.ChipsFactory @@ -102,7 +103,10 @@ fun View.disableFor(timeInMillis: Long) { } } -fun View.showPopupMenu(@MenuRes menuRes: Int, onPrepare:((Menu) -> Unit)? = null, onItemClick: (MenuItem) -> Boolean) { +fun View.showPopupMenu( + @MenuRes menuRes: Int, onPrepare: ((Menu) -> Unit)? = null, + onItemClick: (MenuItem) -> Boolean +) { val menu = PopupMenu(context, this) menu.inflate(menuRes) menu.setOnMenuItemClickListener(onItemClick) @@ -159,3 +163,13 @@ fun View.measureWidth(): Int { measuredWidth } else vw } + +fun ViewPager2.doOnPageChanged(callback: (Int) -> Unit) { + registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + callback(position) + } + }) +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index f5f0bb186..98c3a4a01 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath "com.android.tools.build:gradle:4.0.0-alpha09" + classpath 'com.android.tools.build:gradle:4.0.0-beta01' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f4c0c6323..414a16396 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Jan 29 18:34:45 EET 2020 +#Wed Feb 26 19:30:06 EET 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-rc-1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-all.zip