From bdc05843b92a94e4667a53219c2eed31986c938b Mon Sep 17 00:00:00 2001
From: siriusvoid <271671657+siriusvoid@users.noreply.github.com>
Date: Wed, 3 Jun 2026 20:57:31 +0000
Subject: [PATCH] feat: add "Content Warnings" toggle from tv version
---
.../player/PlayerSettingsStorage.android.kt | 21 +++++++++++++++++++
.../composeResources/values/strings.xml | 2 ++
.../PlayerScreenRuntimePlaybackActions.kt | 1 +
.../player/PlayerSettingsRepository.kt | 13 ++++++++++++
.../features/player/PlayerSettingsStorage.kt | 2 ++
.../features/settings/PlaybackSettingsPage.kt | 8 +++++++
.../player/PlayerSettingsStorage.ios.kt | 18 ++++++++++++++++
7 files changed, 65 insertions(+)
diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.android.kt
index 2a98502da..9067d15c9 100644
--- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.android.kt
+++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.android.kt
@@ -20,6 +20,7 @@ import kotlinx.serialization.json.put
actual object PlayerSettingsStorage {
private const val preferencesName = "nuvio_player_settings"
private const val showLoadingOverlayKey = "show_loading_overlay"
+ private const val showParentalGuideKey = "show_parental_guide"
private const val resizeModeKey = "resize_mode"
private const val holdToSpeedEnabledKey = "hold_to_speed_enabled"
private const val holdToSpeedValueKey = "hold_to_speed_value"
@@ -82,6 +83,7 @@ actual object PlayerSettingsStorage {
private const val iosGammaKey = "ios_gamma"
private val syncKeys = listOf(
showLoadingOverlayKey,
+ showParentalGuideKey,
resizeModeKey,
holdToSpeedEnabledKey,
holdToSpeedValueKey,
@@ -165,6 +167,23 @@ actual object PlayerSettingsStorage {
?.apply()
}
+ actual fun loadShowParentalGuide(): Boolean? =
+ preferences?.let { sharedPreferences ->
+ val key = ProfileScopedKey.of(showParentalGuideKey)
+ if (sharedPreferences.contains(key)) {
+ sharedPreferences.getBoolean(key, true)
+ } else {
+ null
+ }
+ }
+
+ actual fun saveShowParentalGuide(enabled: Boolean) {
+ preferences
+ ?.edit()
+ ?.putBoolean(ProfileScopedKey.of(showParentalGuideKey), enabled)
+ ?.apply()
+ }
+
actual fun loadResizeMode(): String? =
preferences?.getString(ProfileScopedKey.of(resizeModeKey), null)
@@ -955,6 +974,7 @@ actual object PlayerSettingsStorage {
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
loadShowLoadingOverlay()?.let { put(showLoadingOverlayKey, encodeSyncBoolean(it)) }
+ loadShowParentalGuide()?.let { put(showParentalGuideKey, encodeSyncBoolean(it)) }
loadResizeMode()?.let { put(resizeModeKey, encodeSyncString(it)) }
loadHoldToSpeedEnabled()?.let { put(holdToSpeedEnabledKey, encodeSyncBoolean(it)) }
loadHoldToSpeedValue()?.let { put(holdToSpeedValueKey, encodeSyncFloat(it)) }
@@ -1021,6 +1041,7 @@ actual object PlayerSettingsStorage {
}?.apply()
payload.decodeSyncBoolean(showLoadingOverlayKey)?.let(::saveShowLoadingOverlay)
+ payload.decodeSyncBoolean(showParentalGuideKey)?.let(::saveShowParentalGuide)
payload.decodeSyncString(resizeModeKey)?.let(::saveResizeMode)
payload.decodeSyncBoolean(holdToSpeedEnabledKey)?.let(::saveHoldToSpeedEnabled)
payload.decodeSyncFloat(holdToSpeedValueKey)?.let(::saveHoldToSpeedValue)
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index 582b500a1..53d6e03d0 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -901,6 +901,8 @@
%1$d selected
Loading Overlay
Show loading screen until first video frame appears.
+ Content Warnings
+ Show parental guidance overlay when playback starts.
Background Color
Bold
Use a heavier subtitle font weight.
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimePlaybackActions.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimePlaybackActions.kt
index 22828532e..3215fac34 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimePlaybackActions.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimePlaybackActions.kt
@@ -146,6 +146,7 @@ internal fun PlayerScreenRuntime.emitStopScrobbleForCurrentProgress() {
}
internal fun PlayerScreenRuntime.tryShowParentalGuide() {
+ if (!playerSettingsUiState.showParentalGuide) return
if (!parentalGuideHasShown && parentalWarnings.isNotEmpty() && !playbackStartedForParentalGuide) {
playbackStartedForParentalGuide = true
controlsVisible = true
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsRepository.kt
index a4c94820c..8450c8fc0 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsRepository.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsRepository.kt
@@ -33,6 +33,7 @@ fun snapToAllowedTimeout(value: Int): Int {
data class PlayerSettingsUiState(
val showLoadingOverlay: Boolean = true,
+ val showParentalGuide: Boolean = true,
val resizeMode: PlayerResizeMode = PlayerResizeMode.Fit,
val holdToSpeedEnabled: Boolean = true,
val holdToSpeedValue: Float = 2f,
@@ -92,6 +93,7 @@ object PlayerSettingsRepository {
private var hasLoaded = false
private var showLoadingOverlay = true
+ private var showParentalGuide = true
private var resizeMode = PlayerResizeMode.Fit
private var holdToSpeedEnabled = true
private var holdToSpeedValue = 2f
@@ -156,6 +158,7 @@ object PlayerSettingsRepository {
fun clearLocalState() {
hasLoaded = false
showLoadingOverlay = true
+ showParentalGuide = true
resizeMode = PlayerResizeMode.Fit
holdToSpeedEnabled = true
holdToSpeedValue = 2f
@@ -213,6 +216,7 @@ object PlayerSettingsRepository {
private fun loadFromDisk() {
hasLoaded = true
showLoadingOverlay = PlayerSettingsStorage.loadShowLoadingOverlay() ?: true
+ showParentalGuide = PlayerSettingsStorage.loadShowParentalGuide() ?: true
resizeMode = PlayerSettingsStorage.loadResizeMode()
?.let { runCatching { PlayerResizeMode.valueOf(it) }.getOrNull() }
?: PlayerResizeMode.Fit
@@ -344,6 +348,14 @@ object PlayerSettingsRepository {
PlayerSettingsStorage.saveShowLoadingOverlay(enabled)
}
+ fun setShowParentalGuide(enabled: Boolean) {
+ ensureLoaded()
+ if (showParentalGuide == enabled) return
+ showParentalGuide = enabled
+ publish()
+ PlayerSettingsStorage.saveShowParentalGuide(enabled)
+ }
+
fun setResizeMode(mode: PlayerResizeMode) {
ensureLoaded()
if (resizeMode == mode) return
@@ -825,6 +837,7 @@ object PlayerSettingsRepository {
private fun publish() {
_uiState.value = PlayerSettingsUiState(
showLoadingOverlay = showLoadingOverlay,
+ showParentalGuide = showParentalGuide,
resizeMode = resizeMode,
holdToSpeedEnabled = holdToSpeedEnabled,
holdToSpeedValue = holdToSpeedValue,
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.kt
index 0196d338b..71b8eb2cf 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.kt
@@ -5,6 +5,8 @@ import kotlinx.serialization.json.JsonObject
internal expect object PlayerSettingsStorage {
fun loadShowLoadingOverlay(): Boolean?
fun saveShowLoadingOverlay(enabled: Boolean)
+ fun loadShowParentalGuide(): Boolean?
+ fun saveShowParentalGuide(enabled: Boolean)
fun loadResizeMode(): String?
fun saveResizeMode(mode: String)
fun loadHoldToSpeedEnabled(): Boolean?
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt
index ed5ce9da9..ed2cd4d80 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt
@@ -315,6 +315,14 @@ private fun PlaybackSettingsSection(
onCheckedChange = PlayerSettingsRepository::setShowLoadingOverlay,
)
SettingsGroupDivider(isTablet = isTablet)
+ SettingsSwitchRow(
+ title = stringResource(Res.string.settings_playback_parental_guide),
+ description = stringResource(Res.string.settings_playback_parental_guide_description),
+ checked = autoPlayPlayerSettings.showParentalGuide,
+ isTablet = isTablet,
+ onCheckedChange = PlayerSettingsRepository::setShowParentalGuide,
+ )
+ SettingsGroupDivider(isTablet = isTablet)
// Player preference picker: Internal / External
SettingsNavigationRow(
title = stringResource(Res.string.settings_playback_player_preference),
diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.ios.kt
index 6da10affb..f9db4c08d 100644
--- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.ios.kt
+++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.ios.kt
@@ -18,6 +18,7 @@ import platform.Foundation.NSUserDefaults
actual object PlayerSettingsStorage {
private const val showLoadingOverlayKey = "show_loading_overlay"
+ private const val showParentalGuideKey = "show_parental_guide"
private const val resizeModeKey = "resize_mode"
private const val holdToSpeedEnabledKey = "hold_to_speed_enabled"
private const val holdToSpeedValueKey = "hold_to_speed_value"
@@ -80,6 +81,7 @@ actual object PlayerSettingsStorage {
private const val iosGammaKey = "ios_gamma"
private val syncKeys = listOf(
showLoadingOverlayKey,
+ showParentalGuideKey,
resizeModeKey,
holdToSpeedEnabledKey,
holdToSpeedValueKey,
@@ -174,6 +176,20 @@ actual object PlayerSettingsStorage {
NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(showLoadingOverlayKey))
}
+ actual fun loadShowParentalGuide(): Boolean? {
+ val defaults = NSUserDefaults.standardUserDefaults
+ val key = ProfileScopedKey.of(showParentalGuideKey)
+ return if (defaults.objectForKey(key) != null) {
+ defaults.boolForKey(key)
+ } else {
+ null
+ }
+ }
+
+ actual fun saveShowParentalGuide(enabled: Boolean) {
+ NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(showParentalGuideKey))
+ }
+
actual fun loadResizeMode(): String? {
val defaults = NSUserDefaults.standardUserDefaults
val key = ProfileScopedKey.of(resizeModeKey)
@@ -805,6 +821,7 @@ actual object PlayerSettingsStorage {
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
loadShowLoadingOverlay()?.let { put(showLoadingOverlayKey, encodeSyncBoolean(it)) }
+ loadShowParentalGuide()?.let { put(showParentalGuideKey, encodeSyncBoolean(it)) }
loadResizeMode()?.let { put(resizeModeKey, encodeSyncString(it)) }
loadHoldToSpeedEnabled()?.let { put(holdToSpeedEnabledKey, encodeSyncBoolean(it)) }
loadHoldToSpeedValue()?.let { put(holdToSpeedValueKey, encodeSyncFloat(it)) }
@@ -871,6 +888,7 @@ actual object PlayerSettingsStorage {
}
payload.decodeSyncBoolean(showLoadingOverlayKey)?.let(::saveShowLoadingOverlay)
+ payload.decodeSyncBoolean(showParentalGuideKey)?.let(::saveShowParentalGuide)
payload.decodeSyncString(resizeModeKey)?.let(::saveResizeMode)
payload.decodeSyncBoolean(holdToSpeedEnabledKey)?.let(::saveHoldToSpeedEnabled)
payload.decodeSyncFloat(holdToSpeedValueKey)?.let(::saveHoldToSpeedValue)