Files
YTLitePlus/Tweaks/YouMute/Tweak.x
2023-06-27 09:54:41 +02:00

321 lines
11 KiB
Plaintext

#import <rootless.h>
#import "../YouTubeHeader/YTColor.h"
#import "../YouTubeHeader/YTCommonUtils.h"
#import "../YouTubeHeader/YTInlinePlayerBarContainerView.h"
#import "../YouTubeHeader/YTMainAppControlsOverlayView.h"
#import "../YouTubeHeader/YTMainAppVideoPlayerOverlayViewController.h"
#import "../YouTubeHeader/YTSingleVideoController.h"
#import "../YouTubeHeader/YTSettingsPickerViewController.h"
#import "../YouTubeHeader/YTSettingsViewController.h"
#import "../YouTubeHeader/YTSettingsSectionItem.h"
#import "../YouTubeHeader/YTSettingsSectionItemManager.h"
#import "../YouTubeHeader/YTQTMButton.h"
#import "../YouTubeHeader/QTMIcon.h"
#import "../YouTubeHeader/UIView+YouTube.h"
#define EnabledKey @"YouMuteEnabled"
#define PositionKey @"YouMutePosition"
#define LOC(x) [tweakBundle localizedStringForKey:x value:nil table:nil]
static const NSInteger YouMuteSection = 521;
@interface YTSettingsSectionItemManager (YouMute)
- (void)updateYouMuteSectionWithEntry:(id)entry;
@end
@interface YTMainAppControlsOverlayView (YouMute)
@property (retain, nonatomic) YTQTMButton *muteButton;
- (void)didPressMute:(id)arg;
@end
@interface YTInlinePlayerBarContainerView (YouMute)
@property (retain, nonatomic) YTQTMButton *muteButton;
- (void)didPressMute:(id)arg;
@end
static BOOL TweakEnabled() {
return [[NSUserDefaults standardUserDefaults] boolForKey:EnabledKey];
}
static int MuteButtonPosition() {
return [[NSUserDefaults standardUserDefaults] integerForKey:PositionKey];
}
static BOOL UseTopMuteButton() {
return TweakEnabled() && MuteButtonPosition() == 0;
}
static BOOL UseBottomMuteButton() {
return TweakEnabled() && MuteButtonPosition() == 1;
}
static BOOL isMutedTop(YTMainAppControlsOverlayView *self) {
YTMainAppVideoPlayerOverlayViewController *c = [self valueForKey:@"_eventsDelegate"];
YTSingleVideoController *video = [c valueForKey:@"_currentSingleVideoObservable"];
return [video isMuted];
}
static BOOL isMutedBottom(YTInlinePlayerBarContainerView *self) {
YTSingleVideoController *video = [self.delegate valueForKey:@"_currentSingleVideo"];
return [video isMuted];
}
static NSBundle *YTEditResourcesBundle() {
Class YTCommonUtilsClass = %c(YTCommonUtils);
return [YTCommonUtilsClass resourceBundleForModuleName:@"Edit" appBundle:[YTCommonUtilsClass bundleForClass:%c(YTEditBundleIdentifier)]];
}
static UIImage *muteImage(BOOL muted) {
return [%c(QTMIcon) tintImage:[UIImage imageNamed:muted ? @"ic_volume_off" : @"ic_volume_up" inBundle:YTEditResourcesBundle() compatibleWithTraitCollection:nil] color:[%c(YTColor) white1]];
}
static void createMuteButtonTop(YTMainAppControlsOverlayView *self) {
if (!self) return;
CGFloat padding = [[self class] topButtonAdditionalPadding];
UIImage *image = muteImage(isMutedTop(self));
self.muteButton = [self buttonWithImage:image accessibilityLabel:@"Mute" verticalContentPadding:padding];
self.muteButton.hidden = YES;
self.muteButton.alpha = 0;
[self.muteButton addTarget:self action:@selector(didPressMute:) forControlEvents:UIControlEventTouchUpInside];
@try {
[[self valueForKey:@"_topControlsAccessibilityContainerView"] addSubview:self.muteButton];
} @catch (id ex) {
[self addSubview:self.muteButton];
}
}
static void createMuteButtonBottom(YTInlinePlayerBarContainerView *self) {
if (!self) return;
UIImage *image = muteImage(isMutedBottom(self));
self.muteButton = [%c(YTQTMButton) iconButton];
self.muteButton.hidden = YES;
self.muteButton.exclusiveTouch = YES;
self.muteButton.alpha = 0;
self.muteButton.minHitTargetSize = 60;
self.muteButton.accessibilityLabel = @"Mute";
[self.muteButton setImage:image forState:0];
[self.muteButton sizeToFit];
[self.muteButton addTarget:self action:@selector(didPressMute:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.muteButton];
}
static NSMutableArray *topControls(YTMainAppControlsOverlayView *self, NSMutableArray *controls) {
if (UseTopMuteButton())
[controls insertObject:self.muteButton atIndex:0];
return controls;
}
NSBundle *YouMuteBundle() {
static NSBundle *bundle = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *tweakBundlePath = [[NSBundle mainBundle] pathForResource:@"YouMute" ofType:@"bundle"];
if (tweakBundlePath)
bundle = [NSBundle bundleWithPath:tweakBundlePath];
else
bundle = [NSBundle bundleWithPath:ROOT_PATH_NS(@"/Library/Application Support/YouMute.bundle")];
});
return bundle;
}
%group Top
%hook YTMainAppVideoPlayerOverlayViewController
- (void)updateTopRightButtonAvailability {
%orig;
YTMainAppVideoPlayerOverlayView *v = [self videoPlayerOverlayView];
YTMainAppControlsOverlayView *c = [v valueForKey:@"_controlsOverlayView"];
c.muteButton.hidden = !UseTopMuteButton();
[c setNeedsLayout];
}
%end
%hook YTMainAppControlsOverlayView
%property (retain, nonatomic) YTQTMButton *muteButton;
- (id)initWithDelegate:(id)delegate {
self = %orig;
createMuteButtonTop(self);
return self;
}
- (id)initWithDelegate:(id)delegate autoplaySwitchEnabled:(BOOL)autoplaySwitchEnabled {
self = %orig;
createMuteButtonTop(self);
return self;
}
- (NSMutableArray *)topButtonControls {
return topControls(self, %orig);
}
- (NSMutableArray *)topControls {
return topControls(self, %orig);
}
- (void)setTopOverlayVisible:(BOOL)visible isAutonavCanceledState:(BOOL)canceledState {
if (UseTopMuteButton())
self.muteButton.alpha = canceledState || !visible ? 0.0 : 1.0;
%orig;
}
%new(v@:@)
- (void)didPressMute:(id)arg {
YTMainAppVideoPlayerOverlayViewController *c = [self valueForKey:@"_eventsDelegate"];
YTSingleVideoController *video = [c valueForKey:@"_currentSingleVideoObservable"];
[video setMuted:![video isMuted]];
[self.muteButton setImage:muteImage([video isMuted]) forState:0];
}
%end
%end
%group Bottom
%hook YTInlinePlayerBarContainerView
%property (retain, nonatomic) YTQTMButton *muteButton;
- (id)init {
self = %orig;
createMuteButtonBottom(self);
return self;
}
- (NSMutableArray *)rightIcons {
NSMutableArray *icons = %orig;
if (UseBottomMuteButton() && ![icons containsObject:self.muteButton])
[icons insertObject:self.muteButton atIndex:0];
return icons;
}
- (void)updateIconVisibility {
%orig;
if (UseBottomMuteButton())
self.muteButton.hidden = NO;
}
- (void)hideScrubber {
%orig;
if (UseBottomMuteButton())
self.muteButton.alpha = 0;
}
- (void)setPeekableViewVisible:(BOOL)visible fullscreenButtonVisibleShouldMatchPeekableView:(BOOL)match {
%orig;
if (UseBottomMuteButton())
self.muteButton.alpha = visible ? 1 : 0;
}
- (void)layoutSubviews {
%orig;
if (!UseBottomMuteButton()) return;
CGFloat multiFeedWidth = [self respondsToSelector:@selector(multiFeedElementView)] ? [self multiFeedElementView].frame.size.width : 0;
YTQTMButton *enter = [self enterFullscreenButton];
if ([enter yt_isVisible]) {
CGRect frame = enter.frame;
frame.origin.x -= multiFeedWidth + enter.frame.size.width + 16;
self.muteButton.frame = frame;
} else {
YTQTMButton *exit = [self exitFullscreenButton];
if ([exit yt_isVisible]) {
CGRect frame = exit.frame;
frame.origin.x -= multiFeedWidth + exit.frame.size.width + 16;
self.muteButton.frame = frame;
}
}
}
%new(v@:@)
- (void)didPressMute:(id)arg {
YTSingleVideoController *video = [self.delegate valueForKey:@"_currentSingleVideo"];
[video setMuted:![video isMuted]];
[self.muteButton setImage:muteImage([video isMuted]) forState:0];
}
%end
%end
%group Settings
%hook YTAppSettingsPresentationData
+ (NSArray *)settingsCategoryOrder {
NSArray *order = %orig;
NSMutableArray *mutableOrder = [order mutableCopy];
NSUInteger insertIndex = [order indexOfObject:@(1)];
if (insertIndex != NSNotFound)
[mutableOrder insertObject:@(YouMuteSection) atIndex:insertIndex + 1];
return mutableOrder;
}
%end
%hook YTSettingsSectionItemManager
%new(v@:@)
- (void)updateYouMuteSectionWithEntry:(id)entry {
NSMutableArray *sectionItems = [NSMutableArray array];
NSBundle *tweakBundle = YouMuteBundle();
Class YTSettingsSectionItemClass = %c(YTSettingsSectionItem);
YTSettingsViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"];
YTSettingsSectionItem *master = [YTSettingsSectionItemClass switchItemWithTitle:LOC(@"ENABLED")
titleDescription:nil
accessibilityIdentifier:nil
switchOn:TweakEnabled()
switchBlock:^BOOL (YTSettingsCell *cell, BOOL enabled) {
[[NSUserDefaults standardUserDefaults] setBool:enabled forKey:EnabledKey];
return YES;
}
settingItemId:0];
[sectionItems addObject:master];
YTSettingsSectionItem *position = [YTSettingsSectionItemClass itemWithTitle:LOC(@"POSITION")
accessibilityIdentifier:nil
detailTextBlock:^NSString *() {
return MuteButtonPosition() ? LOC(@"BOTTOM") : LOC(@"TOP");
}
selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
NSArray <YTSettingsSectionItem *> *rows = @[
[YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"TOP") titleDescription:LOC(@"TOP_DESC") selectBlock:^BOOL (YTSettingsCell *top, NSUInteger arg1) {
[[NSUserDefaults standardUserDefaults] setInteger:0 forKey:PositionKey];
[settingsViewController reloadData];
return YES;
}],
[YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"BOTTOM") titleDescription:LOC(@"BOTTOM_DESC") selectBlock:^BOOL (YTSettingsCell *bottom, NSUInteger arg1) {
[[NSUserDefaults standardUserDefaults] setInteger:1 forKey:PositionKey];
[settingsViewController reloadData];
return YES;
}]
];
YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"POSITION") pickerSectionTitle:nil rows:rows selectedItemIndex:MuteButtonPosition() parentResponder:[self parentResponder]];
[settingsViewController pushViewController:picker];
return YES;
}];
[sectionItems addObject:position];
[settingsViewController setSectionItems:sectionItems forCategory:YouMuteSection title:@"YouMute" titleDescription:nil headerHidden:NO];
}
- (void)updateSectionForCategory:(NSUInteger)category withEntry:(id)entry {
if (category == YouMuteSection) {
[self updateYouMuteSectionWithEntry:entry];
return;
}
%orig;
}
%end
%end
%ctor {
%init(Settings);
%init(Top);
%init(Bottom);
}