Fix reader state saving

pull/1/head
Koitharu 6 years ago
parent 331ebfabb4
commit 0a4d58b5bd

@ -49,6 +49,12 @@ abstract class BaseRecyclerAdapter<T, E>(private val onItemClickListener: OnRecy
onDataSetChanged() onDataSetChanged()
} }
fun prependData(newData: List<T>) {
dataSet.addAll(0, newData)
notifyItemRangeInserted(0, newData.size)
onDataSetChanged()
}
fun appendItem(newItem: T) { fun appendItem(newItem: T) {
dataSet.add(newItem) dataSet.add(newItem)
notifyItemInserted(dataSet.lastIndex) 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 layoutManager = (recyclerView.layoutManager as? LinearLayoutManager) ?: return
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
if (firstVisibleItemPosition <= offsetTop) { if (firstVisibleItemPosition <= offsetTop) {
onScrolledToTop(recyclerView) onScrolledToStart(recyclerView)
return return
} }
val visibleItemCount = layoutManager.childCount 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) abstract fun onScrolledToEnd(recyclerView: RecyclerView)
} }

@ -6,7 +6,7 @@ class PaginationScrollListener(offset: Int, private val callback: Callback) : Bo
private var lastTotalCount = 0 private var lastTotalCount = 0
override fun onScrolledToTop(recyclerView: RecyclerView) = Unit override fun onScrolledToStart(recyclerView: RecyclerView) = Unit
override fun onScrolledToEnd(recyclerView: RecyclerView) { override fun onScrolledToEnd(recyclerView: RecyclerView) {
val total = recyclerView.adapter?.itemCount ?: 0 val total = recyclerView.adapter?.itemCount ?: 0

@ -2,10 +2,16 @@ package org.koitharu.kotatsu.ui.reader
import android.net.Uri import android.net.Uri
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.core.model.MangaPage import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.core.prefs.ReaderMode
import org.koitharu.kotatsu.ui.common.BaseFragment 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 abstract val hasItems: Boolean
@ -31,4 +37,9 @@ abstract class BaseReaderFragment(@LayoutRes contentLayoutId: Int) : BaseFragmen
* Handled by activity * Handled by activity
*/ */
override fun onPageSaved(uri: Uri?) = Unit 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 androidx.fragment.app.commit
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_reader.* import kotlinx.android.synthetic.main.activity_reader.*
import moxy.MvpDelegate
import moxy.ktx.moxyPresenter import moxy.ktx.moxyPresenter
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.Manga 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 val presenter by moxyPresenter(factory = ReaderPresenter.Companion::getInstance)
private lateinit var state: ReaderState lateinit var state: ReaderState
private set
private lateinit var touchHelper: GridTouchHelper private lateinit var touchHelper: GridTouchHelper
@ -71,25 +73,19 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
insets insets
} }
presenter.loadChapter(state) if (savedInstanceState?.containsKey(MvpDelegate.MOXY_DELEGATE_TAGS_KEY) != true) {
presenter.loadChapter(state.manga, state.chapterId)
}
} }
override fun onInitReader(pages: List<MangaPage>, mode: ReaderMode, state: ReaderState) { override fun onInitReader(mode: ReaderMode) {
val currentReader = reader if (reader == null) {
when (mode) { setReader(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())
}
}
} }
} }
override fun onPagesLoaded(chapterId: Long, pages: List<MangaPage>) = Unit
override fun onPause() { override fun onPause() {
reader?.let { reader?.let {
state = state.copy(page = it.currentPageIndex) state = state.copy(page = it.currentPageIndex)
@ -176,15 +172,7 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
override fun onGridTouch(area: Int) { override fun onGridTouch(area: Int) {
when (area) { when (area) {
GridTouchHelper.AREA_CENTER -> { GridTouchHelper.AREA_CENTER -> {
if (appbar_top.isVisible) { setUiIsVisible(!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()
}
} }
GridTouchHelper.AREA_TOP, GridTouchHelper.AREA_TOP,
GridTouchHelper.AREA_LEFT -> { GridTouchHelper.AREA_LEFT -> {
@ -222,7 +210,7 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
chapterId = chapter.id, chapterId = chapter.id,
page = 0 page = 0
) )
presenter.loadChapter(state) presenter.loadChapter(state.manga, chapter.id)
} }
override fun onPageSelected(page: MangaPage) { override fun onPageSelected(page: MangaPage) {
@ -239,7 +227,12 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
state = state.copy(page = it.currentPageIndex) state = state.copy(page = it.currentPageIndex)
} }
presenter.saveState(state, mode) 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?) { override fun onPageSaved(uri: Uri?) {
@ -259,6 +252,36 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
}.show() }.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 { companion object {
private const val EXTRA_STATE = "state" private const val EXTRA_STATE = "state"

@ -11,6 +11,7 @@ import moxy.presenterScope
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaPage import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.core.prefs.ReaderMode
import org.koitharu.kotatsu.domain.MangaPreferencesRepository import org.koitharu.kotatsu.domain.MangaPreferencesRepository
@ -26,29 +27,42 @@ import org.koitharu.kotatsu.utils.ext.mimeType
@InjectViewState @InjectViewState
class ReaderPresenter : BasePresenter<ReaderView>() { class ReaderPresenter : BasePresenter<ReaderView>() {
fun loadChapter(state: ReaderState) { private var isInitialized = false
fun loadChapter(manga: Manga, chapterId: Long) {
presenterScope.launch { presenterScope.launch {
viewState.onLoadingStateChanged(isLoading = true) viewState.onLoadingStateChanged(isLoading = true)
try { try {
val (pages, mode) = withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val repo = MangaProviderFactory.create(state.manga.source) val repo = MangaProviderFactory.create(manga.source)
val chapter = state.chapter ?: repo.getDetails(state.manga).chapters val chapter = (manga.chapters ?: repo.getDetails(manga).chapters?.also {
?.first { it.id == state.chapterId } withContext(Dispatchers.Main) {
?: throw RuntimeException("Chapter ${state.chapterId} not found") viewState.onChaptersLoader(it)
var mode = MangaPreferencesRepository().getReaderMode(state.manga.id) }
})?.find { it.id == chapterId }
?: throw RuntimeException("Chapter ${chapterId} not found")
val pages = repo.getPages(chapter) val pages = repo.getPages(chapter)
if (mode == null) { if (!isInitialized) {
mode = MangaUtils.determineReaderMode(pages) val prefs = MangaPreferencesRepository()
if (mode != null) { var mode = prefs.getReaderMode(manga.id)
MangaPreferencesRepository().saveData( if (mode == null) {
mangaId = state.manga.id, mode = MangaUtils.determineReaderMode(pages)
mode = mode 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) { } catch (e: Exception) {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
e.printStackTrace() e.printStackTrace()

@ -4,13 +4,20 @@ import android.net.Uri
import moxy.MvpView import moxy.MvpView
import moxy.viewstate.strategy.alias.AddToEndSingle import moxy.viewstate.strategy.alias.AddToEndSingle
import moxy.viewstate.strategy.alias.OneExecution 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.model.MangaPage
import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.core.prefs.ReaderMode
interface ReaderView : MvpView { interface ReaderView : MvpView {
@AddToEndSingle @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 @AddToEndSingle
fun onLoadingStateChanged(isLoading: Boolean) fun onLoadingStateChanged(isLoading: Boolean)

@ -6,17 +6,16 @@ import kotlinx.android.synthetic.main.fragment_reader_standard.*
import moxy.ktx.moxyPresenter import moxy.ktx.moxyPresenter
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.MangaPage 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.BaseReaderFragment
import org.koitharu.kotatsu.ui.reader.PageLoader import org.koitharu.kotatsu.ui.reader.PageLoader
import org.koitharu.kotatsu.ui.reader.ReaderPresenter import org.koitharu.kotatsu.ui.reader.ReaderPresenter
import org.koitharu.kotatsu.ui.reader.ReaderState
class StandardReaderFragment : BaseReaderFragment(R.layout.fragment_reader_standard) { class StandardReaderFragment : BaseReaderFragment(R.layout.fragment_reader_standard) {
private val presenter by moxyPresenter(factory = ReaderPresenter.Companion::getInstance) private val presenter by moxyPresenter(factory = ReaderPresenter.Companion::getInstance)
private var adapter: PagesAdapter? = null private var adapter: PagesAdapter? = null
private var isBusy: Boolean = true
private lateinit var loader: PageLoader private lateinit var loader: PageLoader
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -31,11 +30,21 @@ class StandardReaderFragment : BaseReaderFragment(R.layout.fragment_reader_stand
pager.offscreenPageLimit = 2 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 { adapter?.let {
it.replaceData(pages) 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() { override fun onDestroy() {
@ -55,4 +64,9 @@ class StandardReaderFragment : BaseReaderFragment(R.layout.fragment_reader_stand
override fun setCurrentPage(index: Int, smooth: Boolean) { override fun setCurrentPage(index: Int, smooth: Boolean) {
pager.setCurrentItem(index, smooth) 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 moxy.ktx.moxyPresenter
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.MangaPage 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.BaseReaderFragment
import org.koitharu.kotatsu.ui.reader.PageLoader import org.koitharu.kotatsu.ui.reader.PageLoader
import org.koitharu.kotatsu.ui.reader.ReaderPresenter import org.koitharu.kotatsu.ui.reader.ReaderPresenter
import org.koitharu.kotatsu.ui.reader.ReaderState
import org.koitharu.kotatsu.utils.ext.firstItem import org.koitharu.kotatsu.utils.ext.firstItem
class WebtoonReaderFragment : BaseReaderFragment(R.layout.fragment_reader_webtoon) { class WebtoonReaderFragment : BaseReaderFragment(R.layout.fragment_reader_webtoon) {
@ -31,10 +29,14 @@ class WebtoonReaderFragment : BaseReaderFragment(R.layout.fragment_reader_webtoo
recyclerView.adapter = adapter recyclerView.adapter = adapter
} }
override fun onInitReader(pages: List<MangaPage>, mode: ReaderMode, state: ReaderState) { override fun onPagesLoaded(chapterId: Long, pages: List<MangaPage>) {
adapter?.let { adapter?.let {
it.replaceData(pages) 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.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup import com.google.android.material.chip.ChipGroup
import org.koitharu.kotatsu.ui.common.ChipsFactory 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) val menu = PopupMenu(context, this)
menu.inflate(menuRes) menu.inflate(menuRes)
menu.setOnMenuItemClickListener(onItemClick) menu.setOnMenuItemClickListener(onItemClick)
@ -159,3 +163,13 @@ fun View.measureWidth(): Int {
measuredWidth measuredWidth
} else vw } else vw
} }
fun ViewPager2.doOnPageChanged(callback: (Int) -> Unit) {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
callback(position)
}
})
}

@ -6,7 +6,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { 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" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong

@ -1,6 +1,6 @@
#Wed Jan 29 18:34:45 EET 2020 #Wed Feb 26 19:30:06 EET 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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

Loading…
Cancel
Save