From 8fb3b470fd538cee5338d67941dda310b888e5a3 Mon Sep 17 00:00:00 2001
From: Yonat Sharon <yonat@ootips.org>
Date: Wed, 29 Mar 2023 14:14:08 +0300
Subject: [PATCH] VLCRemoteControlService: Add settings to skip forward/back
 instead of go to next/previous track - on lockscreen and when using external
 controls (like headphones and car controls). (resolve #428 #800)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Felix Paul Kühne <felix@feepk.net>
---
 Documentation/NEWS.md                         |  2 +
 .../iOS/Settings.bundle/Root.inApp.plist      | 40 +++++++++++++++++++
 Resources/iOS/Settings.bundle/Root.plist      | 40 +++++++++++++++++++
 .../iOS/Settings.bundle/en.lproj/Root.strings |  4 ++
 .../Settings.bundle/en.lproj/Root.strings     |  4 ++
 Sources/App/iOS/VLCAppDelegate.m              |  2 +
 Sources/App/tvOS/AppleTVAppDelegate.m         |  2 +
 Sources/Headers/VLCConstants.h                |  2 +
 Sources/Headers/VLCTVConstants.h              |  2 +
 .../OS Integration/VLCRemoteControlService.m  | 27 ++++++++++---
 Sources/Settings/Model/SettingsSection.swift  | 16 ++++++++
 11 files changed, 135 insertions(+), 6 deletions(-)

diff --git a/Documentation/NEWS.md b/Documentation/NEWS.md
index 27ff0aef2..e788ff0a4 100644
--- a/Documentation/NEWS.md
+++ b/Documentation/NEWS.md
@@ -25,6 +25,8 @@
 · Fix silence for a split second when starting playback
 · Add option to force the video playback orientation lock
 · Seek playback using the number keys on external keyboards
+· Add options to skip forward/back instead of go to next/previous track
+    - on lockscreen and when using external controls (like headphones and car controls)
 · Integrate with the pCloud service
 
 Known issues:
diff --git a/Resources/iOS/Settings.bundle/Root.inApp.plist b/Resources/iOS/Settings.bundle/Root.inApp.plist
index 757b8d738..2172d720b 100644
--- a/Resources/iOS/Settings.bundle/Root.inApp.plist
+++ b/Resources/iOS/Settings.bundle/Root.inApp.plist
@@ -356,6 +356,46 @@
 				<integer>60</integer>
 			</array>
 		</dict>
+		<dict>
+			<key>Type</key>
+			<string>PSMultiValueSpecifier</string>
+			<key>Title</key>
+			<string>SETTINGS_PLAYBACK_LOCKSCREEN_SKIP</string>
+			<key>Key</key>
+			<string>playback-lockscreen-skip</string>
+			<key>DefaultValue</key>
+			<false/>
+			<key>Titles</key>
+			<array>
+				<string>SETTINGS_PLAYBACK_CONTROLS_SKIP_NO</string>
+				<string>SETTINGS_PLAYBACK_CONTROLS_SKIP_YES</string>
+			</array>
+			<key>Values</key>
+			<array>
+				<false/>
+				<true/>
+			</array>
+		</dict>
+		<dict>
+			<key>Type</key>
+			<string>PSMultiValueSpecifier</string>
+			<key>Title</key>
+			<string>SETTINGS_PLAYBACK_EXTERNAL_CONTROLS_SKIP</string>
+			<key>Key</key>
+			<string>playback-remote-control-skip</string>
+			<key>DefaultValue</key>
+			<false/>
+			<key>Titles</key>
+			<array>
+				<string>SETTINGS_PLAYBACK_CONTROLS_SKIP_NO</string>
+				<string>SETTINGS_PLAYBACK_CONTROLS_SKIP_YES</string>
+			</array>
+			<key>Values</key>
+			<array>
+				<false/>
+				<true/>
+			</array>
+		</dict>
 		<dict>
 			<key>Type</key>
 			<string>PSMultiValueSpecifier</string>
diff --git a/Resources/iOS/Settings.bundle/Root.plist b/Resources/iOS/Settings.bundle/Root.plist
index 0f5784a30..d5ea292f6 100644
--- a/Resources/iOS/Settings.bundle/Root.plist
+++ b/Resources/iOS/Settings.bundle/Root.plist
@@ -258,6 +258,46 @@
 				<integer>1</integer>
 			</array>
 		</dict>
+		<dict>
+			<key>Type</key>
+			<string>PSMultiValueSpecifier</string>
+			<key>Title</key>
+			<string>SETTINGS_PLAYBACK_LOCKSCREEN_SKIP</string>
+			<key>Key</key>
+			<string>playback-lockscreen-skip</string>
+			<key>DefaultValue</key>
+			<false/>
+			<key>Titles</key>
+			<array>
+				<string>SETTINGS_PLAYBACK_CONTROLS_SKIP_NO</string>
+				<string>SETTINGS_PLAYBACK_CONTROLS_SKIP_YES</string>
+			</array>
+			<key>Values</key>
+			<array>
+				<false/>
+				<true/>
+			</array>
+		</dict>
+		<dict>
+			<key>Type</key>
+			<string>PSMultiValueSpecifier</string>
+			<key>Title</key>
+			<string>SETTINGS_PLAYBACK_EXTERNAL_CONTROLS_SKIP</string>
+			<key>Key</key>
+			<string>playback-remote-control-skip</string>
+			<key>DefaultValue</key>
+			<false/>
+			<key>Titles</key>
+			<array>
+				<string>SETTINGS_PLAYBACK_CONTROLS_SKIP_NO</string>
+				<string>SETTINGS_PLAYBACK_CONTROLS_SKIP_YES</string>
+			</array>
+			<key>Values</key>
+			<array>
+				<false/>
+				<true/>
+			</array>
+		</dict>
 		<dict>
 			<key>Type</key>
 			<string>PSMultiValueSpecifier</string>
diff --git a/Resources/iOS/Settings.bundle/en.lproj/Root.strings b/Resources/iOS/Settings.bundle/en.lproj/Root.strings
index 9379a87c5..533c90bd7 100644
--- a/Resources/iOS/Settings.bundle/en.lproj/Root.strings
+++ b/Resources/iOS/Settings.bundle/en.lproj/Root.strings
@@ -151,6 +151,10 @@
 "SETTINGS_PLAYBACK_SKIP_FORWARD_SWIPE" = "Forward skip on swipe";
 "SETTINGS_PLAYBACK_SKIP_BACKWARD_SWIPE" = "Backward skip on swipe";
 "SETINGS_LONG_TOUCH_SPEED_UP" = "Long touch to speed-up";
+"SETTINGS_PLAYBACK_LOCKSCREEN_SKIP" = "Lock screen controls";
+"SETTINGS_PLAYBACK_EXTERNAL_CONTROLS_SKIP" = "External controls (headphones, car)";
+"SETTINGS_PLAYBACK_CONTROLS_SKIP_NO" = "Next/Previous";
+"SETTINGS_PLAYBACK_CONTROLS_SKIP_YES" = "Skip Forward/Back";
 
 "SETTINGS_DURATION_FOUR" = "4 seconds";
 "SETTINGS_DURATION_FIVE" = "5 seconds";
diff --git a/Resources/tvOS/Settings.bundle/en.lproj/Root.strings b/Resources/tvOS/Settings.bundle/en.lproj/Root.strings
index 9379a87c5..533c90bd7 100644
--- a/Resources/tvOS/Settings.bundle/en.lproj/Root.strings
+++ b/Resources/tvOS/Settings.bundle/en.lproj/Root.strings
@@ -151,6 +151,10 @@
 "SETTINGS_PLAYBACK_SKIP_FORWARD_SWIPE" = "Forward skip on swipe";
 "SETTINGS_PLAYBACK_SKIP_BACKWARD_SWIPE" = "Backward skip on swipe";
 "SETINGS_LONG_TOUCH_SPEED_UP" = "Long touch to speed-up";
+"SETTINGS_PLAYBACK_LOCKSCREEN_SKIP" = "Lock screen controls";
+"SETTINGS_PLAYBACK_EXTERNAL_CONTROLS_SKIP" = "External controls (headphones, car)";
+"SETTINGS_PLAYBACK_CONTROLS_SKIP_NO" = "Next/Previous";
+"SETTINGS_PLAYBACK_CONTROLS_SKIP_YES" = "Skip Forward/Back";
 
 "SETTINGS_DURATION_FOUR" = "4 seconds";
 "SETTINGS_DURATION_FIVE" = "5 seconds";
diff --git a/Sources/App/iOS/VLCAppDelegate.m b/Sources/App/iOS/VLCAppDelegate.m
index a5015b1e9..d054b695f 100644
--- a/Sources/App/iOS/VLCAppDelegate.m
+++ b/Sources/App/iOS/VLCAppDelegate.m
@@ -75,6 +75,8 @@
                                   kVLCSettingPlaybackBackwardSkipLength : kVLCSettingPlaybackBackwardSkipLengthDefaultValue,
                                   kVLCSettingPlaybackForwardSkipLengthSwipe : kVLCSettingPlaybackForwardSkipLengthSwipeDefaultValue,
                                   kVLCSettingPlaybackBackwardSkipLengthSwipe : kVLCSettingPlaybackBackwardSkipLengthSwipeDefaultValue,
+                                  kVLCSettingPlaybackLockscreenSkip : @(NO),
+                                  kVLCSettingPlaybackRemoteControlSkip : @(NO),
                                   kVLCSettingOpenAppForPlayback : kVLCSettingOpenAppForPlaybackDefaultValue,
                                   kVLCAutomaticallyPlayNextItem : @(YES),
                                   kVLCPlaylistPlayNextItem: @(YES),
diff --git a/Sources/App/tvOS/AppleTVAppDelegate.m b/Sources/App/tvOS/AppleTVAppDelegate.m
index 3ebe374a5..450251463 100644
--- a/Sources/App/tvOS/AppleTVAppDelegate.m
+++ b/Sources/App/tvOS/AppleTVAppDelegate.m
@@ -58,6 +58,8 @@
                                   kVLCSettingEqualizerProfile : kVLCSettingEqualizerProfileDefaultValue,
                                   kVLCSettingPlaybackForwardSkipLength : kVLCSettingPlaybackForwardSkipLengthDefaultValue,
                                   kVLCSettingPlaybackBackwardSkipLength : kVLCSettingPlaybackBackwardSkipLengthDefaultValue,
+                                  kVLCSettingPlaybackLockscreenSkip : @(NO),
+                                  kVLCSettingPlaybackRemoteControlSkip : @(NO),
                                   kVLCSettingWiFiSharingIPv6 : kVLCSettingWiFiSharingIPv6DefaultValue,
                                   kVLCAutomaticallyPlayNextItem : @(YES),
                                   kVLCPlayerShouldRememberState: @(YES),
diff --git a/Sources/Headers/VLCConstants.h b/Sources/Headers/VLCConstants.h
index f1fa44db9..c5d22a5b1 100644
--- a/Sources/Headers/VLCConstants.h
+++ b/Sources/Headers/VLCConstants.h
@@ -85,6 +85,8 @@
 #define kVLCSettingPlaybackBackwardSkipLengthSwipe @"playback-backward-skip-length-swipe"
 #define kVLCSettingPlaybackLongTouchSpeedUp @"LongTouchSpeedUp"
 #define kVLCSettingPlaybackBackwardSkipLengthSwipeDefaultValue @(10)
+#define kVLCSettingPlaybackLockscreenSkip @"playback-lockscreen-skip"
+#define kVLCSettingPlaybackRemoteControlSkip @"playback-remote-control-skip"
 #define kVLCSettingOpenAppForPlayback @"open-app-for-playback"
 #define kVLCSettingOpenAppForPlaybackDefaultValue @YES
 #define kVLCSettingShowThumbnails @"ShowThumbnails"
diff --git a/Sources/Headers/VLCTVConstants.h b/Sources/Headers/VLCTVConstants.h
index 0a50d5751..3d5e993b0 100644
--- a/Sources/Headers/VLCTVConstants.h
+++ b/Sources/Headers/VLCTVConstants.h
@@ -65,6 +65,8 @@
 #define kVLCSettingPlaybackForwardSkipLengthDefaultValue @(10)
 #define kVLCSettingPlaybackBackwardSkipLength @"playback-backward-skip-length"
 #define kVLCSettingPlaybackBackwardSkipLengthDefaultValue @(10)
+#define kVLCSettingPlaybackLockscreenSkip @"playback-lockscreen-skip"
+#define kVLCSettingPlaybackRemoteControlSkip @"playback-remote-control-skip"
 #define kVLCSettingSaveHTTPUploadServerStatus @"isHTTPServerOn"
 #define kVLCAutomaticallyPlayNextItem @"AutomaticallyPlayNextItem"
 #define kVLCPlayerUIShouldHide @"PlayerUIShouldHide"
diff --git a/Sources/Playback/OS Integration/VLCRemoteControlService.m b/Sources/Playback/OS Integration/VLCRemoteControlService.m
index fdac5521e..4ac418525 100644
--- a/Sources/Playback/OS Integration/VLCRemoteControlService.m	
+++ b/Sources/Playback/OS Integration/VLCRemoteControlService.m	
@@ -55,12 +55,14 @@ static inline NSArray * RemoteCommandCenterCommandsToHandle(void)
 - (void)playbackStarted:(NSNotification *)aNotification
 {
     MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 
     /* Since the control center and lockscreen shows only either skipForward/Backward
      * or next/previousTrack buttons but prefers skip buttons,
      * we only enable skip buttons if we have no medialist
      */
-    BOOL enableSkip = [VLCPlaybackService sharedInstance].mediaList.count <= 1;
+    BOOL alwaysEnableSkip = [defaults boolForKey:kVLCSettingPlaybackLockscreenSkip];
+    BOOL enableSkip = alwaysEnableSkip || [VLCPlaybackService sharedInstance].mediaList.count <= 1;
     commandCenter.skipForwardCommand.enabled = enableSkip;
     commandCenter.skipBackwardCommand.enabled = enableSkip;
 
@@ -76,7 +78,6 @@ static inline NSArray * RemoteCommandCenterCommandsToHandle(void)
     commandCenter.seekForwardCommand.enabled = NO;
     commandCenter.seekBackwardCommand.enabled = NO;
 
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
     NSNumber *forwardSkip = [defaults valueForKey:kVLCSettingPlaybackForwardSkipLength];
     commandCenter.skipForwardCommand.preferredIntervals = @[forwardSkip];
     NSNumber *backwardSkip = [defaults valueForKey:kVLCSettingPlaybackBackwardSkipLength];
@@ -102,6 +103,7 @@ static inline NSArray * RemoteCommandCenterCommandsToHandle(void)
 {
     MPRemoteCommandCenter *cc = [MPRemoteCommandCenter sharedCommandCenter];
     VLCPlaybackService *vps = [VLCPlaybackService sharedInstance];
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 
     if (event.command == cc.pauseCommand) {
         [vps pause];
@@ -120,12 +122,25 @@ static inline NSArray * RemoteCommandCenterCommandsToHandle(void)
         return MPRemoteCommandHandlerStatusSuccess;
     }
     if (event.command == cc.nextTrackCommand) {
-        BOOL success = [vps next];
-        return success ? MPRemoteCommandHandlerStatusSuccess : MPRemoteCommandHandlerStatusNoSuchContent;
+        if ([defaults boolForKey:kVLCSettingPlaybackRemoteControlSkip]) {
+            NSInteger interval = [defaults integerForKey:kVLCSettingPlaybackForwardSkipLength];
+            [vps jumpForward:(int)interval];
+            return MPRemoteCommandHandlerStatusSuccess;
+        } else {
+            BOOL success = [vps next];
+            return success ? MPRemoteCommandHandlerStatusSuccess : MPRemoteCommandHandlerStatusNoSuchContent;
+        }
     }
     if (event.command == cc.previousTrackCommand) {
-        BOOL success = [vps previous];
-        return success ? MPRemoteCommandHandlerStatusSuccess : MPRemoteCommandHandlerStatusNoSuchContent;
+        if ([defaults boolForKey:kVLCSettingPlaybackRemoteControlSkip]) {
+            NSInteger interval = [defaults integerForKey:kVLCSettingPlaybackBackwardSkipLength];
+            [vps jumpBackward:(int)interval];
+            return MPRemoteCommandHandlerStatusSuccess;
+
+        } else {
+            BOOL success = [vps previous];
+            return success ? MPRemoteCommandHandlerStatusSuccess : MPRemoteCommandHandlerStatusNoSuchContent;
+        }
     }
     if (event.command == cc.skipForwardCommand) {
         MPSkipIntervalCommandEvent *skipEvent = (MPSkipIntervalCommandEvent *)event;
diff --git a/Sources/Settings/Model/SettingsSection.swift b/Sources/Settings/Model/SettingsSection.swift
index 0522ca902..a7b6ffbe5 100644
--- a/Sources/Settings/Model/SettingsSection.swift
+++ b/Sources/Settings/Model/SettingsSection.swift
@@ -397,6 +397,20 @@ enum GestureControlOptions {
                 preferenceKey: kVLCSettingPlaybackLongTouchSpeedUp)
     }
 
+    static var lockScreenSkip: SettingsItem {
+        let k = kVLCSettingPlaybackLockscreenSkip
+        return .init(title: "SETTINGS_PLAYBACK_LOCKSCREEN_SKIP",
+                     subtitle: Localizer.getSubtitle(for: k),
+                     action: .showActionSheet(title: "SETTINGS_PLAYBACK_LOCKSCREEN_SKIP", preferenceKey: k, hasInfo: false))
+    }
+
+    static var externalControlsSkip: SettingsItem {
+        let k = kVLCSettingPlaybackRemoteControlSkip
+        return .init(title: "SETTINGS_PLAYBACK_EXTERNAL_CONTROLS_SKIP",
+                     subtitle: Localizer.getSubtitle(for: k),
+                     action: .showActionSheet(title: "SETTINGS_PLAYBACK_EXTERNAL_CONTROLS_SKIP", preferenceKey: k, hasInfo: false))
+    }
+
     static func section(isForwardBackwardEqual: Bool, isTapSwipeEqual: Bool) -> SettingsSection? {
         .init(title: "SETTINGS_GESTURES", items: [
             swipeUpDownForVolume,
@@ -411,6 +425,8 @@ enum GestureControlOptions {
             isTapSwipeEqual ? nil : forwardSkipLengthSwipe,
             (isTapSwipeEqual || isForwardBackwardEqual) ? nil : backwardSkipLengthSwipe,
             longTouchToSpeedUp,
+            lockScreenSkip,
+            externalControlsSkip,
         ].compactMap { $0 })
     }
 
-- 
GitLab