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)