Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion shared/src/commonMain/composeResources/values-en/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -427,8 +427,10 @@
<string name="settings_section_gestures">Gestures</string>
<string name="settings_gestures_summary">Double-tap, hold speed and swipe controls</string>
<string name="settings_long_press_speed">Hold for temporary speed</string>
<string name="settings_long_press_speed_subtitle">Hold the picture to speed up, drag to adjust or lock</string>
<string name="settings_long_press_speed_subtitle">Hold the picture to speed up</string>
<string name="settings_default_hold_speed">Default hold speed</string>
<string name="settings_long_press_speed_dial">Hold speed dial</string>
<string name="settings_long_press_speed_dial_subtitle">Show the speed dial while holding to drag or lock</string>
<string name="settings_swipe_seek">Swipe horizontally to seek</string>
<string name="settings_swipe_seek_subtitle">Drag left or right on the picture to scrub</string>
<string name="settings_swipe_brightness_volume">Swipe vertically for brightness and volume</string>
Expand Down
4 changes: 3 additions & 1 deletion shared/src/commonMain/composeResources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -510,8 +510,10 @@
<string name="settings_section_gestures">快捷手势</string>
<string name="settings_gestures_summary">双击进退、长按倍速与滑动调节</string>
<string name="settings_long_press_speed">长按倍速</string>
<string name="settings_long_press_speed_subtitle">按住画面临时加速播放,滑动可切换倍速并锁定</string>
<string name="settings_long_press_speed_subtitle">按住画面临时加速播放</string>
<string name="settings_default_hold_speed">默认长按倍速</string>
<string name="settings_long_press_speed_dial">长按倍速条</string>
<string name="settings_long_press_speed_dial_subtitle">长按时展开倍速选档条,滑动可切换倍速并锁定</string>
<string name="settings_swipe_seek">横向滑动调节进度</string>
<string name="settings_swipe_seek_subtitle">在画面上左右拖动跳转播放位置</string>
<string name="settings_swipe_brightness_volume">纵向滑动调节亮度与音量</string>
Expand Down
7 changes: 7 additions & 0 deletions shared/src/commonMain/kotlin/dev/bilby/data/SettingsStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ class SettingsStore(private val store: DataStore<Preferences>) {
playerPickUpdatesDefault = p[KEY_PLAYER_PICK_UPDATES_DEFAULT] ?: false,
doubleTapSeek = p[KEY_DOUBLE_TAP_SEEK] ?: true,
longPressSpeed = p[KEY_LONG_PRESS_SPEED] ?: true,
longPressSpeedDial = p[KEY_LONG_PRESS_SPEED_DIAL] ?: true,
defaultHoldSpeed = (p[KEY_DEFAULT_HOLD_SPEED] ?: p[KEY_LEGACY_HOLD_SPEED] ?: DEFAULT_HOLD_SPEED)
.takeIf { it in HOLD_SPEED_OPTIONS } ?: DEFAULT_HOLD_SPEED,
swipeSeek = p[KEY_SWIPE_SEEK] ?: true,
Expand Down Expand Up @@ -180,6 +181,10 @@ class SettingsStore(private val store: DataStore<Preferences>) {
store.edit { p -> p[KEY_LONG_PRESS_SPEED] = enabled }
}

suspend fun saveLongPressSpeedDial(enabled: Boolean) {
store.edit { p -> p[KEY_LONG_PRESS_SPEED_DIAL] = enabled }
}

suspend fun saveDefaultHoldSpeed(speed: Float) {
val valid = speed.takeIf { it in HOLD_SPEED_OPTIONS } ?: DEFAULT_HOLD_SPEED
store.edit { p -> p[KEY_DEFAULT_HOLD_SPEED] = valid }
Expand Down Expand Up @@ -538,6 +543,7 @@ class SettingsStore(private val store: DataStore<Preferences>) {

private val KEY_DOUBLE_TAP_SEEK = booleanPreferencesKey("player_double_tap_seek")
private val KEY_LONG_PRESS_SPEED = booleanPreferencesKey("player_long_press_speed")
private val KEY_LONG_PRESS_SPEED_DIAL = booleanPreferencesKey("player_long_press_speed_dial")
// 沿用 v0.18.1 已发布的键名,升级时不丢用户已选的默认长按倍速。
private val KEY_DEFAULT_HOLD_SPEED = floatPreferencesKey("player_fast_forward_speed")
private val KEY_LEGACY_HOLD_SPEED = floatPreferencesKey("player_default_hold_speed")
Expand Down Expand Up @@ -758,6 +764,7 @@ data class PlayerPrefs(
val playerPickUpdatesDefault: Boolean = false,
val doubleTapSeek: Boolean = true,
val longPressSpeed: Boolean = true,
val longPressSpeedDial: Boolean = true,
val defaultHoldSpeed: Float = SettingsStore.DEFAULT_HOLD_SPEED,
val swipeSeek: Boolean = true,
val swipeBrightnessVolume: Boolean = true,
Expand Down
2 changes: 2 additions & 0 deletions shared/src/commonMain/kotlin/dev/bilby/ui/BilbyApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,7 @@ private fun SettingsPageRoute(
state = state,
onDoubleTapSeekChange = vm::setDoubleTapSeek,
onLongPressSpeedChange = vm::setLongPressSpeed,
onLongPressSpeedDialChange = vm::setLongPressSpeedDial,
onDefaultHoldSpeedChange = vm::setDefaultHoldSpeed,
onSwipeSeekChange = vm::setSwipeSeek,
onSwipeBrightnessVolumeChange = vm::setSwipeBrightnessVolume,
Expand Down Expand Up @@ -3186,6 +3187,7 @@ private fun VideoPane(
doubleTapSeek = playerPrefs.doubleTapSeek,
brightnessAndVolume = playerPrefs.swipeBrightnessVolume,
fastForward = playerPrefs.longPressSpeed,
fastForwardDial = playerPrefs.longPressSpeedDial,
),
defaultHoldSpeed = playerPrefs.defaultHoldSpeed,
panelPrefs = panelPrefs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ data class PlayerGestureOptions(
val brightnessAndVolume: Boolean = true,
/** 长按选择临时倍速。 */
val fastForward: Boolean = true,
/** 长按时展开倍速选档条并允许滑动切换或锁定倍速;关闭后仅按默认倍速临时加速并在画面顶部显示提示。 */
val fastForwardDial: Boolean = true,
)

/**
Expand Down Expand Up @@ -932,7 +934,8 @@ fun PlayerShell(
.speedSelectionLongPress(
speedController,
enabled = gestures.fastForward && !locked && !pip,
forceHorizontalBelow = !showSpeedOverlay,
dialEnabled = gestures.fastForwardDial,
forceHorizontalBelow = !showSpeedOverlay && gestures.fastForwardDial,
onStart = {
controlsVisible = false
gesture = null
Expand Down Expand Up @@ -1249,10 +1252,11 @@ fun PlayerShell(
}
}

if (showSpeedOverlay && speedController != null && !pip) {
if ((showSpeedOverlay || !gestures.fastForwardDial) && speedController != null && !pip) {
SpeedSelectionOverlay(
controller = speedController,
isFullscreen = isFullscreen,
showDial = gestures.fastForwardDial,
modifier = Modifier.matchParentSize(),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ import androidx.compose.ui.platform.LocalWindowInfo
internal fun Modifier.speedSelectionLongPress(
controller: PlaybackSpeedController?,
enabled: Boolean,
dialEnabled: Boolean = true,
forceHorizontalBelow: Boolean = false,
onStart: () -> Unit,
): Modifier {
val currentOnStart by rememberUpdatedState(onStart)
val haptics = LocalHapticFeedback.current
val window = LocalWindowInfo.current.containerSize
val vertical = !forceHorizontalBelow && window.width > window.height
return pointerInput(controller, enabled, vertical, forceHorizontalBelow) {
return pointerInput(controller, enabled, dialEnabled, vertical, forceHorizontalBelow) {
if (controller == null || !enabled) return@pointerInput
var owned = false
var dockEnd = false
Expand All @@ -31,10 +32,10 @@ internal fun Modifier.speedSelectionLongPress(
try {
detectDragGesturesAfterLongPress(
onDragStart = { startOffset ->
dockEnd = vertical && startOffset.x < size.width / 2f
dockEnd = dialEnabled && vertical && startOffset.x < size.width / 2f
// 竖屏内嵌时选档块画在画面下方(forceHorizontalBelow),一律下滑锁定;
// 竖屏全屏时偏下长按把选档条放画面上沿(上滑锁定),偏上长按放画面下沿(下滑锁定)。
dockTop = !vertical && !forceHorizontalBelow && startOffset.y >= size.height / 2f
dockTop = dialEnabled && !vertical && !forceHorizontalBelow && startOffset.y >= size.height / 2f
controller.open(temporary = true, dockEnd = dockEnd, dockTop = dockTop)
owned = controller.beginDrag(vertical = vertical)
displacement = Offset.Zero
Expand All @@ -51,7 +52,7 @@ internal fun Modifier.speedSelectionLongPress(
},
) { change, amount ->
change.consume()
if (!owned) return@detectDragGesturesAfterLongPress
if (!owned || !dialEnabled) return@detectDragGesturesAfterLongPress
displacement += amount
val before = controller.selection
val acrossPx = when {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ private data class SpeedHintState(
private fun rememberSpeedHintState(
controller: PlaybackSpeedController,
forceHorizontalBelow: Boolean,
showDial: Boolean = true,
): SpeedHintState {
val selection = controller.selection
val window = LocalWindowInfo.current.containerSize
Expand All @@ -129,6 +130,7 @@ private fun rememberSpeedHintState(
val dockTop = !vertical && !forceHorizontalBelow && selection?.dockTop == true
val text = when {
locked != null -> stringResource(Res.string.player_speed_locked, formatSpeed(locked / 100f))
!showDial -> formatSpeed(selection?.speed ?: controller.speed)
releaseToLock -> stringResource(Res.string.player_speed_release_to_lock)
!vertical && dockTop -> stringResource(Res.string.player_speed_coarse_horizontal_top)
!vertical -> stringResource(Res.string.player_speed_coarse_horizontal)
Expand Down Expand Up @@ -185,6 +187,7 @@ private fun SpeedHintCapsule(
internal fun SpeedSelectionOverlay(
controller: PlaybackSpeedController,
isFullscreen: Boolean = false,
showDial: Boolean = true,
modifier: Modifier = Modifier,
) {
var shownSelection by remember { mutableStateOf<SpeedSelection?>(null) }
Expand All @@ -200,18 +203,18 @@ internal fun SpeedSelectionOverlay(
val activeOrLast = controller.selection ?: shownSelection
val dockEnd = vertical && activeOrLast?.dockEnd == true
val dockTop = !vertical && activeOrLast?.dockTop == true
val hintState = rememberSpeedHintState(controller, forceHorizontalBelow = false)
val hintState = rememberSpeedHintState(controller, forceHorizontalBelow = false, showDial = showDial)
val focus = remember { FocusRequester() }
BackHandler(enabled = controller.active) { controller.cancel() }
LaunchedEffect(controller.active) { if (controller.active) focus.requestFocus() }
BackHandler(enabled = controller.active && showDial) { controller.cancel() }
LaunchedEffect(controller.active, showDial) { if (controller.active && showDial) focus.requestFocus() }
PlayerTheme {
val motion = MaterialTheme.motionScheme
val spatialOffset = motion.fastSpatialSpec<IntOffset>()
val effects = motion.fastEffectsSpec<Float>()
Box(
modifier.fillMaxSize()
.onKeyEvent { event ->
if (event.type != KeyEventType.KeyDown || !controller.active) return@onKeyEvent false
if (event.type != KeyEventType.KeyDown || !controller.active || !showDial) return@onKeyEvent false
when (event.key) {
Key.Escape -> controller.cancel()
Key.Enter -> { controller.lock(); controller.finish() }
Expand All @@ -238,13 +241,13 @@ internal fun SpeedSelectionOverlay(
else -> return@onKeyEvent false
}
true
}.focusRequester(focus).focusable(),
}.then(if (showDial) Modifier.focusRequester(focus).focusable() else Modifier),
) {
if (controller.active) {
if (controller.active && showDial) {
Box(Modifier.fillMaxSize().pointerInput(controller) { detectTapGestures { controller.finish() } })
}
AnimatedVisibility(
visible = controller.active,
visible = controller.active && showDial,
enter = (when {
!vertical && dockTop -> slideInVertically(spatialOffset) { -it / 2 }
!vertical -> slideInVertically(spatialOffset) { it / 2 }
Expand Down Expand Up @@ -308,7 +311,7 @@ internal fun SpeedSelectionOverlay(
)
}
}
val hintAtBottom = !vertical && dockTop
val hintAtBottom = showDial && !vertical && dockTop
val hintInsetSides = WindowInsetsSides.Horizontal +
if (hintAtBottom) WindowInsetsSides.Bottom else WindowInsetsSides.Top
AnimatedVisibility(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ fun GestureSettingsPage(
state: SettingsUiState,
onDoubleTapSeekChange: (Boolean) -> Unit,
onLongPressSpeedChange: (Boolean) -> Unit,
onLongPressSpeedDialChange: (Boolean) -> Unit = {},
onDefaultHoldSpeedChange: (Float) -> Unit,
onSwipeSeekChange: (Boolean) -> Unit,
onSwipeBrightnessVolumeChange: (Boolean) -> Unit,
Expand Down Expand Up @@ -490,9 +491,21 @@ fun GestureSettingsPage(
if (holdSpeedOnScreen) {
row { position ->
AnimatedVisibility(visibleState = holdSpeedShown, enter = enter, exit = exit) {
ChoiceRow(
ToggleSettingRow(
position = position,
icon = Icons.Outlined.Tune,
title = stringResource(Res.string.settings_long_press_speed_dial),
subtitle = stringResource(Res.string.settings_long_press_speed_dial_subtitle),
checked = state.longPressSpeedDial,
onCheckedChange = onLongPressSpeedDialChange,
)
}
}
row { position ->
AnimatedVisibility(visibleState = holdSpeedShown, enter = enter, exit = exit) {
ChoiceRow(
position = position,
icon = Icons.Outlined.Speed,
title = stringResource(Res.string.settings_default_hold_speed),
options = SettingsStore.HOLD_SPEED_OPTIONS,
selected = state.defaultHoldSpeed,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ data class SettingsUiState(
val hardwareCodecIds: Set<Int> = emptySet(),
val doubleTapSeek: Boolean = true,
val longPressSpeed: Boolean = true,
val longPressSpeedDial: Boolean = true,
val defaultHoldSpeed: Float = SettingsStore.DEFAULT_HOLD_SPEED,
val swipeSeek: Boolean = true,
val swipeBrightnessVolume: Boolean = true,
Expand Down Expand Up @@ -185,6 +186,7 @@ class SettingsViewModel(
playerPickUpdatesDefault = prefs.playerPickUpdatesDefault,
doubleTapSeek = prefs.doubleTapSeek,
longPressSpeed = prefs.longPressSpeed,
longPressSpeedDial = prefs.longPressSpeedDial,
defaultHoldSpeed = prefs.defaultHoldSpeed,
swipeSeek = prefs.swipeSeek,
swipeBrightnessVolume = prefs.swipeBrightnessVolume,
Expand Down Expand Up @@ -314,6 +316,11 @@ class SettingsViewModel(
persist { settings.saveLongPressSpeed(value) }
}

fun setLongPressSpeedDial(value: Boolean) {
_state.update { it.copy(longPressSpeedDial = value) }
persist { settings.saveLongPressSpeedDial(value) }
}

fun setDefaultHoldSpeed(value: Float) {
_state.update { it.copy(defaultHoldSpeed = value) }
persist { settings.saveDefaultHoldSpeed(value) }
Expand Down
10 changes: 6 additions & 4 deletions shared/src/commonMain/kotlin/dev/bilby/ui/video/VideoScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1324,10 +1324,12 @@ fun VideoScreen(
.background(MaterialTheme.colorScheme.surface)
.windowInsetsPadding(WindowInsets.horizontalCutout),
)
BelowVideoSpeedOverlay(
controller = speedController,
modifier = Modifier.fillMaxSize(),
)
if (gestures.fastForwardDial) {
BelowVideoSpeedOverlay(
controller = speedController,
modifier = Modifier.fillMaxSize(),
)
}
}
}
}
Expand Down
Loading