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..f4bcfb88 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,8 @@ 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 swipeToSeekEnabledKey = "swipe_to_seek_enabled"
+ private const val swipeToSeekSensitivityKey = "swipe_to_seek_sensitivity"
private val syncKeys = listOf(
showLoadingOverlayKey,
resizeModeKey,
@@ -92,6 +94,8 @@ actual object PlayerSettingsStorage {
nextEpisodeThresholdMinutesBeforeEndKey,
useLibassKey,
libassRenderTypeKey,
+ swipeToSeekEnabledKey,
+ swipeToSeekSensitivityKey,
)
private var preferences: SharedPreferences? = null
@@ -688,6 +692,8 @@ actual object PlayerSettingsStorage {
loadNextEpisodeThresholdMinutesBeforeEnd()?.let { put(nextEpisodeThresholdMinutesBeforeEndKey, encodeSyncFloat(it)) }
loadUseLibass()?.let { put(useLibassKey, encodeSyncBoolean(it)) }
loadLibassRenderType()?.let { put(libassRenderTypeKey, encodeSyncString(it)) }
+ loadSwipeToSeekEnabled()?.let { put(swipeToSeekEnabledKey, encodeSyncBoolean(it)) }
+ loadSwipeToSeekSensitivity()?.let { put(swipeToSeekSensitivityKey, encodeSyncString(it)) }
}
actual fun replaceFromSyncPayload(payload: JsonObject) {
@@ -732,5 +738,34 @@ actual object PlayerSettingsStorage {
payload.decodeSyncFloat(nextEpisodeThresholdMinutesBeforeEndKey)?.let(::saveNextEpisodeThresholdMinutesBeforeEnd)
payload.decodeSyncBoolean(useLibassKey)?.let(::saveUseLibass)
payload.decodeSyncString(libassRenderTypeKey)?.let(::saveLibassRenderType)
+ payload.decodeSyncBoolean(swipeToSeekEnabledKey)?.let(::saveSwipeToSeekEnabled)
+ payload.decodeSyncString(swipeToSeekSensitivityKey)?.let(::saveSwipeToSeekSensitivity)
+ }
+
+ actual fun loadSwipeToSeekEnabled(): Boolean? =
+ preferences?.let { sharedPreferences ->
+ val key = ProfileScopedKey.of(swipeToSeekEnabledKey)
+ if (sharedPreferences.contains(key)) {
+ sharedPreferences.getBoolean(key, true)
+ } else {
+ null
+ }
+ }
+
+ actual fun saveSwipeToSeekEnabled(enabled: Boolean) {
+ preferences
+ ?.edit()
+ ?.putBoolean(ProfileScopedKey.of(swipeToSeekEnabledKey), enabled)
+ ?.apply()
+ }
+
+ actual fun loadSwipeToSeekSensitivity(): String? =
+ preferences?.getString(ProfileScopedKey.of(swipeToSeekSensitivityKey), null)
+
+ actual fun saveSwipeToSeekSensitivity(sensitivity: String) {
+ preferences
+ ?.edit()
+ ?.putString(ProfileScopedKey.of(swipeToSeekSensitivityKey), sensitivity)
+ ?.apply()
}
}
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index c32e32d0..4d8d2d36 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -718,6 +718,13 @@
Matches against stream name/title/description/addon/url. Example: 4K|2160p|Remux
Regex Pattern
No pattern set. Example: 4K|2160p|Remux
+ Swipe to Seek
+ Swipe horizontally on the player surface to seek forward or backward.
+ Swipe to Seek Sensitivity
+ Adjust the drag distance required to trigger a seek.
+ Low
+ Medium
+ High
Any 1080p+
AVC / x264
BluRay Quality
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 50c727c1..98f645d8 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
@@ -1480,8 +1480,10 @@ fun PlayerScreen(
if (gestureMode == null) {
val holdToSpeedActive = isHoldToSpeedGestureActiveState.value
val horizontalDominant =
- !holdToSpeedActive &&
- abs(totalDx) > viewConfiguration.touchSlop &&
+ playerSettingsUiState.swipeToSeekEnabled &&
+ !holdToSpeedActive &&
+ abs(totalDx) > viewConfiguration.touchSlop *
+ playerSettingsUiState.swipeToSeekSensitivity.triggerMultiplier &&
abs(totalDx) > abs(totalDy)
val verticalDominant =
!holdToSpeedActive &&
@@ -1518,7 +1520,7 @@ fun PlayerScreen(
else -> 60f
}
val previewOffsetMs =
- ((totalDx / width) * sensitivitySeconds * 1000f).roundToLong()
+ ((totalDx / width) * sensitivitySeconds * playerSettingsUiState.swipeToSeekSensitivity.speedMultiplier * 1000f).roundToLong()
val unclampedPreviewMs = horizontalSeekBaselineMs + previewOffsetMs
horizontalSeekPreviewMs = currentDurationMsState.value
.takeIf { it > 0L }
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..80475af9 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
@@ -8,6 +8,24 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+enum class SwipeToSeekSensitivity {
+ LOW, MEDIUM, HIGH;
+
+ val triggerMultiplier: Float
+ get() = when (this) {
+ LOW -> 4.0f
+ MEDIUM -> 1.0f
+ HIGH -> 0.5f
+ }
+
+ val speedMultiplier: Float
+ get() = when (this) {
+ LOW -> 0.5f
+ MEDIUM -> 1.0f
+ HIGH -> 2.0f
+ }
+}
+
data class PlayerSettingsUiState(
val showLoadingOverlay: Boolean = true,
val resizeMode: PlayerResizeMode = PlayerResizeMode.Fit,
@@ -31,6 +49,8 @@ data class PlayerSettingsUiState(
val streamAutoPlaySelectedPlugins: Set = emptySet(),
val streamAutoPlayRegex: String = "",
val streamAutoPlayTimeoutSeconds: Int = 3,
+ val swipeToSeekEnabled: Boolean = true,
+ val swipeToSeekSensitivity: SwipeToSeekSensitivity = SwipeToSeekSensitivity.MEDIUM,
val skipIntroEnabled: Boolean = true,
val animeSkipEnabled: Boolean = false,
val animeSkipClientId: String = "",
@@ -84,6 +104,8 @@ object PlayerSettingsRepository {
private var nextEpisodeThresholdMinutesBeforeEnd = 2f
private var useLibass = false
private var libassRenderType = "CUES"
+ private var swipeToSeekEnabled = true
+ private var swipeToSeekSensitivity = SwipeToSeekSensitivity.MEDIUM
fun ensureLoaded() {
if (hasLoaded) return
@@ -204,6 +226,12 @@ object PlayerSettingsRepository {
nextEpisodeThresholdMinutesBeforeEnd = PlayerSettingsStorage.loadNextEpisodeThresholdMinutesBeforeEnd() ?: 2f
useLibass = PlayerSettingsStorage.loadUseLibass() ?: false
libassRenderType = PlayerSettingsStorage.loadLibassRenderType() ?: "CUES"
+ swipeToSeekEnabled = PlayerSettingsStorage.loadSwipeToSeekEnabled() ?: true
+ swipeToSeekSensitivity = try {
+ SwipeToSeekSensitivity.valueOf(PlayerSettingsStorage.loadSwipeToSeekSensitivity() ?: "MEDIUM")
+ } catch (e: Exception) {
+ SwipeToSeekSensitivity.MEDIUM
+ }
publish()
}
@@ -498,6 +526,22 @@ object PlayerSettingsRepository {
PlayerSettingsStorage.saveLibassRenderType(renderType)
}
+ fun setSwipeToSeekEnabled(enabled: Boolean) {
+ ensureLoaded()
+ if (swipeToSeekEnabled == enabled) return
+ swipeToSeekEnabled = enabled
+ publish()
+ PlayerSettingsStorage.saveSwipeToSeekEnabled(enabled)
+ }
+
+ fun setSwipeToSeekSensitivity(sensitivity: SwipeToSeekSensitivity) {
+ ensureLoaded()
+ if (swipeToSeekSensitivity == sensitivity) return
+ swipeToSeekSensitivity = sensitivity
+ publish()
+ PlayerSettingsStorage.saveSwipeToSeekSensitivity(sensitivity.name)
+ }
+
private fun publish() {
_uiState.value = PlayerSettingsUiState(
showLoadingOverlay = showLoadingOverlay,
@@ -534,6 +578,8 @@ object PlayerSettingsRepository {
nextEpisodeThresholdMinutesBeforeEnd = nextEpisodeThresholdMinutesBeforeEnd,
useLibass = useLibass,
libassRenderType = libassRenderType,
+ swipeToSeekEnabled = swipeToSeekEnabled,
+ swipeToSeekSensitivity = swipeToSeekSensitivity,
)
}
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..6def2f56 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,10 @@ internal expect object PlayerSettingsStorage {
fun saveUseLibass(enabled: Boolean)
fun loadLibassRenderType(): String?
fun saveLibassRenderType(renderType: String)
+ fun loadSwipeToSeekEnabled(): Boolean?
+ fun saveSwipeToSeekEnabled(enabled: Boolean)
+ fun loadSwipeToSeekSensitivity(): String?
+ fun saveSwipeToSeekSensitivity(sensitivity: String)
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..1c42ed75 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
@@ -56,6 +56,7 @@ import com.nuvio.app.features.player.AvailableLanguageOptions
import com.nuvio.app.features.player.ExternalPlayerApp
import com.nuvio.app.features.player.ExternalPlayerPlatform
import com.nuvio.app.features.player.PlayerSettingsRepository
+import com.nuvio.app.features.player.SwipeToSeekSensitivity
import com.nuvio.app.features.player.SubtitleLanguageOption
import com.nuvio.app.features.player.formatPlaybackSpeedLabel
import com.nuvio.app.features.player.languageLabelForCode
@@ -86,6 +87,8 @@ internal fun LazyListScope.playbackSettingsContent(
tunnelingEnabled: Boolean,
useLibass: Boolean,
libassRenderType: String,
+ swipeToSeekEnabled: Boolean,
+ swipeToSeekSensitivity: SwipeToSeekSensitivity,
) {
item {
PlaybackSettingsSection(
@@ -104,6 +107,8 @@ internal fun LazyListScope.playbackSettingsContent(
tunnelingEnabled = tunnelingEnabled,
useLibass = useLibass,
libassRenderType = libassRenderType,
+ swipeToSeekEnabled = swipeToSeekEnabled,
+ swipeToSeekSensitivity = swipeToSeekSensitivity,
)
}
}
@@ -166,6 +171,8 @@ private fun PlaybackSettingsSection(
tunnelingEnabled: Boolean,
useLibass: Boolean,
libassRenderType: String,
+ swipeToSeekEnabled: Boolean,
+ swipeToSeekSensitivity: SwipeToSeekSensitivity,
) {
var showPreferredAudioDialog by remember { mutableStateOf(false) }
var showSecondaryAudioDialog by remember { mutableStateOf(false) }
@@ -181,6 +188,7 @@ private fun PlaybackSettingsSection(
var showAutoPlayAddonSelectionDialog by remember { mutableStateOf(false) }
var showAutoPlayPluginSelectionDialog by remember { mutableStateOf(false) }
var showAutoPlayRegexDialog by remember { mutableStateOf(false) }
+ var showSwipeSensitivityDialog by remember { mutableStateOf(false) }
val pluginsEnabled = AppFeaturePolicy.pluginsEnabled
val autoPlayPlayerSettings by PlayerSettingsRepository.uiState.collectAsStateWithLifecycle()
val availableExternalPlayers = ExternalPlayerPlatform.availablePlayers()
@@ -262,6 +270,27 @@ private fun PlaybackSettingsSection(
onClick = { showHoldToSpeedValueDialog = true },
)
}
+ SettingsGroupDivider(isTablet = isTablet)
+ SettingsSwitchRow(
+ title = stringResource(Res.string.settings_playback_swipe_to_seek),
+ description = stringResource(Res.string.settings_playback_swipe_to_seek_description),
+ checked = swipeToSeekEnabled,
+ isTablet = isTablet,
+ onCheckedChange = PlayerSettingsRepository::setSwipeToSeekEnabled,
+ )
+ if (swipeToSeekEnabled) {
+ SettingsGroupDivider(isTablet = isTablet)
+ SettingsNavigationRow(
+ title = stringResource(Res.string.settings_playback_swipe_to_seek_sensitivity),
+ description = when (swipeToSeekSensitivity) {
+ SwipeToSeekSensitivity.LOW -> stringResource(Res.string.settings_playback_swipe_to_seek_sensitivity_low)
+ SwipeToSeekSensitivity.MEDIUM -> stringResource(Res.string.settings_playback_swipe_to_seek_sensitivity_medium)
+ SwipeToSeekSensitivity.HIGH -> stringResource(Res.string.settings_playback_swipe_to_seek_sensitivity_high)
+ },
+ isTablet = isTablet,
+ onClick = { showSwipeSensitivityDialog = true },
+ )
+ }
}
}
@@ -865,6 +894,14 @@ private fun PlaybackSettingsSection(
)
}
+ if (showSwipeSensitivityDialog) {
+ SwipeToSeekSensitivityDialog(
+ selected = swipeToSeekSensitivity,
+ onSelect = PlayerSettingsRepository::setSwipeToSeekSensitivity,
+ onDismiss = { showSwipeSensitivityDialog = false },
+ )
+ }
+
if (showAutoPlayModeDialog) {
StreamAutoPlayModeDialog(
selectedMode = autoPlayPlayerSettings.streamAutoPlayMode,
@@ -2324,3 +2361,89 @@ private fun libassRenderTypeRes(renderType: String): StringResource = when (rend
@Composable
private fun libassRenderTypeLabel(renderType: String): String = stringResource(libassRenderTypeRes(renderType))
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+private fun SwipeToSeekSensitivityDialog(
+ selected: SwipeToSeekSensitivity,
+ onSelect: (SwipeToSeekSensitivity) -> Unit,
+ onDismiss: () -> Unit,
+) {
+ val options = SwipeToSeekSensitivity.entries
+
+ BasicAlertDialog(onDismissRequest = onDismiss) {
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(20.dp),
+ color = MaterialTheme.colorScheme.surface,
+ ) {
+ Column(
+ modifier = Modifier.padding(20.dp),
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ Text(
+ text = stringResource(Res.string.settings_playback_swipe_to_seek_sensitivity),
+ style = MaterialTheme.typography.titleLarge,
+ color = MaterialTheme.colorScheme.onSurface,
+ fontWeight = FontWeight.SemiBold,
+ )
+ Text(
+ text = stringResource(Res.string.settings_playback_swipe_to_seek_sensitivity_description),
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ ) {
+ options.forEach { option ->
+ val isSelected = option == selected
+ val containerColor = if (isSelected) {
+ MaterialTheme.colorScheme.primary.copy(alpha = 0.14f)
+ } else {
+ MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f)
+ }
+
+ Surface(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable {
+ onSelect(option)
+ onDismiss()
+ },
+ shape = RoundedCornerShape(12.dp),
+ color = containerColor,
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 14.dp, vertical = 12.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Text(
+ text = when (option) {
+ SwipeToSeekSensitivity.LOW -> stringResource(Res.string.settings_playback_swipe_to_seek_sensitivity_low)
+ SwipeToSeekSensitivity.MEDIUM -> stringResource(Res.string.settings_playback_swipe_to_seek_sensitivity_medium)
+ SwipeToSeekSensitivity.HIGH -> stringResource(Res.string.settings_playback_swipe_to_seek_sensitivity_high)
+ else -> stringResource(Res.string.settings_playback_swipe_to_seek_sensitivity_medium)
+ },
+ style = MaterialTheme.typography.bodyLarge,
+ color = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier.weight(1f),
+ )
+ if (isSelected) {
+ Icon(
+ imageVector = Icons.Rounded.Check,
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.primary,
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
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 4cd95d64..d6d2a640 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
@@ -66,6 +66,9 @@ import com.nuvio.app.features.mdblist.MdbListSettingsRepository
import com.nuvio.app.features.notifications.EpisodeReleaseNotificationsRepository
import com.nuvio.app.features.notifications.EpisodeReleaseNotificationsUiState
import com.nuvio.app.features.player.PlayerSettingsRepository
+import com.nuvio.app.features.player.PlayerResizeMode
+import com.nuvio.app.features.player.SwipeToSeekSensitivity
+import com.nuvio.app.features.player.skip.NextEpisodeThresholdMode
import com.nuvio.app.features.trakt.TraktAuthUiState
import com.nuvio.app.features.trakt.TraktAuthRepository
import com.nuvio.app.features.trakt.TraktCommentsSettings
@@ -220,6 +223,8 @@ fun SettingsScreen(
tunnelingEnabled = playerSettingsUiState.tunnelingEnabled,
useLibass = playerSettingsUiState.useLibass,
libassRenderType = playerSettingsUiState.libassRenderType,
+ swipeToSeekEnabled = playerSettingsUiState.swipeToSeekEnabled,
+ swipeToSeekSensitivity = playerSettingsUiState.swipeToSeekSensitivity,
selectedTheme = selectedTheme,
onThemeSelected = ThemeSettingsRepository::setTheme,
amoledEnabled = amoledEnabled,
@@ -267,6 +272,8 @@ fun SettingsScreen(
tunnelingEnabled = playerSettingsUiState.tunnelingEnabled,
useLibass = playerSettingsUiState.useLibass,
libassRenderType = playerSettingsUiState.libassRenderType,
+ swipeToSeekEnabled = playerSettingsUiState.swipeToSeekEnabled,
+ swipeToSeekSensitivity = playerSettingsUiState.swipeToSeekSensitivity,
selectedTheme = selectedTheme,
onThemeSelected = ThemeSettingsRepository::setTheme,
amoledEnabled = amoledEnabled,
@@ -324,6 +331,8 @@ private fun MobileSettingsScreen(
tunnelingEnabled: Boolean,
useLibass: Boolean,
libassRenderType: String,
+ swipeToSeekEnabled: Boolean,
+ swipeToSeekSensitivity: SwipeToSeekSensitivity,
selectedTheme: AppTheme,
onThemeSelected: (AppTheme) -> Unit,
amoledEnabled: Boolean,
@@ -484,6 +493,8 @@ private fun MobileSettingsScreen(
tunnelingEnabled = tunnelingEnabled,
useLibass = useLibass,
libassRenderType = libassRenderType,
+ swipeToSeekEnabled = swipeToSeekEnabled,
+ swipeToSeekSensitivity = swipeToSeekSensitivity,
)
SettingsPage.Appearance -> appearanceSettingsContent(
isTablet = false,
@@ -625,6 +636,8 @@ private fun TabletSettingsScreen(
tunnelingEnabled: Boolean,
useLibass: Boolean,
libassRenderType: String,
+ swipeToSeekEnabled: Boolean,
+ swipeToSeekSensitivity: SwipeToSeekSensitivity,
selectedTheme: AppTheme,
onThemeSelected: (AppTheme) -> Unit,
amoledEnabled: Boolean,
@@ -844,6 +857,8 @@ private fun TabletSettingsScreen(
tunnelingEnabled = tunnelingEnabled,
useLibass = useLibass,
libassRenderType = libassRenderType,
+ swipeToSeekEnabled = swipeToSeekEnabled,
+ swipeToSeekSensitivity = swipeToSeekSensitivity,
)
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..5a5f5a08 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,8 @@ 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 swipeToSeekEnabledKey = "swipe_to_seek_enabled"
+ private const val swipeToSeekSensitivityKey = "swipe_to_seek_sensitivity"
private val syncKeys = listOf(
showLoadingOverlayKey,
resizeModeKey,
@@ -90,6 +92,8 @@ actual object PlayerSettingsStorage {
nextEpisodeThresholdMinutesBeforeEndKey,
useLibassKey,
libassRenderTypeKey,
+ swipeToSeekEnabledKey,
+ swipeToSeekSensitivityKey,
)
actual fun loadShowLoadingOverlay(): Boolean? {
@@ -588,6 +592,8 @@ actual object PlayerSettingsStorage {
loadNextEpisodeThresholdMinutesBeforeEnd()?.let { put(nextEpisodeThresholdMinutesBeforeEndKey, encodeSyncFloat(it)) }
loadUseLibass()?.let { put(useLibassKey, encodeSyncBoolean(it)) }
loadLibassRenderType()?.let { put(libassRenderTypeKey, encodeSyncString(it)) }
+ loadSwipeToSeekEnabled()?.let { put(swipeToSeekEnabledKey, encodeSyncBoolean(it)) }
+ loadSwipeToSeekSensitivity()?.let { put(swipeToSeekSensitivityKey, encodeSyncString(it)) }
}
actual fun replaceFromSyncPayload(payload: JsonObject) {
@@ -631,5 +637,29 @@ actual object PlayerSettingsStorage {
payload.decodeSyncFloat(nextEpisodeThresholdMinutesBeforeEndKey)?.let(::saveNextEpisodeThresholdMinutesBeforeEnd)
payload.decodeSyncBoolean(useLibassKey)?.let(::saveUseLibass)
payload.decodeSyncString(libassRenderTypeKey)?.let(::saveLibassRenderType)
+ payload.decodeSyncBoolean(swipeToSeekEnabledKey)?.let(::saveSwipeToSeekEnabled)
+ payload.decodeSyncString(swipeToSeekSensitivityKey)?.let(::saveSwipeToSeekSensitivity)
+ }
+
+ actual fun loadSwipeToSeekEnabled(): Boolean? {
+ val defaults = NSUserDefaults.standardUserDefaults
+ val key = ProfileScopedKey.of(swipeToSeekEnabledKey)
+ return if (defaults.objectForKey(key) != null) {
+ defaults.boolForKey(key)
+ } else {
+ null
+ }
+ }
+
+ actual fun saveSwipeToSeekEnabled(enabled: Boolean) {
+ val defaults = NSUserDefaults.standardUserDefaults
+ defaults.setBool(enabled, ProfileScopedKey.of(swipeToSeekEnabledKey))
+ }
+
+ actual fun loadSwipeToSeekSensitivity(): String? =
+ NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(swipeToSeekSensitivityKey))
+
+ actual fun saveSwipeToSeekSensitivity(sensitivity: String) {
+ NSUserDefaults.standardUserDefaults.setObject(sensitivity, ProfileScopedKey.of(swipeToSeekSensitivityKey))
}
}