Merge pull request #377 from bhackel/gesture-improvement

Gesture improvement
This commit is contained in:
Bryce Hackel
2024-08-26 01:12:43 -07:00
committed by GitHub
17 changed files with 260 additions and 76 deletions

View File

@@ -372,7 +372,9 @@ static const NSInteger YTLiteSection = 789;
createSectionGestureSelector(@"BOTTOM_SECTION", @"playerGestureBottomSelection"), createSectionGestureSelector(@"BOTTOM_SECTION", @"playerGestureBottomSelection"),
// Pickers for configuration settings // Pickers for configuration settings
deadzonePicker, deadzonePicker,
sensitivityPicker sensitivityPicker,
// Toggle for haptic feedback
BASIC_SWITCH(LOC(@"PLAYER_GESTURES_HAPTIC_FEEDBACK"), nil, @"playerGesturesHapticFeedback_enabled"),
]; ];
YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"Player Gestures (Beta)") pickerSectionTitle:nil rows:rows selectedItemIndex:NSNotFound parentResponder:[self parentResponder]]; YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"Player Gestures (Beta)") pickerSectionTitle:nil rows:rows selectedItemIndex:NSNotFound parentResponder:[self parentResponder]];
[settingsViewController pushViewController:picker]; [settingsViewController pushViewController:picker];

View File

