From d79e8c63bb2c48dc2ac1692f1ab018e8d9b490e8 Mon Sep 17 00:00:00 2001 From: Jesse Bate Date: Mon, 9 Feb 2026 20:08:49 +1030 Subject: [PATCH 1/4] Remove restriction on playlist navigation using up/down nav buttons. --- .../playback/PlaybackPageContent.kt | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt index e9c307d19..c2243a40d 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt @@ -448,7 +448,6 @@ fun PlaybackPageContent( val nextWithUpDown = remember { playlistPager != null && - playlistPager.filter.dataType == DataType.MARKER && playlistPager.size > 1 && uiConfig.preferences.interfacePreferences.useUpDownPreviousNext } @@ -1033,10 +1032,40 @@ class PlaybackKeyHandler( private val controllerViewState: ControllerViewState, private val updateSkipIndicator: (Long) -> Unit, ) { + private var keyDownTime = 0L + private var keyDownKey: Key? = null + private var holdActionTriggered = false + private val holdThresholdMs = 500L // Time in milliseconds to detect a "hold" + fun onKeyEvent(it: KeyEvent): Boolean { var result = true if (!controlsEnabled) { result = false + } else if (it.type == KeyEventType.KeyDown) { + // Track key down events for hold detection + if (nextWithUpDown && (it.key == Key.DirectionUp || it.key == Key.DirectionDown)) { + if (keyDownKey == null) { + keyDownKey = it.key + keyDownTime = System.currentTimeMillis() + holdActionTriggered = false + } else if (keyDownKey == it.key && !holdActionTriggered) { + // Check if hold threshold is met + val holdDuration = System.currentTimeMillis() - keyDownTime + if (holdDuration >= holdThresholdMs) { + holdActionTriggered = true + if (!controllerViewState.controlsVisible) { + if (it.key == Key.DirectionUp) { + player.seekToPreviousMediaItem() + } else if (it.key == Key.DirectionDown) { + player.seekToNextMediaItem() + } + } + } + } + result = true + } else { + result = false + } } else if (it.type != KeyEventType.KeyUp) { result = false } else if (isDpad(it)) { @@ -1048,9 +1077,21 @@ class PlaybackKeyHandler( player.seekForward() updateSkipIndicator(player.seekForwardIncrement) } else if (nextWithUpDown && it.key == Key.DirectionUp) { - player.seekToPreviousMediaItem() + val wasHeld = keyDownKey == it.key && holdActionTriggered + keyDownKey = null + holdActionTriggered = false + if (!wasHeld) { + // Only show controls if it was a short press (not a hold) + controllerViewState.showControls() + } } else if (nextWithUpDown && it.key == Key.DirectionDown) { - player.seekToNextMediaItem() + val wasHeld = keyDownKey == it.key && holdActionTriggered + keyDownKey = null + holdActionTriggered = false + if (!wasHeld) { + // Only show controls if it was a short press (not a hold) + controllerViewState.showControls() + } } else { controllerViewState.showControls() } From 6a2a48971dc5800df4ba551f990dfe72e43e4da2 Mon Sep 17 00:00:00 2001 From: Jesse Bate Date: Mon, 9 Feb 2026 20:23:05 +1030 Subject: [PATCH 2/4] Update strings.xml so the new behaviour is documented in app settings. --- .../damontecres/stashapp/ui/components/prefs/StashPreference.kt | 2 +- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/prefs/StashPreference.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/prefs/StashPreference.kt index 0dca5d24a..c75e53992 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/prefs/StashPreference.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/prefs/StashPreference.kt @@ -502,7 +502,7 @@ sealed interface StashPreference { setter = { prefs, value -> prefs.updateInterfacePreferences { useUpDownPreviousNext = value } }, - summaryOn = R.string.enabled_for_markers, + summaryOn = R.string.enabled_for_scenes_and_markers, summaryOff = R.string.transcode_options_disabled, ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 05e7f0ee0..1deaa2ab1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -169,7 +169,7 @@ Use Up/Down for Previous/Next Turn captions on by default Whether to turn captions for the device\'s language on by default - Enabled for marker playlists + Enabled for scene and marker playlists Scroll to next on View All Automatically scroll down to the \'next\' results when clicking View All on main page Scroll to the top of page when pressing the back button From 166ab47b1e2e6dfd18bcfb8923fc2a0daf7dc7b2 Mon Sep 17 00:00:00 2001 From: Jesse Bate Date: Mon, 9 Feb 2026 20:47:48 +1030 Subject: [PATCH 3/4] Refactor key up event handling. --- .../ui/components/playback/PlaybackPageContent.kt | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt index c2243a40d..740d6493b 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt @@ -1076,20 +1076,12 @@ class PlaybackKeyHandler( } else if (skipWithLeftRight && it.key == Key.DirectionRight) { player.seekForward() updateSkipIndicator(player.seekForwardIncrement) - } else if (nextWithUpDown && it.key == Key.DirectionUp) { + } else if (nextWithUpDown && (it.key == Key.DirectionUp || it.key == Key.DirectionDown)) { val wasHeld = keyDownKey == it.key && holdActionTriggered keyDownKey = null holdActionTriggered = false if (!wasHeld) { - // Only show controls if it was a short press (not a hold) - controllerViewState.showControls() - } - } else if (nextWithUpDown && it.key == Key.DirectionDown) { - val wasHeld = keyDownKey == it.key && holdActionTriggered - keyDownKey = null - holdActionTriggered = false - if (!wasHeld) { - // Only show controls if it was a short press (not a hold) + // Only show player controls if it was a short press (not a hold) controllerViewState.showControls() } } else { From ee877441a173abe66b709e9bab655b81327cef54 Mon Sep 17 00:00:00 2001 From: Jesse Bate Date: Sun, 15 Mar 2026 01:59:31 +1030 Subject: [PATCH 4/4] Hold detection using repeatCount from the API event --- .../playback/PlaybackPageContent.kt | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt index c05c99e40..d8aa30145 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackPageContent.kt @@ -1110,33 +1110,27 @@ class PlaybackKeyHandler( private val controllerViewState: ControllerViewState, private val updateSkipIndicator: (Long) -> Unit, ) { - private var keyDownTime = 0L private var keyDownKey: Key? = null private var holdActionTriggered = false - private val holdThresholdMs = 500L // Time in milliseconds to detect a "hold" fun onKeyEvent(it: KeyEvent): Boolean { var result = true if (!controlsEnabled) { result = false } else if (it.type == KeyEventType.KeyDown) { - // Track key down events for hold detection if (nextWithUpDown && (it.key == Key.DirectionUp || it.key == Key.DirectionDown)) { + val repeatCount = it.nativeKeyEvent.repeatCount if (keyDownKey == null) { keyDownKey = it.key - keyDownTime = System.currentTimeMillis() holdActionTriggered = false - } else if (keyDownKey == it.key && !holdActionTriggered) { - // Check if hold threshold is met - val holdDuration = System.currentTimeMillis() - keyDownTime - if (holdDuration >= holdThresholdMs) { - holdActionTriggered = true - if (!controllerViewState.controlsVisible) { - if (it.key == Key.DirectionUp) { - player.seekToPreviousMediaItem() - } else if (it.key == Key.DirectionDown) { - player.seekToNextMediaItem() - } + } else if (keyDownKey == it.key && !holdActionTriggered && repeatCount >= 2) { + // Each repeat is roughly 250-300ms, so repeatCount==2 is ~500-600ms + holdActionTriggered = true + if (!controllerViewState.controlsVisible) { + if (it.key == Key.DirectionUp) { + player.seekToPreviousMediaItem() + } else if (it.key == Key.DirectionDown) { + player.seekToNextMediaItem() } } }