Skip to content

Commit c71cf23

Browse files
committed
introduce Persistent Playback Speed option
When set, any changes to the *current* playback speed are also applied to the *default* value, allowing them to persist through app restarts.
1 parent f0515aa commit c71cf23

9 files changed

Lines changed: 71 additions & 9 deletions

File tree

core/model/src/main/java/dev/anilbeesetti/nextplayer/core/model/PlayerPreferences.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ data class PlayerPreferences(
1212
val playerScreenOrientation: ScreenOrientation = ScreenOrientation.VIDEO_ORIENTATION,
1313
val playerVideoZoom: VideoContentScale = VideoContentScale.BEST_FIT,
1414
val defaultPlaybackSpeed: Float = 1.0f,
15+
val persistentPlaybackSpeed: Boolean = false,
1516
val autoplay: Boolean = true,
1617
val autoPip: Boolean = true,
1718
val autoBackgroundPlay: Boolean = false,

core/ui/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
<string name="player_description">Player appearance, playback controls</string>
3636
<string name="player_name">Player</string>
3737
<string name="quick_settings">Quick Settings</string>
38+
<string name="persistent_playback_speed">Persistent Playback Speed</string>
39+
<string name="persistent_playback_speed_description">Changing playback speed changes the default with it.</string>
3840
<string name="autoplay_settings">Autoplay</string>
3941
<string name="autoplay_settings_description">Automatically play the next video in the current folder</string>
4042
<string name="pip_settings">Picture in Picture Mode</string>

feature/player/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ dependencies {
4141
implementation(project(":core:domain"))
4242
implementation(project(":core:model"))
4343
implementation(project(":core:ui"))
44+
implementation(project(":feature:settings"))
4445

4546
implementation(libs.androidx.core.ktx)
4647
implementation(libs.androidx.appcompat)

feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/MediaPlayerScreen.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ fun MediaPlayerScreen(
349349

350350
OverlayShowView(
351351
player = player,
352+
preferencesRepository = viewModel.preferencesRepository,
352353
overlayView = overlayView,
353354
videoContentScale = videoZoomAndContentScaleState.videoContentScale,
354355
onDismiss = { overlayView = null },

feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerViewModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import kotlinx.coroutines.launch
2222
@HiltViewModel
2323
class PlayerViewModel @Inject constructor(
2424
private val mediaRepository: MediaRepository,
25-
private val preferencesRepository: PreferencesRepository,
25+
val preferencesRepository: PreferencesRepository,
2626
private val getSortedPlaylistUseCase: GetSortedPlaylistUseCase,
2727
) : ViewModel() {
2828

feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/ui/OverlayShowView.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import androidx.compose.foundation.layout.BoxScope
66
import androidx.compose.runtime.Composable
77
import androidx.compose.ui.Modifier
88
import androidx.media3.common.Player
9+
import dev.anilbeesetti.nextplayer.core.data.repository.PreferencesRepository
910
import dev.anilbeesetti.nextplayer.core.model.VideoContentScale
1011
import dev.anilbeesetti.nextplayer.feature.player.extensions.noRippleClickable
1112

1213
@Composable
1314
fun BoxScope.OverlayShowView(
1415
modifier: Modifier = Modifier,
1516
player: Player,
17+
preferencesRepository: PreferencesRepository,
1618
overlayView: OverlayView?,
1719
videoContentScale: VideoContentScale,
1820
onDismiss: () -> Unit = {},
@@ -47,6 +49,7 @@ fun BoxScope.OverlayShowView(
4749
PlaybackSpeedSelectorView(
4850
show = overlayView == OverlayView.PLAYBACK_SPEED,
4951
player = player,
52+
preferencesRepository = preferencesRepository,
5053
)
5154

5255
VideoContentScaleSelectorView(

feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/ui/PlaybackSpeedSelectorView.kt

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,20 @@ import androidx.compose.ui.unit.dp
3434
import androidx.media3.common.Player
3535
import androidx.media3.common.util.UnstableApi
3636
import dev.anilbeesetti.nextplayer.core.common.extensions.round
37+
import dev.anilbeesetti.nextplayer.core.data.repository.PreferencesRepository
3738
import dev.anilbeesetti.nextplayer.core.ui.R
3839
import dev.anilbeesetti.nextplayer.core.ui.components.NextSwitch
3940
import dev.anilbeesetti.nextplayer.feature.player.state.rememberPlaybackParametersState
41+
import dev.anilbeesetti.nextplayer.settings.screens.player.PlayerPreferencesUiEvent
42+
import dev.anilbeesetti.nextplayer.settings.screens.player.PlayerPreferencesViewModel
4043

4144
@OptIn(UnstableApi::class)
4245
@Composable
4346
fun BoxScope.PlaybackSpeedSelectorView(
4447
modifier: Modifier = Modifier,
4548
show: Boolean,
4649
player: Player,
50+
preferencesRepository: PreferencesRepository,
4751
) {
4852
val hapticFeedback = LocalHapticFeedback.current
4953
val playbackParametersState = rememberPlaybackParametersState(player)
@@ -66,6 +70,11 @@ fun BoxScope.PlaybackSpeedSelectorView(
6670
val newSpeed =
6771
(playbackParametersState.speed - stepSize).coerceAtLeast(minValue)
6872
playbackParametersState.setPlaybackSpeed(newSpeed)
73+
74+
if (preferencesRepository.playerPreferences.value.persistentPlaybackSpeed) {
75+
val viewModel = PlayerPreferencesViewModel(preferencesRepository)
76+
viewModel.onEvent(PlayerPreferencesUiEvent.UpdateDefaultPlaybackSpeed(newSpeed))
77+
}
6978
},
7079
) {
7180
Icon(
@@ -85,6 +94,11 @@ fun BoxScope.PlaybackSpeedSelectorView(
8594
onClick = {
8695
val newSpeed = (playbackParametersState.speed + stepSize).coerceAtMost(maxValue)
8796
playbackParametersState.setPlaybackSpeed(newSpeed)
97+
98+
if (preferencesRepository.playerPreferences.value.persistentPlaybackSpeed) {
99+
val viewModel = PlayerPreferencesViewModel(preferencesRepository)
100+
viewModel.onEvent(PlayerPreferencesUiEvent.UpdateDefaultPlaybackSpeed(newSpeed))
101+
}
88102
},
89103
) {
90104
Icon(
@@ -103,10 +117,22 @@ fun BoxScope.PlaybackSpeedSelectorView(
103117
onValueChange = {
104118
hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove)
105119
playbackParametersState.setPlaybackSpeed(it)
120+
121+
if (preferencesRepository.playerPreferences.value.persistentPlaybackSpeed) {
122+
val viewModel = PlayerPreferencesViewModel(preferencesRepository)
123+
viewModel.onEvent(PlayerPreferencesUiEvent.UpdateDefaultPlaybackSpeed(it))
124+
}
106125
},
107126
modifier = Modifier.weight(1f),
108127
)
109-
IconButton(onClick = { playbackParametersState.setPlaybackSpeed(1f) }) {
128+
IconButton(onClick = {
129+
playbackParametersState.setPlaybackSpeed(1f)
130+
131+
if (preferencesRepository.playerPreferences.value.persistentPlaybackSpeed) {
132+
val viewModel = PlayerPreferencesViewModel(preferencesRepository)
133+
viewModel.onEvent(PlayerPreferencesUiEvent.UpdateDefaultPlaybackSpeed(1f))
134+
}
135+
}) {
110136
Icon(
111137
painter = painterResource(R.drawable.ic_reset),
112138
contentDescription = null,
@@ -129,7 +155,14 @@ fun BoxScope.PlaybackSpeedSelectorView(
129155
color = LocalContentColor.current,
130156
shape = CircleShape,
131157
)
132-
.clickable { playbackParametersState.setPlaybackSpeed(speed) }
158+
.clickable {
159+
playbackParametersState.setPlaybackSpeed(speed)
160+
161+
if (preferencesRepository.playerPreferences.value.persistentPlaybackSpeed) {
162+
val viewModel = PlayerPreferencesViewModel(preferencesRepository)
163+
viewModel.onEvent(PlayerPreferencesUiEvent.UpdateDefaultPlaybackSpeed(speed))
164+
}
165+
}
133166
.padding(
134167
horizontal = 8.dp,
135168
vertical = 8.dp,

feature/settings/src/main/java/dev/anilbeesetti/nextplayer/settings/screens/player/PlayerPreferencesScreen.kt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,17 @@ private fun PlayerPreferencesContent(
267267
}
268268
},
269269
)
270+
PreferenceSwitch(
271+
title = stringResource(id = R.string.persistent_playback_speed),
272+
description = stringResource(
273+
id = R.string.persistent_playback_speed_description,
274+
),
275+
icon = NextIcons.Speed,
276+
isChecked = uiState.preferences.persistentPlaybackSpeed,
277+
onClick = { onEvent(PlayerPreferencesUiEvent.TogglePersistentPlaybackSpeed) },
278+
index = 2,
279+
count = totalRows,
280+
)
270281
PreferenceSwitch(
271282
title = stringResource(id = R.string.autoplay_settings),
272283
description = stringResource(
@@ -275,7 +286,7 @@ private fun PlayerPreferencesContent(
275286
icon = NextIcons.Player,
276287
isChecked = uiState.preferences.autoplay,
277288
onClick = { onEvent(PlayerPreferencesUiEvent.ToggleAutoplay) },
278-
index = 2,
289+
index = 3,
279290
count = totalRows,
280291
)
281292
PreferenceSwitch(
@@ -286,7 +297,7 @@ private fun PlayerPreferencesContent(
286297
icon = NextIcons.Pip,
287298
isChecked = uiState.preferences.autoPip,
288299
onClick = { onEvent(PlayerPreferencesUiEvent.ToggleAutoPip) },
289-
index = 3,
300+
index = 4,
290301
count = totalRows,
291302
)
292303
PreferenceSwitch(
@@ -297,7 +308,7 @@ private fun PlayerPreferencesContent(
297308
icon = NextIcons.Headset,
298309
isChecked = uiState.preferences.autoBackgroundPlay,
299310
onClick = { onEvent(PlayerPreferencesUiEvent.ToggleAutoBackgroundPlay) },
300-
index = 4,
311+
index = 5,
301312
count = totalRows,
302313
)
303314
PreferenceSwitch(
@@ -308,7 +319,7 @@ private fun PlayerPreferencesContent(
308319
icon = NextIcons.Brightness,
309320
isChecked = uiState.preferences.rememberPlayerBrightness,
310321
onClick = { onEvent(PlayerPreferencesUiEvent.ToggleRememberBrightnessLevel) },
311-
index = 5,
322+
index = 6,
312323
count = totalRows,
313324
)
314325
PreferenceSwitch(
@@ -317,7 +328,7 @@ private fun PlayerPreferencesContent(
317328
icon = NextIcons.Selection,
318329
isChecked = uiState.preferences.rememberSelections,
319330
onClick = { onEvent(PlayerPreferencesUiEvent.ToggleRememberSelections) },
320-
index = 6,
331+
index = 7,
321332
count = totalRows,
322333
)
323334
ClickablePreferenceItem(
@@ -327,7 +338,7 @@ private fun PlayerPreferencesContent(
327338
onClick = {
328339
onEvent(PlayerPreferencesUiEvent.ShowDialog(PlayerPreferenceDialog.PlayerScreenOrientationDialog))
329340
},
330-
index = 7,
341+
index = 8,
331342
count = totalRows,
332343
)
333344
}

feature/settings/src/main/java/dev/anilbeesetti/nextplayer/settings/screens/player/PlayerPreferencesViewModel.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class PlayerPreferencesViewModel @Inject constructor(
5757
is PlayerPreferencesUiEvent.UpdatePreferredPlayerOrientation -> updatePreferredPlayerOrientation(event.value)
5858
is PlayerPreferencesUiEvent.UpdatePreferredControlButtonsPosition -> updatePreferredControlButtonsPosition(event.value)
5959
is PlayerPreferencesUiEvent.UpdateDefaultPlaybackSpeed -> updateDefaultPlaybackSpeed(event.value)
60+
PlayerPreferencesUiEvent.TogglePersistentPlaybackSpeed -> togglePersistentPlaybackSpeed()
6061
is PlayerPreferencesUiEvent.UpdateLongPressControlsSpeed -> updateLongPressControlsSpeed(event.value)
6162
is PlayerPreferencesUiEvent.UpdateControlAutoHideTimeout -> updateControlAutoHideTimeout(event.value)
6263
is PlayerPreferencesUiEvent.UpdateSeekIncrement -> updateSeekIncrement(event.value)
@@ -215,6 +216,14 @@ class PlayerPreferencesViewModel @Inject constructor(
215216
}
216217
}
217218

219+
private fun togglePersistentPlaybackSpeed() {
220+
viewModelScope.launch {
221+
preferencesRepository.updatePlayerPreferences {
222+
it.copy(persistentPlaybackSpeed = !it.persistentPlaybackSpeed)
223+
}
224+
}
225+
}
226+
218227
private fun updateLongPressControlsSpeed(value: Float) {
219228
viewModelScope.launch {
220229
preferencesRepository.updatePlayerPreferences { it.copy(longPressControlsSpeed = value) }
@@ -274,6 +283,7 @@ sealed interface PlayerPreferencesUiEvent {
274283
data class UpdateDoubleTapGesture(val gesture: DoubleTapGesture) : PlayerPreferencesUiEvent
275284
data object ToggleUseLongPressControls : PlayerPreferencesUiEvent
276285
data object ToggleDoubleTapGesture : PlayerPreferencesUiEvent
286+
data object TogglePersistentPlaybackSpeed : PlayerPreferencesUiEvent
277287
data object ToggleAutoplay : PlayerPreferencesUiEvent
278288
data object ToggleAutoPip : PlayerPreferencesUiEvent
279289
data object ToggleAutoBackgroundPlay : PlayerPreferencesUiEvent

0 commit comments

Comments
 (0)