@@ -48,6 +48,7 @@
#import "Tweaks/YouTubeHeader/YTWatchLayerViewController.h" #import "Tweaks/YouTubeHeader/YTWatchLayerViewController.h"
#import "Tweaks/YouTubeHeader/YTPageStyleController.h" #import "Tweaks/YouTubeHeader/YTPageStyleController.h"
#import "Tweaks/YouTubeHeader/YTRightNavigationButtons.h" #import "Tweaks/YouTubeHeader/YTRightNavigationButtons.h"
#import "Tweaks/YouTubeHeader/YTInlinePlayerBarView.h"
#define LOC(x) [tweakBundle localizedStringForKey:x value:nil table:nil] #define LOC(x) [tweakBundle localizedStringForKey:x value:nil table:nil]
#define YT_BUNDLE_ID @"com.google.ios.youtube" #define YT_BUNDLE_ID @"com.google.ios.youtube"
@@ -155,6 +156,10 @@ typedef NS_ENUM(NSUInteger, GestureSection) {
@end @end
// Player Gestures - @bhackel // Player Gestures - @bhackel
@interface YTFineScrubberFilmstripView : UIView
@end
@interface YTFineScrubberFilmstripCollectionView : UICollectionView
@end
@interface YTPlayerViewController (YTLitePlus) <UIGestureRecognizerDelegate> @interface YTPlayerViewController (YTLitePlus) <UIGestureRecognizerDelegate>
@property (nonatomic, retain) UIPanGestureRecognizer *YTLitePlusPanGesture; @property (nonatomic, retain) UIPanGestureRecognizer *YTLitePlusPanGesture;
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer; - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
@@ -164,6 +169,20 @@ typedef NS_ENUM(NSUInteger, GestureSection) {
@interface MPVolumeController : NSObject @interface MPVolumeController : NSObject
@property (nonatomic, assign, readwrite) float volumeValue; @property (nonatomic, assign, readwrite) float volumeValue;
@end @end
@interface YTPlayerBarController (YTLitePlus)
- (void)didScrub:(UIPanGestureRecognizer *)gestureRecognizer;
- (void)startScrubbing;
- (void)didScrubToPoint:(CGPoint)point;
- (void)endScrubbingForSeekSource:(int)seekSource;
@end
@interface YTMainAppVideoPlayerOverlayViewController (YTLitePlus)
@property (nonatomic, strong, readwrite) YTPlayerBarController *playerBarController;
@end
@interface YTInlinePlayerBarContainerView (YTLitePlus)
@property UIPanGestureRecognizer *scrubGestureRecognizer;
@property (nonatomic, strong, readwrite) YTFineScrubberFilmstripView *fineScrubberFilmstrip;
- (CGFloat)scrubXForScrubRange:(CGFloat)scrubRange;
@end
// Hide Collapse Button - @arichornlover // Hide Collapse Button - @arichornlover
@interface YTMainAppControlsOverlayView (YTLitePlus) @interface YTMainAppControlsOverlayView (YTLitePlus)

View File

@@ -641,7 +641,7 @@ BOOL isTabSelected = NO;
%end %end
// Gestures - @bhackel // Gestures - @bhackel
%group playerGestures %group gPlayerGestures
%hook YTWatchLayerViewController %hook YTWatchLayerViewController
// invoked when the player view controller is either created or destroyed // invoked when the player view controller is either created or destroyed
- (void)watchController:(YTWatchController *)watchController didSetPlayerViewController:(YTPlayerViewController *)playerViewController { - (void)watchController:(YTWatchController *)watchController didSetPlayerViewController:(YTPlayerViewController *)playerViewController {
@@ -662,14 +662,22 @@ BOOL isTabSelected = NO;
%hook YTPlayerViewController %hook YTPlayerViewController
// the pan gesture that will be created and added to the player view // the pan gesture that will be created and added to the player view
%property (nonatomic, retain) UIPanGestureRecognizer *YTLitePlusPanGesture; %property (nonatomic, retain) UIPanGestureRecognizer *YTLitePlusPanGesture;
/**
* This method is called when the pan gesture is started, changed, or ended. It handles
* 12 different possible cases depending on the configuration: 3 zones with 4 choices
* for each zone. The zones are horizontal sections that divide the player into
* 3 equal parts. The choices are volume, brightness, seek, and disabled.
* There is also a deadzone that can be configured in the settings.
* There are 4 logical states: initial, changed in deadzone, changed, end.
*/
%new %new
- (void)YTLitePlusHandlePanGesture:(UIPanGestureRecognizer *)panGestureRecognizer { - (void)YTLitePlusHandlePanGesture:(UIPanGestureRecognizer *)panGestureRecognizer {
// Haptic feedback generator // Haptic feedback generator
static UIImpactFeedbackGenerator *feedbackGenerator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium]; static UIImpactFeedbackGenerator *feedbackGenerator;
// Variables for storing initial values to be adjusted // Variables for storing initial values to be adjusted
static float initialVolume; static float initialVolume;
static float initialBrightness; static float initialBrightness;
static CGFloat currentTime; static CGFloat initialTime;
// Flag to determine if the pan gesture is valid // Flag to determine if the pan gesture is valid
static BOOL isValidHorizontalPan = NO; static BOOL isValidHorizontalPan = NO;
// Variable to store the section of the screen the gesture is in // Variable to store the section of the screen the gesture is in
@@ -678,62 +686,128 @@ BOOL isTabSelected = NO;
static CGPoint startLocation; static CGPoint startLocation;
// Variable to track the X translation when exiting the deadzone // Variable to track the X translation when exiting the deadzone
static CGFloat deadzoneStartingXTranslation; static CGFloat deadzoneStartingXTranslation;
// Variable to track the X translation of the pan gesture after exiting the deadzone
static CGFloat adjustedTranslationX;
// Variable used to smooth out the X translation
static CGFloat smoothedTranslationX = 0;
// Constant for the filter constant to change responsiveness
static const CGFloat filterConstant = 0.1;
// Constant for the deadzone radius that can be changed in the settings // Constant for the deadzone radius that can be changed in the settings
static CGFloat deadzoneRadius = (CGFloat)GetFloat(@"playerGesturesDeadzone"); static CGFloat deadzoneRadius = (CGFloat)GetFloat(@"playerGesturesDeadzone");
// Constant for the sensitivity factor that can be changed in the settings // Constant for the sensitivity factor that can be changed in the settings
static CGFloat sensitivityFactor = (CGFloat)GetFloat(@"playerGesturesSensitivity"); static CGFloat sensitivityFactor = (CGFloat)GetFloat(@"playerGesturesSensitivity");
// Objects for modifying the system volume
/***** Helper functions *****/ static MPVolumeView *volumeView;
// Helper function to adjust brightness static UISlider *volumeViewSlider;
void (^adjustBrightness)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat initialBrightness) { // Get objects that should only be initialized once
float newBrightness = initialBrightness + ((translationX / 1000.0) * sensitivityFactor); static dispatch_once_t onceToken;
newBrightness = fmaxf(fminf(newBrightness, 1.0), 0.0); dispatch_once(&onceToken, ^{
[[UIScreen mainScreen] setBrightness:newBrightness]; volumeView = [[MPVolumeView alloc] init];
};
// Helper function to adjust volume
void (^adjustVolume)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat initialVolume) {
float newVolume = initialVolume + ((translationX / 1000.0) * sensitivityFactor);
newVolume = fmaxf(fminf(newVolume, 1.0), 0.0);
// https://stackoverflow.com/questions/50737943/how-to-change-volume-programmatically-on-ios-11-4
MPVolumeView *volumeView = [[MPVolumeView alloc] init];
UISlider *volumeViewSlider = nil;
for (UIView *view in volumeView.subviews) { for (UIView *view in volumeView.subviews) {
if ([view isKindOfClass:[UISlider class]]) { if ([view isKindOfClass:[UISlider class]]) {
volumeViewSlider = (UISlider *)view; volumeViewSlider = (UISlider *)view;
break; break;
} }
} }
feedbackGenerator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium];
});
// Get objects used to seek nicely in the video player
static YTMainAppVideoPlayerOverlayViewController *mainVideoPlayerController = (YTMainAppVideoPlayerOverlayViewController *)self.childViewControllers.firstObject;
static YTPlayerBarController *playerBarController = mainVideoPlayerController.playerBarController;
static YTInlinePlayerBarContainerView *playerBar = playerBarController.playerBar;
/***** Helper functions for adjusting player state *****/
// Helper function to adjust brightness
void (^adjustBrightness)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat initialBrightness) {
float brightnessSensitivityFactor = 3;
float newBrightness = initialBrightness + ((translationX / 1000.0) * sensitivityFactor * brightnessSensitivityFactor);
newBrightness = fmaxf(fminf(newBrightness, 1.0), 0.0);
[[UIScreen mainScreen] setBrightness:newBrightness];
};
// Helper function to adjust volume
void (^adjustVolume)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat initialVolume) {
float volumeSensitivityFactor = 3.0;
float newVolume = initialVolume + ((translationX / 1000.0) * sensitivityFactor * volumeSensitivityFactor);
newVolume = fmaxf(fminf(newVolume, 1.0), 0.0);
// Improve smoothness - ignore if the volume is within 0.01 of the current volume
CGFloat currentVolume = [[AVAudioSession sharedInstance] outputVolume];
if (fabs(newVolume - currentVolume) < 0.01 && currentVolume > 0.01 && currentVolume < 0.99) {
return;
}
// https://stackoverflow.com/questions/50737943/how-to-change-volume-programmatically-on-ios-11-4
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
volumeViewSlider.value = newVolume; volumeViewSlider.value = newVolume;
}); });
}; };
// Helper function to adjust seek time // Helper function to adjust seek time
void (^adjustSeek)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat currentTime) { void (^adjustSeek)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat initialTime) {
// Calculate a seek fraction based on the horizontal translation // Get the location in view for the current video time
CGFloat totalDuration = self.currentVideoTotalMediaTime; CGFloat totalTime = self.currentVideoTotalMediaTime;
CGFloat viewWidth = self.view.bounds.size.width; CGFloat videoFraction = initialTime / totalTime;
CGFloat seekFraction = (translationX / viewWidth); CGFloat initialTimeXPosition = [playerBar scrubXForScrubRange:videoFraction];
// Seek to the new time based on the calculated offset // Calculate the new seek X position
CGFloat sensitivityFactor = 1; // Adjust this value to make seeking less sensitive CGFloat sensitivityFactor = 1; // Adjust this value to make seeking more/less sensitive
seekFraction = sensitivityFactor * seekFraction; CGFloat newSeekXPosition = initialTimeXPosition + translationX * sensitivityFactor;
CGFloat seekTime = currentTime + totalDuration * seekFraction; // Create a CGPoint using this new X position
[self seekToTime:seekTime]; CGPoint newSeekPoint = CGPointMake(newSeekXPosition, 0);
// Send this to a seek method in the player bar controller
[playerBarController didScrubToPoint:newSeekPoint];
}; };
// Helper function to run the selected gesture action
void (^runSelectedGesture)(NSString*, CGFloat, CGFloat, CGFloat, CGFloat) // Helper function to smooth out the X translation
= ^(NSString *sectionKey, CGFloat translationX, CGFloat initialBrightness, CGFloat initialVolume, CGFloat currentTime) { CGFloat (^applyLowPassFilter)(CGFloat) = ^(CGFloat newTranslation) {
smoothedTranslationX = filterConstant * newTranslation + (1 - filterConstant) * smoothedTranslationX;
return smoothedTranslationX;
};
/***** Helper functions for running the selected gesture *****/
// Helper function to run any setup for the selected gesture mode
void (^runSelectedGestureSetup)(NSString*) = ^(NSString *sectionKey) {
// Determine the selected gesture mode using the section key
GestureMode selectedGestureMode = (GestureMode)GetInteger(sectionKey);
// Handle the setup based on the selected mode
switch (selectedGestureMode) {
case GestureModeVolume:
initialVolume = [[AVAudioSession sharedInstance] outputVolume];
break;
case GestureModeBrightness:
initialBrightness = [UIScreen mainScreen].brightness;
break;
case GestureModeSeek:
initialTime = self.currentVideoMediaTime;
// Start a seek action
[playerBarController startScrubbing];
break;
case GestureModeDisabled:
// Do nothing if the gesture is disabled
break;
default:
// Show an alert if the gesture mode is invalid
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Invalid Gesture Mode" message:@"Please report this bug." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[alertController addAction:okAction];
[self presentViewController:alertController animated:YES completion:nil];
break;
}
};
// Helper function to run the selected gesture action when the gesture changes
void (^runSelectedGestureChanged)(NSString*) = ^(NSString *sectionKey) {
// Determine the selected gesture mode using the section key // Determine the selected gesture mode using the section key
GestureMode selectedGestureMode = (GestureMode)GetInteger(sectionKey); GestureMode selectedGestureMode = (GestureMode)GetInteger(sectionKey);
// Handle the gesture action based on the selected mode // Handle the gesture action based on the selected mode
switch (selectedGestureMode) { switch (selectedGestureMode) {
case GestureModeVolume: case GestureModeVolume:
adjustVolume(translationX, initialVolume); adjustVolume(adjustedTranslationX, initialVolume);
break; break;
case GestureModeBrightness: case GestureModeBrightness:
adjustBrightness(translationX, initialBrightness); adjustBrightness(adjustedTranslationX, initialBrightness);
break; break;
case GestureModeSeek: case GestureModeSeek:
adjustSeek(translationX, currentTime); adjustSeek(adjustedTranslationX, initialTime);
break; break;
case GestureModeDisabled: case GestureModeDisabled:
// Do nothing if the gesture is disabled // Do nothing if the gesture is disabled
@@ -747,6 +821,31 @@ BOOL isTabSelected = NO;
break; break;
} }
}; };
// Helper function to run the selected gesture action when the gesture ends
void (^runSelectedGestureEnded)(NSString*) = ^(NSString *sectionKey) {
// Determine the selected gesture mode using the section key
GestureMode selectedGestureMode = (GestureMode)GetInteger(sectionKey);
// Handle the gesture action based on the selected mode
switch (selectedGestureMode) {
case GestureModeVolume:
break;
case GestureModeBrightness:
break;
case GestureModeSeek:
[playerBarController endScrubbingForSeekSource:0];
break;
case GestureModeDisabled:
break;
default:
// Show an alert if the gesture mode is invalid
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Invalid Gesture Mode" message:@"Please report this bug." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[alertController addAction:okAction];
[self presentViewController:alertController animated:YES completion:nil];
break;
}
};
/***** End of Helper functions *****/ /***** End of Helper functions *****/
// Handle gesture based on current gesture state // Handle gesture based on current gesture state
@@ -754,37 +853,29 @@ BOOL isTabSelected = NO;
// Get the gesture's start position // Get the gesture's start position
startLocation = [panGestureRecognizer locationInView:self.view]; startLocation = [panGestureRecognizer locationInView:self.view];
CGFloat viewHeight = self.view.bounds.size.height; CGFloat viewHeight = self.view.bounds.size.height;
// Determine the section based on the start position by dividing the view into thirds
// Determine the section based on the start position
// by dividing the view into thirds
if (startLocation.y <= viewHeight / 3.0) { if (startLocation.y <= viewHeight / 3.0) {
gestureSection = GestureSectionTop; gestureSection = GestureSectionTop;
// Cancel the gesture if the mode is disabled
if (GetInteger(@"playerGestureTopSelection") == GestureModeDisabled) {
panGestureRecognizer.state = UIGestureRecognizerStateCancelled;
return;
}
} else if (startLocation.y <= 2 * viewHeight / 3.0) { } else if (startLocation.y <= 2 * viewHeight / 3.0) {
gestureSection = GestureSectionMiddle; gestureSection = GestureSectionMiddle;
// Cancel the gesture if the mode is disabled
if (GetInteger(@"playerGestureMiddleSelection") == GestureModeDisabled) {
panGestureRecognizer.state = UIGestureRecognizerStateCancelled;
return;
}
} else if (startLocation.y <= viewHeight) { } else if (startLocation.y <= viewHeight) {
gestureSection = GestureSectionBottom; gestureSection = GestureSectionBottom;
// Cancel the gesture if the mode is disabled
if (GetInteger(@"playerGestureBottomSelection") == GestureModeDisabled) {
panGestureRecognizer.state = UIGestureRecognizerStateCancelled;
return;
}
} else { } else {
gestureSection = GestureSectionInvalid; gestureSection = GestureSectionInvalid;
} }
// Cancel the gesture if the chosen mode for this section is disabled
if ( ((gestureSection == GestureSectionTop) && (GetInteger(@"playerGestureTopSelection") == GestureModeDisabled))
|| ((gestureSection == GestureSectionMiddle) && (GetInteger(@"playerGestureMiddleSelection") == GestureModeDisabled))
|| ((gestureSection == GestureSectionBottom) && (GetInteger(@"playerGestureBottomSelection") == GestureModeDisabled))) {
panGestureRecognizer.state = UIGestureRecognizerStateCancelled;
return;
}
// Deactive the activity flag // Deactive the activity flag
isValidHorizontalPan = NO; isValidHorizontalPan = NO;
} }
// Handle changed gesture state by activating the gesture once it has exited the deadzone,
// and then adjusting the player based on the selected gesture mode
if (panGestureRecognizer.state == UIGestureRecognizerStateChanged) { if (panGestureRecognizer.state == UIGestureRecognizerStateChanged) {
// Determine if the gesture is predominantly horizontal // Determine if the gesture is predominantly horizontal
CGPoint translation = [panGestureRecognizer translationInView:self.view]; CGPoint translation = [panGestureRecognizer translationInView:self.view];
@@ -799,12 +890,29 @@ BOOL isTabSelected = NO;
// If outside the deadzone, activate the pan gesture and store the initial values // If outside the deadzone, activate the pan gesture and store the initial values
isValidHorizontalPan = YES; isValidHorizontalPan = YES;
deadzoneStartingXTranslation = translation.x; deadzoneStartingXTranslation = translation.x;
initialBrightness = [UIScreen mainScreen].brightness; adjustedTranslationX = 0;
initialVolume = [[AVAudioSession sharedInstance] outputVolume]; smoothedTranslationX = 0;
currentTime = self.currentVideoMediaTime; // Run the setup for the selected gesture mode
switch (gestureSection) {
case GestureSectionTop:
runSelectedGestureSetup(@"playerGestureTopSelection");
break;
case GestureSectionMiddle:
runSelectedGestureSetup(@"playerGestureMiddleSelection");
break;
case GestureSectionBottom:
runSelectedGestureSetup(@"playerGestureBottomSelection");
break;
default:
// If the section is invalid, cancel the gesture
panGestureRecognizer.state = UIGestureRecognizerStateCancelled;
break;
}
// Provide haptic feedback to indicate a gesture start // Provide haptic feedback to indicate a gesture start
[feedbackGenerator prepare]; if (IS_ENABLED(@"playerGesturesHapticFeedback_enabled")) {
[feedbackGenerator impactOccurred]; [feedbackGenerator prepare];
[feedbackGenerator impactOccurred];
}
} else { } else {
// Cancel the gesture if the translation is not horizontal // Cancel the gesture if the translation is not horizontal
panGestureRecognizer.state = UIGestureRecognizerStateCancelled; panGestureRecognizer.state = UIGestureRecognizerStateCancelled;
@@ -814,34 +922,72 @@ BOOL isTabSelected = NO;
// Handle the gesture based on the identified section // Handle the gesture based on the identified section
if (isValidHorizontalPan) { if (isValidHorizontalPan) {
// Adjust the X translation based on the value hit after // Adjust the X translation based on the value hit after exiting the deadzone
// exiting the deadzone adjustedTranslationX = translation.x - deadzoneStartingXTranslation;
CGFloat adjustedTranslationX = translation.x - deadzoneStartingXTranslation; // Smooth the translation value
adjustedTranslationX = applyLowPassFilter(adjustedTranslationX);
// Pass the adjusted translation to the selected gesture // Pass the adjusted translation to the selected gesture
if (gestureSection == GestureSectionTop) { switch (gestureSection) {
runSelectedGesture(@"playerGestureTopSelection", adjustedTranslationX, initialBrightness, initialVolume, currentTime); case GestureSectionTop:
} else if (gestureSection == GestureSectionMiddle) { runSelectedGestureChanged(@"playerGestureTopSelection");
runSelectedGesture(@"playerGestureMiddleSelection", adjustedTranslationX, initialBrightness, initialVolume, currentTime); break;
} else if (gestureSection == GestureSectionBottom) { case GestureSectionMiddle:
runSelectedGesture(@"playerGestureBottomSelection", adjustedTranslationX, initialBrightness, initialVolume, currentTime); runSelectedGestureChanged(@"playerGestureMiddleSelection");
} else { break;
// If the section is invalid, cancel the gesture case GestureSectionBottom:
panGestureRecognizer.state = UIGestureRecognizerStateCancelled; runSelectedGestureChanged(@"playerGestureBottomSelection");
break;
default:
// If the section is invalid, cancel the gesture
panGestureRecognizer.state = UIGestureRecognizerStateCancelled;
break;
} }
} }
} }
if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) {
if (isValidHorizontalPan) { // Handle the gesture end state by running the selected gesture mode's end action
// Provide haptic feedback upon successful gesture recognition if (panGestureRecognizer.state == UIGestureRecognizerStateEnded && isValidHorizontalPan) {
[feedbackGenerator prepare]; switch (gestureSection) {
[feedbackGenerator impactOccurred]; case GestureSectionTop:
runSelectedGestureEnded(@"playerGestureTopSelection");
break;
case GestureSectionMiddle:
runSelectedGestureEnded(@"playerGestureMiddleSelection");
break;
case GestureSectionBottom:
runSelectedGestureEnded(@"playerGestureBottomSelection");
break;
default:
break;
} }
// Provide haptic feedback upon successful gesture recognition
// [feedbackGenerator prepare];
// [feedbackGenerator impactOccurred];
} }
} }
// allow the pan gesture to be recognized simultaneously with other gestures // allow the pan gesture to be recognized simultaneously with other gestures
%new %new
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
// Do not allow this gesture to activate with the normal seek bar gesture
YTMainAppVideoPlayerOverlayViewController *mainVideoPlayerController = (YTMainAppVideoPlayerOverlayViewController *)self.childViewControllers.firstObject;
YTPlayerBarController *playerBarController = mainVideoPlayerController.playerBarController;
YTInlinePlayerBarContainerView *playerBar = playerBarController.playerBar;
if (otherGestureRecognizer == playerBar.scrubGestureRecognizer) {
return NO;
}
// Do not allow this gesture to activate with the fine scrubber gesture
YTFineScrubberFilmstripView *fineScrubberFilmstrip = playerBar.fineScrubberFilmstrip;
if (!fineScrubberFilmstrip) {
return YES;
}
YTFineScrubberFilmstripCollectionView *filmstripCollectionView = [fineScrubberFilmstrip valueForKey:@"_filmstripCollectionView"];
if (filmstripCollectionView && otherGestureRecognizer == filmstripCollectionView.panGestureRecognizer) {
return NO;
}
}
return YES; return YES;
} }
%end %end
@@ -1156,7 +1302,7 @@ NSInteger pageStyle = 0;
%init(gDisableEngagementOverlay); %init(gDisableEngagementOverlay);
} }
if (IsEnabled(@"playerGestures_enabled")) { if (IsEnabled(@"playerGestures_enabled")) {
%init(playerGestures); %init(gPlayerGestures);
} }
if (IsEnabled(@"videoPlayerButton_enabled")) { if (IsEnabled(@"videoPlayerButton_enabled")) {
%init(gVideoPlayerButton); %init(gVideoPlayerButton);
@@ -1196,4 +1342,7 @@ NSInteger pageStyle = 0;
if (![allKeys containsObject:@"playerGesturesSensitivity"]) { if (![allKeys containsObject:@"playerGesturesSensitivity"]) {
[[NSUserDefaults standardUserDefaults] setFloat:1.0 forKey:@"playerGesturesSensitivity"]; [[NSUserDefaults standardUserDefaults] setFloat:1.0 forKey:@"playerGesturesSensitivity"];
} }
if (![allKeys containsObject:@"playerGesturesHapticFeedback_enabled"]) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"playerGesturesHapticFeedback_enabled"];
}
} }

