Double reader integration

master
Koitharu 2 years ago
parent f881cc439a
commit 72187e7da0
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -6,6 +6,7 @@ enum class ReaderMode(val id: Int) {
REVERSED(3), REVERSED(3),
VERTICAL(4), VERTICAL(4),
WEBTOON(2), WEBTOON(2),
DOUBLE(5),
; ;
companion object { companion object {

@ -6,7 +6,7 @@ import androidx.fragment.app.commit
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.core.prefs.ReaderMode
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderFragment import org.koitharu.kotatsu.reader.ui.pager.BaseReaderFragment
import org.koitharu.kotatsu.reader.ui.pager.doublepage.DoublePageReaderFragment import org.koitharu.kotatsu.reader.ui.pager.doublepage.DoubleReaderFragment
import org.koitharu.kotatsu.reader.ui.pager.reversed.ReversedReaderFragment import org.koitharu.kotatsu.reader.ui.pager.reversed.ReversedReaderFragment
import org.koitharu.kotatsu.reader.ui.pager.standard.PagerReaderFragment import org.koitharu.kotatsu.reader.ui.pager.standard.PagerReaderFragment
import org.koitharu.kotatsu.reader.ui.pager.vertical.VerticalReaderFragment import org.koitharu.kotatsu.reader.ui.pager.vertical.VerticalReaderFragment
@ -23,7 +23,7 @@ class ReaderManager(
init { init {
val isTablet = container.resources.getBoolean(R.bool.is_tablet) val isTablet = container.resources.getBoolean(R.bool.is_tablet)
modeMap[ReaderMode.STANDARD] = if (isTablet) { modeMap[ReaderMode.STANDARD] = if (isTablet) {
DoublePageReaderFragment::class.java DoubleReaderFragment::class.java
} else { } else {
PagerReaderFragment::class.java PagerReaderFragment::class.java
} }

@ -80,6 +80,7 @@ class ReaderConfigSheet :
binding.buttonReversed.isChecked = mode == ReaderMode.REVERSED binding.buttonReversed.isChecked = mode == ReaderMode.REVERSED
binding.buttonWebtoon.isChecked = mode == ReaderMode.WEBTOON binding.buttonWebtoon.isChecked = mode == ReaderMode.WEBTOON
binding.buttonVertical.isChecked = mode == ReaderMode.VERTICAL binding.buttonVertical.isChecked = mode == ReaderMode.VERTICAL
binding.buttonDouble.isChecked = mode == ReaderMode.DOUBLE
binding.checkableGroup.addOnButtonCheckedListener(this) binding.checkableGroup.addOnButtonCheckedListener(this)
binding.buttonSavePage.setOnClickListener(this) binding.buttonSavePage.setOnClickListener(this)
@ -155,6 +156,7 @@ class ReaderConfigSheet :
R.id.button_webtoon -> ReaderMode.WEBTOON R.id.button_webtoon -> ReaderMode.WEBTOON
R.id.button_reversed -> ReaderMode.REVERSED R.id.button_reversed -> ReaderMode.REVERSED
R.id.button_vertical -> ReaderMode.VERTICAL R.id.button_vertical -> ReaderMode.VERTICAL
R.id.button_double -> ReaderMode.DOUBLE
else -> return else -> return
} }
if (newMode == mode) { if (newMode == mode) {

@ -7,8 +7,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield import kotlinx.coroutines.yield
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.os.NetworkState import org.koitharu.kotatsu.core.os.NetworkState
@ -17,14 +17,16 @@ import org.koitharu.kotatsu.databinding.FragmentReaderDoubleBinding
import org.koitharu.kotatsu.parsers.util.toIntUp import org.koitharu.kotatsu.parsers.util.toIntUp
import org.koitharu.kotatsu.reader.domain.PageLoader import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.reader.ui.ReaderViewModel
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderFragment import org.koitharu.kotatsu.reader.ui.pager.BaseReaderFragment
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
import org.koitharu.kotatsu.reader.ui.pager.standard.PageHolder
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@AndroidEntryPoint @AndroidEntryPoint
class DoublePageReaderFragment : BaseReaderFragment<FragmentReaderDoubleBinding>() { class DoubleReaderFragment : BaseReaderFragment<FragmentReaderDoubleBinding>() {
@Inject @Inject
lateinit var networkState: NetworkState lateinit var networkState: NetworkState
@ -44,7 +46,7 @@ class DoublePageReaderFragment : BaseReaderFragment<FragmentReaderDoubleBinding>
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
with(binding.recyclerView) { with(binding.recyclerView) {
adapter = readerAdapter adapter = readerAdapter
addOnScrollListener(PageScrollListener()) addOnScrollListener(PageScrollListener(viewModel))
DoublePageSnapHelper().attachToRecyclerView(this) DoublePageSnapHelper().attachToRecyclerView(this)
} }
} }
@ -54,26 +56,26 @@ class DoublePageReaderFragment : BaseReaderFragment<FragmentReaderDoubleBinding>
super.onDestroyView() super.onDestroyView()
} }
override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) = override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) = coroutineScope {
coroutineScope { val items = launch {
val items = async {
requireAdapter().setItems(pages) requireAdapter().setItems(pages)
yield() yield()
} }
if (pendingState != null) { if (pendingState != null) {
val position = pages.indexOfFirst { var position = pages.indexOfFirst {
it.chapterId == pendingState.chapterId && it.index == pendingState.page it.chapterId == pendingState.chapterId && it.index == pendingState.page
} }
items.await() items.join()
if (position != -1) { if (position != -1) {
requireViewBinding().recyclerView.firstVisibleItemPosition = position or 1 position = position or 1
notifyPageChanged(position) requireViewBinding().recyclerView.firstVisibleItemPosition = position
viewModel.onCurrentPageChanged(position, position + 1)
} else { } else {
Snackbar.make(requireView(), R.string.not_found_404, Snackbar.LENGTH_SHORT) Snackbar.make(requireView(), R.string.not_found_404, Snackbar.LENGTH_SHORT)
.show() .show()
} }
} else { } else {
items.await() items.join()
} }
} }
@ -85,6 +87,16 @@ class DoublePageReaderFragment : BaseReaderFragment<FragmentReaderDoubleBinding>
exceptionResolver = exceptionResolver, exceptionResolver = exceptionResolver,
) )
override fun onZoomIn() {
(viewBinding ?: return).recyclerView.pageHolders()
.forEach { it.onZoomIn() }
}
override fun onZoomOut() {
(viewBinding ?: return).recyclerView.pageHolders()
.forEach { it.onZoomOut() }
}
override fun switchPageBy(delta: Int) { override fun switchPageBy(delta: Int) {
switchPageTo((requireViewBinding().recyclerView.currentItem() + delta) or 1, delta.absoluteValue > 1) switchPageTo((requireViewBinding().recyclerView.currentItem() + delta) or 1, delta.absoluteValue > 1)
} }
@ -103,25 +115,38 @@ class DoublePageReaderFragment : BaseReaderFragment<FragmentReaderDoubleBinding>
) )
} }
private fun notifyPageChanged(page: Int) {
viewModel.onCurrentPageChanged(page)
}
private fun RecyclerView.currentItem(): Int { private fun RecyclerView.currentItem(): Int {
val lm = layoutManager as LinearLayoutManager val lm = layoutManager as LinearLayoutManager
return ((lm.findFirstVisibleItemPosition() + lm.findLastVisibleItemPosition()) / 2f).toIntUp() return ((lm.findFirstVisibleItemPosition() + lm.findLastVisibleItemPosition()) / 2f).toIntUp()
} }
private inner class PageScrollListener : RecyclerView.OnScrollListener() { private fun RecyclerView.pageHolders(): Sequence<PageHolder> {
val lm = layoutManager as? LinearLayoutManager ?: return emptySequence()
return (lm.findFirstVisibleItemPosition()..lm.findLastVisibleItemPosition()).asSequence()
.mapNotNull { findViewHolderForAdapterPosition(it) as? PageHolder }
}
private class PageScrollListener(
private val viewModel: ReaderViewModel,
) : RecyclerView.OnScrollListener() {
private var lastPage = RecyclerView.NO_POSITION private var firstPos = RecyclerView.NO_POSITION
private var lastPos = RecyclerView.NO_POSITION
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
val page = recyclerView.currentItem() val lm = recyclerView.layoutManager as? LinearLayoutManager
if (page != lastPage) { if (lm == null) {
lastPage = page firstPos = RecyclerView.NO_POSITION
notifyPageChanged(page) lastPos = RecyclerView.NO_POSITION
return
}
val newFirstPos = lm.findFirstVisibleItemPosition()
val newLastPos = lm.findLastVisibleItemPosition()
if (newFirstPos != firstPos || newLastPos != lastPos) {
firstPos = newFirstPos
lastPos = newLastPos
viewModel.onCurrentPageChanged(newFirstPos, newLastPos)
} }
} }
} }

