mirror of
				https://github.com/SoPat712/YTLitePlus.git
				synced 2025-10-30 12:33:57 -04:00 
			
		
		
		
	Merge pull request #377 from bhackel/gesture-improvement
Gesture improvement
This commit is contained in:
		| @@ -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]; | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								YTLitePlus.h
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								YTLitePlus.h
									
									
									
									
									
								
							| @@ -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) | ||||||
|   | |||||||
							
								
								
									
										291
									
								
								YTLitePlus.xm
									
									
									
									
									
								
							
							
						
						
									
										291
									
								
								YTLitePlus.xm
									
									
									
									
									
								
							| @@ -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 | ||||||
|  |                 if (IS_ENABLED(@"playerGesturesHapticFeedback_enabled")) { | ||||||
|                     [feedbackGenerator prepare]; |                     [feedbackGenerator prepare]; | ||||||
|                     [feedbackGenerator impactOccurred]; |                     [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; | ||||||
|  |                 case GestureSectionBottom: | ||||||
|  |                     runSelectedGestureChanged(@"playerGestureBottomSelection"); | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|                     // If the section is invalid, cancel the gesture |                     // If the section is invalid, cancel the gesture | ||||||
|                     panGestureRecognizer.state = UIGestureRecognizerStateCancelled; |                     panGestureRecognizer.state = UIGestureRecognizerStateCancelled; | ||||||
|  |                     break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) { |  | ||||||
|         if (isValidHorizontalPan) { |     // Handle the gesture end state by running the selected gesture mode's end action | ||||||
|  |     if (panGestureRecognizer.state == UIGestureRecognizerStateEnded && isValidHorizontalPan) { | ||||||
|  |         switch (gestureSection) { | ||||||
|  |             case GestureSectionTop: | ||||||
|  |                 runSelectedGestureEnded(@"playerGestureTopSelection"); | ||||||
|  |                 break; | ||||||
|  |             case GestureSectionMiddle: | ||||||
|  |                 runSelectedGestureEnded(@"playerGestureMiddleSelection"); | ||||||
|  |                 break; | ||||||
|  |             case GestureSectionBottom: | ||||||
|  |                 runSelectedGestureEnded(@"playerGestureBottomSelection"); | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|         // Provide haptic feedback upon successful gesture recognition |         // Provide haptic feedback upon successful gesture recognition | ||||||
|             [feedbackGenerator prepare]; |         // [feedbackGenerator prepare]; | ||||||
|             [feedbackGenerator impactOccurred]; |         // [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"];  | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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" = "خيارات تراكب ضوابط الفيديو"; | ||||||
|   | |||||||
| @@ -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" = "Опции за контрол на видеото"; | ||||||
|   | |||||||
| @@ -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"; | ||||||
|   | |||||||
| @@ -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"; | ||||||
|   | |||||||
| @@ -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"; | ||||||
|   | |||||||
| @@ -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"; | ||||||
|   | |||||||
| @@ -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" = "動画コントロールオーバーレイの設定"; | ||||||
|   | |||||||
| @@ -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"; | ||||||
|   | |||||||
| @@ -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"; | ||||||
|   | |||||||
| @@ -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"; | ||||||
|   | |||||||
| @@ -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"; | ||||||
|   | |||||||
| @@ -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ç."; | ||||||
|   | |||||||
| @@ -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"; | ||||||
|   | |||||||
| @@ -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" = "影片區覆蓋按鈕設定"; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Bryce Hackel
					Bryce Hackel