View File

@@ -31,6 +31,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "خيارات تراكب ضوابط الفيديو"; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "خيارات تراكب ضوابط الفيديو";

View File

@@ -31,6 +31,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Опции за контрол на видеото"; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Опции за контрол на видеото";

View File

@@ -31,6 +31,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Overlay-Optionen für Videosteuerungen"; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Overlay-Optionen für Videosteuerungen";

View File

@@ -31,6 +31,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Controls Overlay Options"; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Controls Overlay Options";

View File

@@ -31,6 +31,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Opciones de superposición de controles de vídeo"; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Opciones de superposición de controles de vídeo";

View File

@@ -31,6 +31,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Options de l'overlay des contrôles vidéo"; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Options de l'overlay des contrôles vidéo";

View File

@@ -31,6 +31,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "動画コントロールオーバーレイの設定"; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "動画コントロールオーバーレイの設定";

View File

@@ -31,6 +31,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Opções de Sobreposição de Controles de Vídeo"; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Opções de Sobreposição de Controles de Vídeo";

View File

@@ -31,6 +31,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Opțiuni Overlay Controale Video"; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Opțiuni Overlay Controale Video";

View File

@@ -31,6 +31,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Controls Overlay Options"; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Controls Overlay Options";

View File

@@ -46,6 +46,7 @@ https://github.com/PoomSmart/Return-YouTube-Dislikes/tree/main/layout/Library/Ap
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Controls Overlay Options"; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Controls Overlay Options";

View File

@@ -31,6 +31,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Kontrol Seç."; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Kontrol Seç.";

View File

@@ -31,6 +31,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video player options // Video player options
"VIDEO_PLAYER_OPTIONS" = "Tùy chọn trình phát video"; "VIDEO_PLAYER_OPTIONS" = "Tùy chọn trình phát video";

View File

@@ -32,6 +32,7 @@
"TOP_SECTION" = "Top Section"; "TOP_SECTION" = "Top Section";
"MIDDLE_SECTION" = "Middle Section"; "MIDDLE_SECTION" = "Middle Section";
"BOTTOM_SECTION" = "Bottom Section"; "BOTTOM_SECTION" = "Bottom Section";
"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback";
// Video controls overlay options // Video controls overlay options
"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "影片區覆蓋按鈕設定"; "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "影片區覆蓋按鈕設定";