Double reader integration
This commit is contained in:
@@ -6,6 +6,7 @@ enum class ReaderMode(val id: Int) {
|
||||
REVERSED(3),
|
||||
VERTICAL(4),
|
||||
WEBTOON(2),
|
||||
DOUBLE(5),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -6,7 +6,7 @@ import androidx.fragment.app.commit
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
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.standard.PagerReaderFragment
|
||||
import org.koitharu.kotatsu.reader.ui.pager.vertical.VerticalReaderFragment
|
||||
@@ -23,7 +23,7 @@ class ReaderManager(
|
||||
init {
|
||||
val isTablet = container.resources.getBoolean(R.bool.is_tablet)
|
||||
modeMap[ReaderMode.STANDARD] = if (isTablet) {
|
||||
DoublePageReaderFragment::class.java
|
||||
DoubleReaderFragment::class.java
|
||||
} else {
|
||||
PagerReaderFragment::class.java
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ class ReaderConfigSheet :
|
||||
binding.buttonReversed.isChecked = mode == ReaderMode.REVERSED
|
||||
binding.buttonWebtoon.isChecked = mode == ReaderMode.WEBTOON
|
||||
binding.buttonVertical.isChecked = mode == ReaderMode.VERTICAL
|
||||
binding.buttonDouble.isChecked = mode == ReaderMode.DOUBLE
|
||||
|
||||
binding.checkableGroup.addOnButtonCheckedListener(this)
|
||||
binding.buttonSavePage.setOnClickListener(this)
|
||||
@@ -155,6 +156,7 @@ class ReaderConfigSheet :
|
||||
R.id.button_webtoon -> ReaderMode.WEBTOON
|
||||
R.id.button_reversed -> ReaderMode.REVERSED
|
||||
R.id.button_vertical -> ReaderMode.VERTICAL
|
||||
R.id.button_double -> ReaderMode.DOUBLE
|
||||
else -> return
|
||||
}
|
||||
if (newMode == mode) {
|
||||
|
||||
@@ -7,8 +7,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.yield
|
||||
import org.koitharu.kotatsu.R
|
||||
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.reader.domain.PageLoader
|
||||
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.BaseReaderFragment
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PageHolder
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DoublePageReaderFragment : BaseReaderFragment<FragmentReaderDoubleBinding>() {
|
||||
class DoubleReaderFragment : BaseReaderFragment<FragmentReaderDoubleBinding>() {
|
||||
|
||||
@Inject
|
||||
lateinit var networkState: NetworkState
|
||||
@@ -44,7 +46,7 @@ class DoublePageReaderFragment : BaseReaderFragment<FragmentReaderDoubleBinding>
|
||||
super.onViewBindingCreated(binding, savedInstanceState)
|
||||
with(binding.recyclerView) {
|
||||
adapter = readerAdapter
|
||||
addOnScrollListener(PageScrollListener())
|
||||
addOnScrollListener(PageScrollListener(viewModel))
|
||||
DoublePageSnapHelper().attachToRecyclerView(this)
|
||||
}
|
||||
}
|
||||
@@ -54,28 +56,28 @@ class DoublePageReaderFragment : BaseReaderFragment<FragmentReaderDoubleBinding>
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) =
|
||||
coroutineScope {
|
||||
val items = async {
|
||||
requireAdapter().setItems(pages)
|
||||
yield()
|
||||
}
|
||||
if (pendingState != null) {
|
||||
val position = pages.indexOfFirst {
|
||||
it.chapterId == pendingState.chapterId && it.index == pendingState.page
|
||||
}
|
||||
items.await()
|
||||
if (position != -1) {
|
||||
requireViewBinding().recyclerView.firstVisibleItemPosition = position or 1
|
||||
notifyPageChanged(position)
|
||||
} else {
|
||||
Snackbar.make(requireView(), R.string.not_found_404, Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
items.await()
|
||||
}
|
||||
override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) = coroutineScope {
|
||||
val items = launch {
|
||||
requireAdapter().setItems(pages)
|
||||
yield()
|
||||
}
|
||||
if (pendingState != null) {
|
||||
var position = pages.indexOfFirst {
|
||||
it.chapterId == pendingState.chapterId && it.index == pendingState.page
|
||||
}
|
||||
items.join()
|
||||
if (position != -1) {
|
||||
position = position or 1
|
||||
requireViewBinding().recyclerView.firstVisibleItemPosition = position
|
||||
viewModel.onCurrentPageChanged(position, position + 1)
|
||||
} else {
|
||||
Snackbar.make(requireView(), R.string.not_found_404, Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
items.join()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateAdapter() = DoublePagesAdapter(
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
@@ -85,6 +87,16 @@ class DoublePageReaderFragment : BaseReaderFragment<FragmentReaderDoubleBinding>
|
||||
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) {
|
||||
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 {
|
||||
val lm = layoutManager as LinearLayoutManager
|
||||
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 var lastPage = RecyclerView.NO_POSITION
|
||||
private class PageScrollListener(
|
||||
private val viewModel: ReaderViewModel,
|
||||
) : RecyclerView.OnScrollListener() {
|
||||
|
||||
private var firstPos = RecyclerView.NO_POSITION
|
||||
private var lastPos = RecyclerView.NO_POSITION
|
||||
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
val page = recyclerView.currentItem()
|
||||
if (page != lastPage) {
|
||||
lastPage = page
|
||||
notifyPageChanged(page)
|
||||
val lm = recyclerView.layoutManager as? LinearLayoutManager
|
||||
if (lm == null) {
|
||||
firstPos = RecyclerView.NO_POSITION
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
24
app/src/main/res/drawable/ic_pager_double_ltr.xml
Normal file
24
app/src/main/res/drawable/ic_pager_double_ltr.xml
Normal file
@@ -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"
|
||||
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>
|
||||
|
||||
<TextView
|
||||
|
||||
@@ -567,4 +567,5 @@
|
||||
<string name="incognito_mode_hint">Your reading progress will not be saved</string>
|
||||
<string name="vertical">Vertical</string>
|
||||
<string name="last_read">Last read</string>
|
||||
<string name="two_pages">Two pages</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user