diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt
index 7c82c921a8..684dc5c29c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt
@@ -25,6 +25,8 @@ object PreferenceKeys {
const val dualPageSplit = "pref_dual_page_split"
+ const val dualPageInvert = "pref_dual_page_invert"
+
const val showReadingMode = "pref_show_reading_mode"
const val trueColor = "pref_true_color_key"
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt
index 0b5bae2545..7e75acca6c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt
@@ -91,6 +91,8 @@ class PreferencesHelper(val context: Context) {
fun dualPageSplit() = flowPrefs.getBoolean(Keys.dualPageSplit, false)
+ fun dualPageInvert() = flowPrefs.getBoolean(Keys.dualPageInvert, false)
+
fun showReadingMode() = prefs.getBoolean(Keys.showReadingMode, true)
fun trueColor() = flowPrefs.getBoolean(Keys.trueColor, false)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSettingsSheet.kt
index a8896a5bc9..2ee7533f24 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSettingsSheet.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSettingsSheet.kt
@@ -6,14 +6,17 @@ import android.widget.Spinner
import androidx.annotation.ArrayRes
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
+import androidx.lifecycle.lifecycleScope
import com.tfcporciuncula.flow.Preference
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
+import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.databinding.ReaderSettingsSheetBinding
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
+import kotlinx.coroutines.flow.launchIn
import uy.kohesive.injekt.injectLazy
/**
@@ -71,6 +74,12 @@ class ReaderSettingsSheet(private val activity: ReaderActivity) : BaseBottomShee
binding.alwaysShowChapterTransition.bindToPreference(preferences.alwaysShowChapterTransition())
binding.pageTransitions.bindToPreference(preferences.pageTransitions())
+ // Makes so that dual page invert gets hidden away when turning of dual page split
+ preferences.dualPageSplit()
+ .asImmediateFlow { binding.dualPageInvert.isVisible = it }
+ .launchIn(activity.lifecycleScope)
+ binding.dualPageInvert.bindToPreference(preferences.dualPageInvert())
+
// If the preference is explicitly disabled, that means the setting was configured since there is a cutout
if (activity.hasCutout || !preferences.cutoutShort().get()) {
binding.cutoutShort.isVisible = true
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt
index 25b417cebc..6614f9c6fb 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt
@@ -25,6 +25,7 @@ abstract class ViewerConfig(preferences: PreferencesHelper, private val scope: C
var trueColor = false
var alwaysShowChapterTransition = true
var dualPageSplit = false
+ var dualPageInvert = false
var navigationMode = 0
protected set
@@ -58,6 +59,9 @@ abstract class ViewerConfig(preferences: PreferencesHelper, private val scope: C
preferences.dualPageSplit()
.register({ dualPageSplit = it }, { imagePropertyChangedListener?.invoke() })
+
+ preferences.dualPageInvert()
+ .register({ dualPageInvert = it }, { imagePropertyChangedListener?.invoke() })
}
protected abstract fun defaultNavigation(): ViewerNavigation
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
index 36e4fff0a9..633e7af31b 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
@@ -264,24 +264,29 @@ class PagerPageHolder(
else -> ImageUtil.isDoublePage(inputStream)
}
inputStream = stream
- if (isDoublePage) {
- val side = when {
- viewer is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.RIGHT
- viewer is R2LPagerViewer && page is InsertPage -> ImageUtil.Side.LEFT
- viewer is L2RPagerViewer && page !is InsertPage -> ImageUtil.Side.LEFT
- viewer is R2LPagerViewer && page !is InsertPage -> ImageUtil.Side.RIGHT
- viewer is VerticalPagerViewer && page !is InsertPage -> ImageUtil.Side.RIGHT
- viewer is VerticalPagerViewer && page is InsertPage -> ImageUtil.Side.LEFT
- else -> error("We should choose a side!")
- }
- if (page !is InsertPage) {
- onPageSplit()
- }
+ if (!isDoublePage) return inputStream
- inputStream = ImageUtil.splitInHalf(inputStream, side)
+ var side = when {
+ viewer is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.RIGHT
+ (viewer is R2LPagerViewer || viewer is VerticalPagerViewer) && page is InsertPage -> ImageUtil.Side.LEFT
+ viewer is L2RPagerViewer && page !is InsertPage -> ImageUtil.Side.LEFT
+ (viewer is R2LPagerViewer || viewer is VerticalPagerViewer) && page !is InsertPage -> ImageUtil.Side.RIGHT
+ else -> error("We should choose a side!")
}
- return inputStream
+
+ if (viewer.config.dualPageInvert) {
+ side = when (side) {
+ ImageUtil.Side.RIGHT -> ImageUtil.Side.LEFT
+ ImageUtil.Side.LEFT -> ImageUtil.Side.RIGHT
+ }
+ }
+
+ if (page !is InsertPage) {
+ onPageSplit()
+ }
+
+ return ImageUtil.splitInHalf(inputStream, side)
}
private fun onPageSplit() {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt
index 6268f069d5..ca124c2f59 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt
@@ -292,7 +292,8 @@ class WebtoonPageHolder(
openStream = if (!isDoublePage) {
stream
} else {
- ImageUtil.splitAndMerge(stream)
+ val upperSide = if (viewer.config.dualPageInvert) ImageUtil.Side.LEFT else ImageUtil.Side.RIGHT
+ ImageUtil.splitAndMerge(stream, upperSide)
}
}
if (!isAnimated) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt
index dca513d5c7..c0a6ea7194 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt
@@ -55,6 +55,13 @@ class SettingsReaderController : SettingsController() {
titleRes = R.string.pref_dual_page_split
defaultValue = false
}
+ switchPreference {
+ key = Keys.dualPageInvert
+ titleRes = R.string.pref_dual_page_invert
+ summaryRes = R.string.pref_dual_page_invert_summary
+ defaultValue = false
+ preferences.dualPageSplit().asImmediateFlow { isVisible = it }.launchIn(viewScope)
+ }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
switchPreference {
key = Keys.trueColor
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt
index 4716fa24ca..73fda3cc52 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt
@@ -115,7 +115,7 @@ object ImageUtil {
/**
* Split the image into left and right parts, then merge them into a new image.
*/
- fun splitAndMerge(imageStream: InputStream): InputStream {
+ fun splitAndMerge(imageStream: InputStream, upperSide: Side): InputStream {
val imageBytes = imageStream.readBytes()
val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
@@ -125,11 +125,17 @@ object ImageUtil {
val result = Bitmap.createBitmap(width / 2, height * 2, Bitmap.Config.ARGB_8888)
val canvas = Canvas(result)
// right -> upper
- val rightPart = Rect(width - width / 2, 0, width, height)
+ val rightPart = when (upperSide) {
+ Side.RIGHT -> Rect(width - width / 2, 0, width, height)
+ Side.LEFT -> Rect(0, 0, width / 2, height)
+ }
val upperPart = Rect(0, 0, width / 2, height)
canvas.drawBitmap(imageBitmap, rightPart, upperPart, null)
// left -> bottom
- val leftPart = Rect(0, 0, width / 2, height)
+ val leftPart = when (upperSide) {
+ Side.LEFT -> Rect(width - width / 2, 0, width, height)
+ Side.RIGHT -> Rect(0, 0, width / 2, height)
+ }
val bottomPart = Rect(0, height, width / 2, height * 2)
canvas.drawBitmap(imageBitmap, leftPart, bottomPart, null)
diff --git a/app/src/main/res/layout/reader_settings_sheet.xml b/app/src/main/res/layout/reader_settings_sheet.xml
index d28d64fc84..95624aafba 100644
--- a/app/src/main/res/layout/reader_settings_sheet.xml
+++ b/app/src/main/res/layout/reader_settings_sheet.xml
@@ -159,6 +159,16 @@
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/fullscreen" />
+
+
Fullscreen
Dual page split (ALPHA)
+ Invert dual page split placement
+ If the placement of the dual page split doesn\'t match reading direction
Show content in cutout area
Lock orientation
Animate page transitions