mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-06 00:58:55 +01:00
Handle reader cutout setting with Insets to support Android 15+ (#2640)
This commit is contained in:
@@ -19,7 +19,6 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
|
||||
|
||||
### Changed
|
||||
- Increased default concurrent page downloads to 5 ([@AntsyLich](https://github.com/AntsyLich)) ([#2637](https://github.com/mihonapp/mihon/pull/2637))
|
||||
- Hide "Show content in cutout area" reader setting on Android 15+ as it's not supported ([@AntsyLich](https://github.com/AntsyLich)) ([#1908](https://github.com/mihonapp/mihon/pull/1908))
|
||||
|
||||
### Improved
|
||||
- Spoofing of `X-Requested-With` header to support newer WebView versions ([@Guzmazow](https://github.com/Guzmazow)) ([#2491](https://github.com/mihonapp/mihon/pull/2491))
|
||||
|
||||
@@ -101,7 +101,7 @@ object SettingsReaderScreen : SearchableSettings {
|
||||
title = stringResource(MR.strings.pref_fullscreen),
|
||||
),
|
||||
Preference.PreferenceItem.SwitchPreference(
|
||||
preference = readerPreferences.cutoutShort(),
|
||||
preference = readerPreferences.drawUnderCutout(),
|
||||
title = stringResource(MR.strings.pref_cutout_short),
|
||||
enabled = LocalView.current.hasDisplayCutout() && fullscreen,
|
||||
),
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package eu.kanade.presentation.reader.settings
|
||||
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||
import eu.kanade.tachiyomi.util.system.hasDisplayCutout
|
||||
@@ -67,10 +67,10 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
|
||||
)
|
||||
|
||||
val isFullscreen by screenModel.preferences.fullscreen().collectAsState()
|
||||
if (LocalView.current.hasDisplayCutout() && isFullscreen) {
|
||||
if (LocalActivity.current?.hasDisplayCutout() == true && isFullscreen) {
|
||||
CheckboxItem(
|
||||
label = stringResource(MR.strings.pref_cutout_short),
|
||||
pref = screenModel.preferences.cutoutShort(),
|
||||
pref = screenModel.preferences.drawUnderCutout(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import android.view.View
|
||||
import android.view.View.LAYER_TYPE_HARDWARE
|
||||
import android.view.WindowManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -42,7 +41,6 @@ import androidx.core.graphics.Insets
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.transition.doOnEnd
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@@ -79,18 +77,17 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.system.hasDisplayCutout
|
||||
import eu.kanade.tachiyomi.util.system.isNightMode
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.view.setComposeContent
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -159,7 +156,7 @@ class ReaderActivity : BaseActivity() {
|
||||
overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit)
|
||||
}
|
||||
|
||||
enableEdgeToEdge(navigationBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT))
|
||||
enableEdgeToEdge()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.isNavigationBarContrastEnforced = false
|
||||
}
|
||||
@@ -252,7 +249,6 @@ class ReaderActivity : BaseActivity() {
|
||||
private fun ReaderActivityBinding.setComposeOverlay(): Unit = composeOverlay.setComposeContent {
|
||||
val state by viewModel.state.collectAsState()
|
||||
val showPageNumber by readerPreferences.showPageNumber().collectAsState()
|
||||
val isFullscreen by readerPreferences.fullscreen().collectAsState()
|
||||
val settingsScreenModel = remember {
|
||||
ReaderSettingsScreenModel(
|
||||
readerState = viewModel.state,
|
||||
@@ -268,7 +264,7 @@ class ReaderActivity : BaseActivity() {
|
||||
totalPages = state.totalPages,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.then(if (isFullscreen) Modifier else Modifier.navigationBarsPadding()),
|
||||
.navigationBarsPadding(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -537,7 +533,7 @@ class ReaderActivity : BaseActivity() {
|
||||
binding.viewerContainer.removeAllViews()
|
||||
}
|
||||
viewModel.onViewerLoaded(newViewer)
|
||||
updateViewerInset(readerPreferences.fullscreen().get())
|
||||
updateViewerInset(readerPreferences.fullscreen().get(), readerPreferences.drawUnderCutout().get())
|
||||
binding.viewerContainer.addView(newViewer.getView())
|
||||
|
||||
if (readerPreferences.showReadingMode().get()) {
|
||||
@@ -780,22 +776,28 @@ class ReaderActivity : BaseActivity() {
|
||||
/**
|
||||
* Updates viewer inset depending on fullscreen reader preferences.
|
||||
*/
|
||||
private fun updateViewerInset(fullscreen: Boolean) {
|
||||
private fun updateViewerInset(fullscreen: Boolean, drawUnderCutout: Boolean) {
|
||||
val view = viewModel.state.value.viewer?.getView() ?: return
|
||||
|
||||
view.applyInsetsPadding(ViewCompat.getRootWindowInsets(view), fullscreen)
|
||||
view.applyInsetsPadding(ViewCompat.getRootWindowInsets(view), fullscreen, drawUnderCutout)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(view) { view, windowInsets ->
|
||||
view.applyInsetsPadding(windowInsets, fullscreen)
|
||||
view.applyInsetsPadding(windowInsets, fullscreen, drawUnderCutout)
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
||||
private fun View.applyInsetsPadding(windowInsets: WindowInsetsCompat?, fullscreen: Boolean) {
|
||||
val insets = if (!fullscreen) {
|
||||
windowInsets?.getInsets(WindowInsetsCompat.Type.systemBars()) ?: Insets.NONE
|
||||
} else {
|
||||
Insets.NONE
|
||||
private fun View.applyInsetsPadding(
|
||||
windowInsets: WindowInsetsCompat?,
|
||||
fullscreen: Boolean,
|
||||
drawUnderCutout: Boolean,
|
||||
) {
|
||||
val insets = when {
|
||||
!fullscreen -> windowInsets?.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
!drawUnderCutout -> windowInsets?.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
else -> null
|
||||
}
|
||||
?: Insets.NONE
|
||||
|
||||
setPadding(insets.left, insets.top, insets.right, insets.bottom)
|
||||
}
|
||||
|
||||
@@ -851,10 +853,6 @@ class ReaderActivity : BaseActivity() {
|
||||
.onEach { setDisplayProfile(it) }
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
readerPreferences.cutoutShort().changes()
|
||||
.onEach(::setCutoutShort)
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
readerPreferences.keepScreenOn().changes()
|
||||
.onEach(::setKeepScreenOn)
|
||||
.launchIn(lifecycleScope)
|
||||
@@ -863,14 +861,21 @@ class ReaderActivity : BaseActivity() {
|
||||
.onEach(::setCustomBrightness)
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
merge(readerPreferences.grayscale().changes(), readerPreferences.invertedColors().changes())
|
||||
.onEach { setLayerPaint(readerPreferences.grayscale().get(), readerPreferences.invertedColors().get()) }
|
||||
combine(
|
||||
readerPreferences.grayscale().changes(),
|
||||
readerPreferences.invertedColors().changes(),
|
||||
) { grayscale, invertedColors -> grayscale to invertedColors }
|
||||
.onEach { (grayscale, invertedColors) ->
|
||||
setLayerPaint(grayscale, invertedColors)
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
readerPreferences.fullscreen().changes()
|
||||
.onEach {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, !it)
|
||||
updateViewerInset(it)
|
||||
combine(
|
||||
readerPreferences.fullscreen().changes(),
|
||||
readerPreferences.drawUnderCutout().changes(),
|
||||
) { fullscreen, drawUnderCutout -> fullscreen to drawUnderCutout }
|
||||
.onEach { (fullscreen, drawUnderCutout) ->
|
||||
updateViewerInset(fullscreen, drawUnderCutout)
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
||||
@@ -905,15 +910,6 @@ class ReaderActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setCutoutShort(enabled: Boolean) {
|
||||
if (!window.decorView.hasDisplayCutout()) return
|
||||
|
||||
window.attributes.layoutInDisplayCutoutMode = when (enabled) {
|
||||
true -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
|
||||
false -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the keep screen on mode according to [enabled].
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,7 @@ class ReaderPreferences(
|
||||
|
||||
fun fullscreen() = preferenceStore.getBoolean("fullscreen", true)
|
||||
|
||||
fun cutoutShort() = preferenceStore.getBoolean("cutout_short", true)
|
||||
fun drawUnderCutout() = preferenceStore.getBoolean("cutout_short", true)
|
||||
|
||||
fun keepScreenOn() = preferenceStore.getBoolean("pref_keep_screen_on_key", false)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
@@ -57,11 +58,19 @@ fun Context.isNightMode(): Boolean {
|
||||
/**
|
||||
* Checks whether if the device has a display cutout (i.e. notch, camera cutout, etc.).
|
||||
*
|
||||
* Only relevant from Android 9 to Android 14.
|
||||
* Only works on Android 9+.
|
||||
*/
|
||||
fun Activity.hasDisplayCutout(): Boolean {
|
||||
return window.decorView.hasDisplayCutout()
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether if the device has a display cutout (i.e. notch, camera cutout, etc.).
|
||||
*
|
||||
* Only works on Android 9+.
|
||||
*/
|
||||
fun View.hasDisplayCutout(): Boolean {
|
||||
return Build.VERSION.SDK_INT in Build.VERSION_CODES.P..Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
|
||||
rootWindowInsets?.displayCutout != null
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && rootWindowInsets?.displayCutout != null
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user