Fix reader state saving

This commit is contained in:
Koitharu
2020-02-26 20:57:17 +02:00
parent 331ebfabb4
commit 0a4d58b5bd
12 changed files with 153 additions and 62 deletions

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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"

View File

@@ -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()

View File

@@ -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)

View File

@@ -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
}
}

View File

@@ -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
}
}
}
}

View File

@@ -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)
}
})
}