diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java
index 919fc5170..1c74e521f 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java
@@ -578,9 +578,12 @@ public class Settings extends BaseSettings {
// PreferenceScreen: Swipe controls
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", TRUE, true);
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", TRUE, true);
+
public static final BooleanSetting SWIPE_SPEED = new BooleanSetting("revanced_swipe_speed", TRUE, true);
public static final BooleanSetting SWIPE_SEEK = new BooleanSetting("revanced_swipe_seek", TRUE, true);
- public static final BooleanSetting SWIPE_SWITCH_SPEED_AND_SEEK = new BooleanSetting("revanced_swipe_switch_speed_and_seek", FALSE, true);
+ public static final BooleanSetting SWIPE_SWITCH_SPEED_AND_SEEK = new BooleanSetting("revanced_swipe_switch_speed_and_seek", FALSE, true, parentsAny(SWIPE_SPEED, SWIPE_SEEK));
+ public static final LongSetting SWIPE_SPEED_AND_SEEK_DELAY = new LongSetting("revanced_swipe_speed_and_seek_delay", 50L, true, parentsAny(SWIPE_SPEED, SWIPE_SEEK));
+
public static final BooleanSetting SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS = new BooleanSetting("revanced_swipe_lowest_value_enable_auto_brightness", TRUE, true,
parent(SWIPE_BRIGHTNESS));
public static final BooleanSetting SWIPE_PRESS_TO_ENGAGE = new BooleanSetting("revanced_swipe_press_to_engage", FALSE, true,
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/swipecontrols/controller/gesture/ClassicSwipeController.kt b/extensions/shared/src/main/java/app/revanced/extension/youtube/swipecontrols/controller/gesture/ClassicSwipeController.kt
index aedff61c9..8d8793197 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/swipecontrols/controller/gesture/ClassicSwipeController.kt
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/swipecontrols/controller/gesture/ClassicSwipeController.kt
@@ -1,6 +1,9 @@
package app.revanced.extension.youtube.swipecontrols.controller.gesture
+import android.os.Handler
+import android.os.Looper
import android.view.MotionEvent
+import app.revanced.extension.youtube.settings.Settings
import app.revanced.extension.youtube.shared.PlayerControlsVisibilityObserver
import app.revanced.extension.youtube.shared.PlayerControlsVisibilityObserverImpl
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider
@@ -15,6 +18,7 @@ import app.revanced.extension.youtube.swipecontrols.misc.toPoint
*
* @param controller reference to the main swipe controller
*/
+@Suppress("DEPRECATED_SMARTCAST_ON_DELEGATED_PROPERTY")
class ClassicSwipeController(
private val controller: SwipeControlsHostActivity,
private val config: SwipeControlsConfigurationProvider,
@@ -26,6 +30,11 @@ class ClassicSwipeController(
*/
private var lastOnDownEvent: MotionEvent? = null
+ private val handler = Handler(Looper.getMainLooper())
+ private var horizontalSwipeRunnable: Runnable? = null
+ private var isHorizontalSwipeConfirmed = false
+ private val horizontalSwipeDelayMs = Settings.SWIPE_SPEED_AND_SEEK_DELAY.get()
+
override val shouldForceInterceptEvents: Boolean
get() = currentSwipe == SwipeDetector.SwipeDirection.VERTICAL
@@ -56,6 +65,10 @@ class ClassicSwipeController(
// ignore gestures with more than one pointer
// when such a gesture is detected, dispatch the first event of the gesture to downstream
if (motionEvent.pointerCount > 1) {
+ // This is a multitouch gesture (like pinch-to-zoom), so cancel any pending swipe action.
+ cancelHorizontalSwipeRunnable()
+ isHorizontalSwipeConfirmed = false
+
lastOnDownEvent?.let {
controller.dispatchDownstreamTouchEvent(it)
it.recycle()
@@ -69,6 +82,9 @@ class ClassicSwipeController(
}
override fun onDown(motionEvent: MotionEvent): Boolean {
+ cancelHorizontalSwipeRunnable()
+ isHorizontalSwipeConfirmed = false
+
// save the event for later
lastOnDownEvent?.recycle()
lastOnDownEvent = MotionEvent.obtain(motionEvent)
@@ -77,6 +93,12 @@ class ClassicSwipeController(
return isInSwipeZone(motionEvent) || isInHorizontalSwipeZone(motionEvent)
}
+ override fun onUp(motionEvent: MotionEvent) {
+ super.onUp(motionEvent)
+ cancelHorizontalSwipeRunnable()
+ isHorizontalSwipeConfirmed = false
+ }
+
override fun onSingleTapUp(motionEvent: MotionEvent): Boolean {
MotionEvent.obtain(motionEvent).let {
it.action = MotionEvent.ACTION_DOWN
@@ -114,6 +136,9 @@ class ClassicSwipeController(
// cancel if locked
if (!config.enableSwipeControlsLockMode && config.isScreenLocked) return false
if (currentSwipe == SwipeDetector.SwipeDirection.VERTICAL) {
+ cancelHorizontalSwipeRunnable()
+ isHorizontalSwipeConfirmed = false
+
return when (from.toPoint()) {
in controller.zones.volume -> {
scrollVolume(distanceY)
@@ -122,31 +147,51 @@ class ClassicSwipeController(
in controller.zones.brightness -> {
scrollBrightness(distanceY)
- true}
-
-
- else -> false
- }
- } else if (currentSwipe == SwipeDetector.SwipeDirection.HORIZONTAL) {
- return when (from.toPoint()) {
- in controller.zones.speed -> {
- if (config.enableSpeedControl) {
- scrollSpeed(distanceX)
- true
- }
- false
+ true
}
- in controller.zones.seek -> {
- if (config.enableSeekControl) {
- scrollSeek(distanceX)
- true
- }
- false
- }
-
else -> false
}
}
+ else if (currentSwipe == SwipeDetector.SwipeDirection.HORIZONTAL) {
+ if (isHorizontalSwipeConfirmed) {
+ return processHorizontalSwipe(from, distanceX)
+ }
+
+ if (horizontalSwipeRunnable == null) {
+ horizontalSwipeRunnable = Runnable {
+ isHorizontalSwipeConfirmed = true
+ processHorizontalSwipe(from, distanceX)
+ }
+ handler.postDelayed(horizontalSwipeRunnable!!, horizontalSwipeDelayMs)
+ }
+ return true
+ }
+
return false
}
+
+ private fun processHorizontalSwipe(from: MotionEvent, distanceX: Double): Boolean {
+ return when (from.toPoint()) {
+ in controller.zones.speed -> {
+ if (config.enableSpeedControl) {
+ scrollSpeed(distanceX)
+ true
+ } else false
+ }
+ in controller.zones.seek -> {
+ if (config.enableSeekControl) {
+ scrollSeek(distanceX)
+ true
+ } else false
+ }
+ else -> false
+ }
+ }
+
+ private fun cancelHorizontalSwipeRunnable() {
+ horizontalSwipeRunnable?.let {
+ handler.removeCallbacks(it)
+ }
+ horizontalSwipeRunnable = null
+ }
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/visual/VisualPreferencesIconsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/visual/VisualPreferencesIconsPatch.kt
index 6ab977380..5bd86fa31 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/visual/VisualPreferencesIconsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/visual/VisualPreferencesIconsPatch.kt
@@ -481,7 +481,6 @@ private val emptyTitles = setOf(
"revanced_external_downloader_package_name_playlist",
"revanced_external_downloader_package_name_video",
"revanced_external_downloader_package_name_video_long_press",
- "revanced_fix_swipe_tap_and_hold_speed",
"revanced_gemini_transcribe_subtitles_font_size",
"revanced_gms_show_dialog",
"revanced_hide_live_chat_replay_button",
@@ -524,6 +523,7 @@ private val emptyTitles = setOf(
"revanced_swipe_overlay_progress_speed_color",
"revanced_swipe_overlay_progress_volume_color",
"revanced_swipe_seek_distance",
+ "revanced_swipe_speed_and_seek_delay",
"revanced_swipe_speed_distance",
"revanced_swipe_switch_speed_and_seek",
"revanced_swipe_volume_distance",
diff --git a/patches/src/main/resources/youtube/settings/host/values/strings.xml b/patches/src/main/resources/youtube/settings/host/values/strings.xml
index ecdab1561..70c700422 100644
--- a/patches/src/main/resources/youtube/settings/host/values/strings.xml
+++ b/patches/src/main/resources/youtube/settings/host/values/strings.xml
@@ -2662,6 +2662,8 @@ Adjust brightness by swiping vertically on the left side of the screen."
Enable seek gesture
+ The delay in milliseconds before a horizontal swipe is recognized. Increase this value if it interferes with the pinch-to-zoom gesture.
+ Speed and seek gestures delay
The amount of pixels to swipe to change the video speed. Lower values make the speed change faster, higher values make it slower (1-1000).
Speed distance
Fullscreen speed swipe is disabled.
diff --git a/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml b/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml
index e702d7273..7f72e876b 100644
--- a/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml
+++ b/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml
@@ -779,6 +779,7 @@
+
diff --git a/xml_tools/handlers/check_prefs_reverse.py b/xml_tools/handlers/check_prefs_reverse.py
index a9e463183..b0911b81f 100644
--- a/xml_tools/handlers/check_prefs_reverse.py
+++ b/xml_tools/handlers/check_prefs_reverse.py
@@ -49,6 +49,7 @@ BLACKLIST: set[str] = {
"revanced_swipe_seek_sensitivity",
"revanced_swipe_show_circular_overlay",
"revanced_swipe_speed",
+ "revanced_swipe_speed_and_seek_delay",
"revanced_swipe_speed_distance",
"revanced_swipe_speed_sensitivity",
"revanced_swipe_switch_speed_and_seek",