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 e05257c7f..74d313473 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" @@ -88,6 +89,7 @@ actual object PlayerSettingsStorage { private const val iosGammaKey = "ios_gamma" private val syncKeys = listOf( showLoadingOverlayKey, + showParentalGuideKey, resizeModeKey, holdToSpeedEnabledKey, holdToSpeedValueKey, @@ -177,6 +179,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) @@ -1055,6 +1074,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)) } @@ -1129,6 +1149,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 a05e2bf91..77f6c9e63 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -966,6 +966,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 8f7be4f9a..516548999 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 @@ -173,6 +173,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 fa03c37a2..c0f4c4294 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, @@ -98,6 +99,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 @@ -168,6 +170,7 @@ object PlayerSettingsRepository { fun clearLocalState() { hasLoaded = false showLoadingOverlay = true + showParentalGuide = true resizeMode = PlayerResizeMode.Fit holdToSpeedEnabled = true holdToSpeedValue = 2f @@ -231,6 +234,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 @@ -370,6 +374,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 @@ -899,6 +911,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 3b7a3a98a..312a0764b 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 f95eeb503..46b733c52 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 @@ -334,6 +334,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 e15bc88f2..4eeb657ae 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" @@ -86,6 +87,7 @@ actual object PlayerSettingsStorage { private const val iosGammaKey = "ios_gamma" private val syncKeys = listOf( showLoadingOverlayKey, + showParentalGuideKey, resizeModeKey, holdToSpeedEnabledKey, holdToSpeedValueKey, @@ -186,6 +188,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) @@ -879,6 +895,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)) } @@ -952,6 +969,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)