diff --git a/app/build.gradle.kts b/app/build.gradle.kts index dffc18fdc2..75e130dee7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -136,6 +136,7 @@ dependencies { implementation("androidx.core:core-ktx:1.8.0") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1") implementation("com.google.android.flexbox:flexbox:3.0.0") + implementation("androidx.window:window:1.0.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") @@ -144,7 +145,7 @@ dependencies { implementation("com.google.firebase:firebase-core:21.1.0") implementation("com.google.firebase:firebase-analytics-ktx:21.1.0") - val lifecycleVersion = "2.4.0-rc01" + val lifecycleVersion = "2.5.1" kapt("androidx.lifecycle:lifecycle-compiler:$lifecycleVersion") implementation("androidx.lifecycle:lifecycle-process:$lifecycleVersion") implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion") diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index cccd7aa6ce..8479d0b784 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -78,8 +78,6 @@ open class App : Application(), DefaultLifecycleObserver { setupAcra() setupNotificationChannels() - ProcessLifecycleOwner.get().lifecycle.addObserver(this) - MangaCoverMetadata.load() preferences.nightMode() .asImmediateFlow { AppCompatDelegate.setDefaultNightMode(it) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 3945555d40..1d3ced4cad 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -46,7 +46,11 @@ import androidx.core.view.updateLayoutParams import androidx.core.view.updatePaddingRelative import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat +import androidx.window.layout.DisplayFeature +import androidx.window.layout.FoldingFeature +import androidx.window.layout.WindowInfoTracker import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog @@ -117,6 +121,7 @@ import eu.kanade.tachiyomi.widget.doOnStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.merge @@ -211,6 +216,11 @@ class ReaderActivity : BaseRxActivity() { } var isScrollingThroughPagesOrChapters = false + private var hingeGapSize = 0 + set(value) { + field = value + (viewer as? PagerViewer)?.config?.hingeGapSize = value + } companion object { @@ -315,6 +325,30 @@ class ReaderActivity : BaseRxActivity() { SecureActivityDelegate.setSecure(this) } reEnableBackPressedCallBack() + lifecycleScope.launchUI { + // The block passed to repeatOnLifecycle is executed when the lifecycle + // is at least STARTED and is cancelled when the lifecycle is STOPPED. + // It automatically restarts the block when the lifecycle is STARTED again. + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + // Safely collects from windowInfoRepository when the lifecycle is + // STARTED and stops collection when the lifecycle is STOPPED. + WindowInfoTracker.getOrCreate(this@ReaderActivity).windowLayoutInfo(this@ReaderActivity) + .collect { newLayoutInfo -> + hingeGapSize = 0 + for (displayFeature: DisplayFeature in newLayoutInfo.displayFeatures) { + if (displayFeature is FoldingFeature && displayFeature.occlusionType == FoldingFeature.OcclusionType.FULL && + displayFeature.isSeparating && displayFeature.orientation == FoldingFeature.Orientation.VERTICAL + ) { + hingeGapSize = displayFeature.bounds.width() + } + } +// newLayoutInfo.displayFeatures. + // Check newLayoutInfo.displayFeatures to see if the + // feature list includes a FoldingFeature, then check + // the feature's data. + } + } + } } /** @@ -1097,6 +1131,7 @@ class ReaderActivity : BaseRxActivity() { } if (newViewer is PagerViewer) { + newViewer.config.hingeGapSize = hingeGapSize if (preferences.pageLayout().get() == PageLayout.AUTOMATIC.value) { setDoublePageMode(newViewer) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt index ae8e83b1b4..3aac71b74f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt @@ -64,6 +64,8 @@ class PagerConfig( } } + var hingeGapSize = 0 + var invertDoublePages = false var autoDoublePages = preferences.pageLayout().get() == PageLayout.AUTOMATIC.value 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 ef295096ad..e413407e5f 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 @@ -840,7 +840,7 @@ class PagerPageHolder( imageStream.close() imageStream2.close() - return ImageUtil.mergeBitmaps(imageBitmap, imageBitmap2, isLTR, bg) { + return ImageUtil.mergeBitmaps(imageBitmap, imageBitmap2, isLTR, bg, viewer.config.hingeGapSize) { scope?.launchUI { if (it == 100) { progressBar.completeAndFadeOut() 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 64f19319e3..22e8f42003 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 @@ -429,6 +429,7 @@ object ImageUtil { imageBitmap2: Bitmap, isLTR: Boolean, @ColorInt background: Int = Color.WHITE, + hingeGap: Int = 0, progressCallback: ((Int) -> Unit)? = null, ): ByteArrayInputStream { val height = imageBitmap.height @@ -436,21 +437,22 @@ object ImageUtil { val height2 = imageBitmap2.height val width2 = imageBitmap2.width val maxHeight = max(height, height2) - val result = Bitmap.createBitmap(width + width2, max(height, height2), Bitmap.Config.ARGB_8888) + val maxWidth = max(width, width2) + val result = Bitmap.createBitmap((maxWidth * 2) + hingeGap, max(height, height2), Bitmap.Config.ARGB_8888) val canvas = Canvas(result) canvas.drawColor(background) val upperPart = Rect( - if (isLTR) 0 else width2, + if (isLTR) max(maxWidth - imageBitmap.width, 0) else maxWidth + hingeGap, (maxHeight - imageBitmap.height) / 2, - (if (isLTR) 0 else width2) + imageBitmap.width, + (if (isLTR) max(maxWidth - imageBitmap.width, 0) else maxWidth + hingeGap) + imageBitmap.width, imageBitmap.height + (maxHeight - imageBitmap.height) / 2, ) canvas.drawBitmap(imageBitmap, imageBitmap.rect, upperPart, null) progressCallback?.invoke(98) val bottomPart = Rect( - if (!isLTR) 0 else width, + if (!isLTR) max(maxWidth - imageBitmap2.width, 0) else maxWidth + hingeGap, (maxHeight - imageBitmap2.height) / 2, - (if (!isLTR) 0 else width) + imageBitmap2.width, + (if (!isLTR) max(maxWidth - imageBitmap2.width, 0) else maxWidth + hingeGap) + imageBitmap2.width, imageBitmap2.height + (maxHeight - imageBitmap2.height) / 2, ) canvas.drawBitmap(imageBitmap2, imageBitmap2.rect, bottomPart, null)