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 5cb861a8..3874f56c 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
@@ -56,6 +56,7 @@ actual object PlayerSettingsStorage {
private const val nextEpisodeThresholdMinutesBeforeEndKey = "next_episode_threshold_minutes_before_end_v2"
private const val useLibassKey = "use_libass"
private const val libassRenderTypeKey = "libass_render_type"
+ private const val swipeGesturesEnabledKey = "swipe_gestures_enabled"
private val syncKeys = listOf(
showLoadingOverlayKey,
resizeModeKey,
@@ -92,6 +93,7 @@ actual object PlayerSettingsStorage {
nextEpisodeThresholdMinutesBeforeEndKey,
useLibassKey,
libassRenderTypeKey,
+ swipeGesturesEnabledKey,
)
private var preferences: SharedPreferences? = null
@@ -652,6 +654,23 @@ actual object PlayerSettingsStorage {
?.apply()
}
+ actual fun loadSwipeGesturesEnabled(): Boolean? =
+ preferences?.let { sharedPreferences ->
+ val key = ProfileScopedKey.of(swipeGesturesEnabledKey)
+ if (sharedPreferences.contains(key)) {
+ sharedPreferences.getBoolean(key, true)
+ } else {
+ null
+ }
+ }
+
+ actual fun saveSwipeGesturesEnabled(enabled: Boolean) {
+ preferences
+ ?.edit()
+ ?.putBoolean(ProfileScopedKey.of(swipeGesturesEnabledKey), enabled)
+ ?.apply()
+ }
+
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
loadShowLoadingOverlay()?.let { put(showLoadingOverlayKey, encodeSyncBoolean(it)) }
loadResizeMode()?.let { put(resizeModeKey, encodeSyncString(it)) }
@@ -688,6 +707,7 @@ actual object PlayerSettingsStorage {
loadNextEpisodeThresholdMinutesBeforeEnd()?.let { put(nextEpisodeThresholdMinutesBeforeEndKey, encodeSyncFloat(it)) }
loadUseLibass()?.let { put(useLibassKey, encodeSyncBoolean(it)) }
loadLibassRenderType()?.let { put(libassRenderTypeKey, encodeSyncString(it)) }
+ loadSwipeGesturesEnabled()?.let { put(swipeGesturesEnabledKey, encodeSyncBoolean(it)) }
}
actual fun replaceFromSyncPayload(payload: JsonObject) {
@@ -732,5 +752,6 @@ actual object PlayerSettingsStorage {
payload.decodeSyncFloat(nextEpisodeThresholdMinutesBeforeEndKey)?.let(::saveNextEpisodeThresholdMinutesBeforeEnd)
payload.decodeSyncBoolean(useLibassKey)?.let(::saveUseLibass)
payload.decodeSyncString(libassRenderTypeKey)?.let(::saveLibassRenderType)
+ payload.decodeSyncBoolean(swipeGesturesEnabledKey)?.let(::saveSwipeGesturesEnabled)
}
}
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index 3c21777b..2b23c1e8 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -732,6 +732,8 @@
Hold Speed
Hold To Speed
Long-press anywhere on the player surface to temporarily boost playback speed.
+ Swipe Gestures
+ Allow vertical swiping on the player surface to control volume and brightness.
Invalid regex pattern
Last Link Cache Duration
DV7 - HEVC Fallback
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreen.kt
index 279aae9f..729dcfb3 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreen.kt
@@ -1556,10 +1556,10 @@ fun PlayerScreen(
abs(totalDx) > viewConfiguration.touchSlop &&
abs(totalDx) > abs(totalDy)
val verticalDominant =
- !holdToSpeedActive &&
+ playerSettingsUiState.swipeGesturesEnabled &&
+ !holdToSpeedActive &&
abs(totalDy) > viewConfiguration.touchSlop &&
abs(totalDy) > abs(totalDx)
-
gestureMode = when {
horizontalDominant -> {
deactivateHoldToSpeedState.value()
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 15f4f4d7..b1295ed5 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
@@ -43,6 +43,7 @@ data class PlayerSettingsUiState(
val nextEpisodeThresholdMinutesBeforeEnd: Float = 2f,
val useLibass: Boolean = false,
val libassRenderType: String = "CUES",
+ val swipeGesturesEnabled: Boolean = true,
)
object PlayerSettingsRepository {
@@ -84,6 +85,7 @@ object PlayerSettingsRepository {
private var nextEpisodeThresholdMinutesBeforeEnd = 2f
private var useLibass = false
private var libassRenderType = "CUES"
+ private var swipeGesturesEnabled = true
fun ensureLoaded() {
if (hasLoaded) return
@@ -130,6 +132,7 @@ object PlayerSettingsRepository {
nextEpisodeThresholdMinutesBeforeEnd = 2f
useLibass = false
libassRenderType = "CUES"
+ swipeGesturesEnabled = true
publish()
}
@@ -204,6 +207,7 @@ object PlayerSettingsRepository {
nextEpisodeThresholdMinutesBeforeEnd = PlayerSettingsStorage.loadNextEpisodeThresholdMinutesBeforeEnd() ?: 2f
useLibass = PlayerSettingsStorage.loadUseLibass() ?: false
libassRenderType = PlayerSettingsStorage.loadLibassRenderType() ?: "CUES"
+ swipeGesturesEnabled = PlayerSettingsStorage.loadSwipeGesturesEnabled() ?: true
publish()
}
@@ -498,6 +502,14 @@ object PlayerSettingsRepository {
PlayerSettingsStorage.saveLibassRenderType(renderType)
}
+ fun setSwipeGesturesEnabled(enabled: Boolean) {
+ ensureLoaded()
+ if (swipeGesturesEnabled == enabled) return
+ swipeGesturesEnabled = enabled
+ publish()
+ PlayerSettingsStorage.saveSwipeGesturesEnabled(enabled)
+ }
+
private fun publish() {
_uiState.value = PlayerSettingsUiState(
showLoadingOverlay = showLoadingOverlay,
@@ -534,6 +546,7 @@ object PlayerSettingsRepository {
nextEpisodeThresholdMinutesBeforeEnd = nextEpisodeThresholdMinutesBeforeEnd,
useLibass = useLibass,
libassRenderType = libassRenderType,
+ swipeGesturesEnabled = swipeGesturesEnabled,
)
}
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 5c3b3756..6f982661 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
@@ -78,6 +78,8 @@ internal expect object PlayerSettingsStorage {
fun saveUseLibass(enabled: Boolean)
fun loadLibassRenderType(): String?
fun saveLibassRenderType(renderType: String)
+ fun loadSwipeGesturesEnabled(): Boolean?
+ fun saveSwipeGesturesEnabled(enabled: Boolean)
fun exportToSyncPayload(): JsonObject
fun replaceFromSyncPayload(payload: JsonObject)
}
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 042d592d..cc93b0fd 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
@@ -86,6 +86,7 @@ internal fun LazyListScope.playbackSettingsContent(
tunnelingEnabled: Boolean,
useLibass: Boolean,
libassRenderType: String,
+ swipeGesturesEnabled: Boolean,
) {
item {
PlaybackSettingsSection(
@@ -104,6 +105,7 @@ internal fun LazyListScope.playbackSettingsContent(
tunnelingEnabled = tunnelingEnabled,
useLibass = useLibass,
libassRenderType = libassRenderType,
+ swipeGesturesEnabled = swipeGesturesEnabled,
)
}
}
@@ -166,6 +168,7 @@ private fun PlaybackSettingsSection(
tunnelingEnabled: Boolean,
useLibass: Boolean,
libassRenderType: String,
+ swipeGesturesEnabled: Boolean,
) {
var showPreferredAudioDialog by remember { mutableStateOf(false) }
var showSecondaryAudioDialog by remember { mutableStateOf(false) }
@@ -262,6 +265,14 @@ private fun PlaybackSettingsSection(
onClick = { showHoldToSpeedValueDialog = true },
)
}
+ SettingsGroupDivider(isTablet = isTablet)
+ SettingsSwitchRow(
+ title = stringResource(Res.string.settings_playback_enable_swipe_gestures),
+ description = stringResource(Res.string.settings_playback_enable_swipe_gestures_description),
+ checked = swipeGesturesEnabled,
+ isTablet = isTablet,
+ onCheckedChange = PlayerSettingsRepository::setSwipeGesturesEnabled,
+ )
}
}
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt
index 519dc8d5..ce9b8c62 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt
@@ -226,6 +226,7 @@ fun SettingsScreen(
tunnelingEnabled = playerSettingsUiState.tunnelingEnabled,
useLibass = playerSettingsUiState.useLibass,
libassRenderType = playerSettingsUiState.libassRenderType,
+ swipeGesturesEnabled = playerSettingsUiState.swipeGesturesEnabled,
selectedTheme = selectedTheme,
onThemeSelected = ThemeSettingsRepository::setTheme,
amoledEnabled = amoledEnabled,
@@ -274,6 +275,7 @@ fun SettingsScreen(
tunnelingEnabled = playerSettingsUiState.tunnelingEnabled,
useLibass = playerSettingsUiState.useLibass,
libassRenderType = playerSettingsUiState.libassRenderType,
+ swipeGesturesEnabled = playerSettingsUiState.swipeGesturesEnabled,
selectedTheme = selectedTheme,
onThemeSelected = ThemeSettingsRepository::setTheme,
amoledEnabled = amoledEnabled,
@@ -332,6 +334,7 @@ private fun MobileSettingsScreen(
tunnelingEnabled: Boolean,
useLibass: Boolean,
libassRenderType: String,
+ swipeGesturesEnabled: Boolean,
selectedTheme: AppTheme,
onThemeSelected: (AppTheme) -> Unit,
amoledEnabled: Boolean,
@@ -493,6 +496,7 @@ private fun MobileSettingsScreen(
tunnelingEnabled = tunnelingEnabled,
useLibass = useLibass,
libassRenderType = libassRenderType,
+ swipeGesturesEnabled = swipeGesturesEnabled,
)
SettingsPage.Appearance -> appearanceSettingsContent(
isTablet = false,
@@ -639,6 +643,7 @@ private fun TabletSettingsScreen(
tunnelingEnabled: Boolean,
useLibass: Boolean,
libassRenderType: String,
+ swipeGesturesEnabled: Boolean,
selectedTheme: AppTheme,
onThemeSelected: (AppTheme) -> Unit,
amoledEnabled: Boolean,
@@ -859,6 +864,7 @@ private fun TabletSettingsScreen(
tunnelingEnabled = tunnelingEnabled,
useLibass = useLibass,
libassRenderType = libassRenderType,
+ swipeGesturesEnabled = swipeGesturesEnabled,
)
SettingsPage.Appearance -> appearanceSettingsContent(
isTablet = true,
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 0aedbb30..fe4c417d 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
@@ -54,6 +54,7 @@ actual object PlayerSettingsStorage {
private const val nextEpisodeThresholdMinutesBeforeEndKey = "next_episode_threshold_minutes_before_end_v2"
private const val useLibassKey = "use_libass"
private const val libassRenderTypeKey = "libass_render_type"
+ private const val swipeGesturesEnabledKey = "swipe_gestures_enabled"
private val syncKeys = listOf(
showLoadingOverlayKey,
resizeModeKey,
@@ -90,6 +91,7 @@ actual object PlayerSettingsStorage {
nextEpisodeThresholdMinutesBeforeEndKey,
useLibassKey,
libassRenderTypeKey,
+ swipeGesturesEnabledKey,
)
actual fun loadShowLoadingOverlay(): Boolean? {
@@ -102,6 +104,21 @@ actual object PlayerSettingsStorage {
}
}
+ actual fun loadSwipeGesturesEnabled(): Boolean? {
+ val defaults = NSUserDefaults.standardUserDefaults
+ val key = ProfileScopedKey.of(swipeGesturesEnabledKey)
+ return if (defaults.objectForKey(key) != null) {
+ defaults.boolForKey(key)
+ } else {
+ null
+ }
+ }
+
+ actual fun saveSwipeGesturesEnabled(enabled: Boolean) {
+ val defaults = NSUserDefaults.standardUserDefaults
+ defaults.setBool(enabled, ProfileScopedKey.of(swipeGesturesEnabledKey))
+ }
+
actual fun saveShowLoadingOverlay(enabled: Boolean) {
NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(showLoadingOverlayKey))
}
@@ -588,6 +605,7 @@ actual object PlayerSettingsStorage {
loadNextEpisodeThresholdMinutesBeforeEnd()?.let { put(nextEpisodeThresholdMinutesBeforeEndKey, encodeSyncFloat(it)) }
loadUseLibass()?.let { put(useLibassKey, encodeSyncBoolean(it)) }
loadLibassRenderType()?.let { put(libassRenderTypeKey, encodeSyncString(it)) }
+ loadSwipeGesturesEnabled()?.let { put(swipeGesturesEnabledKey, encodeSyncBoolean(it)) }
}
actual fun replaceFromSyncPayload(payload: JsonObject) {
@@ -631,5 +649,6 @@ actual object PlayerSettingsStorage {
payload.decodeSyncFloat(nextEpisodeThresholdMinutesBeforeEndKey)?.let(::saveNextEpisodeThresholdMinutesBeforeEnd)
payload.decodeSyncBoolean(useLibassKey)?.let(::saveUseLibass)
payload.decodeSyncString(libassRenderTypeKey)?.let(::saveLibassRenderType)
+ payload.decodeSyncBoolean(swipeGesturesEnabledKey)?.let(::saveSwipeGesturesEnabled)
}
}