@ -0,0 +1,24 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M4,4C2.89,4 2,4.89 2,6L2,18C2,19.11 2.89,20 4,20L9,20L9,18L8,18L6,18L4,18L4,6L6,6L8,6L9,6L9,4L4,4zM15,4L15,6L16,6L18,6L20,6L20,18L18,18L16,18L15,18L15,20L20,20C21.1,20 22,19.11 22,18L22,6C22,4.89 21.1,4 20,4L15,4z"/>
<path
android:fillColor="#FF000000"
android:pathData="m11,22.43v-3h2v3z"/>
<path
android:fillColor="#FF000000"
android:pathData="m13,1.57v3h-2v-3h2"/>
<path
android:fillColor="#FF000000"
android:pathData="m11,17.43v-3h2v3h-2"/>
<path
android:fillColor="#FF000000"
android:pathData="m11,9.57v-3h2v3h-2"/>
<path
android:fillColor="#FF000000"
android:pathData="m14.5,15v-2h-7v-2h7V9l4,3 -4,3"/>
</vector>

@ -119,6 +119,15 @@
android:text="@string/webtoon" android:text="@string/webtoon"
app:icon="@drawable/ic_script" /> app:icon="@drawable/ic_script" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_double"
style="@style/Widget.Kotatsu.ToggleButton.Vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/two_pages"
app:icon="@drawable/ic_pager_double_ltr" />
</com.google.android.material.button.MaterialButtonToggleGroup> </com.google.android.material.button.MaterialButtonToggleGroup>
<TextView <TextView

@ -567,4 +567,5 @@
<string name="incognito_mode_hint">Your reading progress will not be saved</string> <string name="incognito_mode_hint">Your reading progress will not be saved</string>
<string name="vertical">Vertical</string> <string name="vertical">Vertical</string>
<string name="last_read">Last read</string> <string name="last_read">Last read</string>
<string name="two_pages">Two pages</string>
</resources> </resources>

Loading…
Cancel
Save