Fix reader state saving
This commit is contained in:
@@ -49,6 +49,12 @@ abstract class BaseRecyclerAdapter<T, E>(private val onItemClickListener: OnRecy
|
||||
onDataSetChanged()
|
||||
}
|
||||
|
||||
fun prependData(newData: List<T>) {
|
||||
dataSet.addAll(0, newData)
|
||||
notifyItemRangeInserted(0, newData.size)
|
||||
onDataSetChanged()
|
||||
}
|
||||
|
||||
fun appendItem(newItem: T) {
|
||||
dataSet.add(newItem)
|
||||
notifyItemInserted(dataSet.lastIndex)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<MangaChapter>) = Unit
|
||||
}
|
||||
@@ -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<MangaPage>, 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<MangaPage>) = 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<MangaChapter>) {
|
||||
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"
|
||||
|
||||
@@ -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<ReaderView>() {
|
||||
|
||||
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()
|
||||
|
||||
@@ -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<MangaPage>, mode: ReaderMode, state: ReaderState)
|
||||
fun onInitReader(mode: ReaderMode)
|
||||
|
||||
@AddToEndSingle
|
||||
fun onChaptersLoader(chapters: List<MangaChapter>)
|
||||
|
||||
@AddToEndSingle
|
||||
fun onPagesLoaded(chapterId: Long, pages: List<MangaPage>)
|
||||
|
||||
@AddToEndSingle
|
||||
fun onLoadingStateChanged(isLoading: Boolean)
|
||||
|
||||
@@ -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<MangaPage>, mode: ReaderMode, state: ReaderState) {
|
||||
override fun onDestroyView() {
|
||||
adapter = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onPagesLoaded(chapterId: Long, pages: List<MangaPage>) {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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<MangaPage>, mode: ReaderMode, state: ReaderState) {
|
||||
override fun onPagesLoaded(chapterId: Long, pages: List<MangaPage>) {
|
||||
adapter?.let {
|
||||
it.replaceData(pages)
|
||||
recyclerView.firstItem = state.page
|
||||
lastState?.let { state ->
|
||||
if (chapterId == state.chapterId) {
|
||||
recyclerView.firstItem = state.page
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user