Files
2023-11-11 17:38:58 +07:00

165 lines
5.8 KiB
Objective-C

#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#import "LauncherPreferences.h"
#import "UIKit+hook.h"
#import "utils.h"
void swizzle(Class class, SEL originalAction, SEL swizzledAction) {
method_exchangeImplementations(class_getInstanceMethod(class, originalAction), class_getInstanceMethod(class, swizzledAction));
}
void swizzleClass(Class class, SEL originalAction, SEL swizzledAction) {
method_exchangeImplementations(class_getClassMethod(class, originalAction), class_getClassMethod(class, swizzledAction));
}
void init_hookUIKitConstructor(void) {
swizzle(UIDevice.class, @selector(userInterfaceIdiom), @selector(hook_userInterfaceIdiom));
swizzle(UIImageView.class, @selector(setImage:), @selector(hook_setImage:));
if (realUIIdiom == UIUserInterfaceIdiomTV) {
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// If you are about to test iPadOS idiom on tvOS, there's no better way for this
class_setSuperclass(NSClassFromString(@"UITableConstants_Pad"), NSClassFromString(@"UITableConstants_TV"));
#pragma clang diagnostic pop
}
swizzle(UINavigationController.class, @selector(toolbar), @selector(hook_toolbar));
swizzle(UINavigationController.class, @selector(setToolbar:), @selector(hook_setToolbar:));
swizzleClass(UISwitch.class, @selector(visualElementForTraitCollection:), @selector(hook_visualElementForTraitCollection:));
}
}
@implementation UIDevice(hook)
- (NSString *)completeOSVersion {
return [NSString stringWithFormat:@"%@ %@ (%@)", self.systemName, self.systemVersion, self.buildVersion];
}
- (UIUserInterfaceIdiom)hook_userInterfaceIdiom {
if (getPrefBool(@"debug.debug_ipad_ui")) {
return UIUserInterfaceIdiomPad;
} else if (self.hook_userInterfaceIdiom == UIUserInterfaceIdiomTV) {
return self.hook_userInterfaceIdiom;
} else {
return UIUserInterfaceIdiomPhone;
}
}
@end
// Patch: emulate scaleToFill for table views
@implementation UIImageView(hook)
- (BOOL)isSizeFixed {
return [objc_getAssociatedObject(self, @selector(isSizeFixed)) boolValue];
}
- (void)setIsSizeFixed:(BOOL)fixed {
objc_setAssociatedObject(self, @selector(isSizeFixed), @(fixed), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)hook_setImage:(UIImage *)image {
if (self.isSizeFixed) {
UIImage *resizedImage = [image _imageWithSize:self.frame.size];
[self hook_setImage:resizedImage];
} else {
[self hook_setImage:image];
}
}
@end
// Patch: unimplemented get/set UIToolbar functions on tvOS
@implementation UINavigationController(hook)
- (UIToolbar *)hook_toolbar {
UIToolbar *toolbar = objc_getAssociatedObject(self, @selector(toolbar));
if (toolbar == nil) {
toolbar = [[UIToolbar alloc] initWithFrame:
CGRectMake(self.view.bounds.origin.x, self.view.bounds.size.height - 100,
self.view.bounds.size.width, 100)];
toolbar.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
toolbar.backgroundColor = UIColor.systemBackgroundColor;
objc_setAssociatedObject(self, @selector(toolbar), toolbar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self performSelector:@selector(_configureToolbar)];
}
return toolbar;
}
- (void)hook_setToolbar:(UIToolbar *)toolbar {
objc_setAssociatedObject(self, @selector(toolbar), toolbar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
// Patch: UISwitch crashes if platform == tvOS
@implementation UISwitch(hook)
+ (id)hook_visualElementForTraitCollection:(UITraitCollection *)collection {
if (collection.userInterfaceIdiom == UIUserInterfaceIdiomTV) {
UITraitCollection *override = [UITraitCollection traitCollectionWithUserInterfaceIdiom:UIUserInterfaceIdiomPad];
UITraitCollection *new = [UITraitCollection traitCollectionWithTraitsFromCollections:@[collection, override]];
return [self hook_visualElementForTraitCollection:new];
}
return [self hook_visualElementForTraitCollection:collection];
}
@end
@implementation UITraitCollection(hook)
- (UIUserInterfaceSizeClass)horizontalSizeClass {
return UIUserInterfaceSizeClassRegular;
}
- (UIUserInterfaceSizeClass)verticalSizeClass {
return UIUserInterfaceSizeClassRegular;
}
@end
@implementation UIWindow(hook)
- (UIViewController *)visibleViewController {
UIViewController *current = self.rootViewController;
while (current.presentedViewController) {
if ([current.presentedViewController isKindOfClass:UIAlertController.class] || [current.presentedViewController isKindOfClass:NSClassFromString(@"UIInputWindowController")]) {
break;
}
current = current.presentedViewController;
}
if ([current isKindOfClass:UINavigationController.class]) {
return [(UINavigationController *)self.rootViewController visibleViewController];
} else {
return current;
}
}
@end
// This forces the navigation bar to keep its height (44dp) in landscape
@implementation UINavigationBar(forceFullHeightInLandscape)
- (BOOL)forceFullHeightInLandscape {
return YES;
//UIScreen.mainScreen.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiomPhone;
}
@end
UIWindow* currentWindowInScene(BOOL external) {
id delegate = UIApplication.sharedApplication.delegate;
for (UIScene *scene in UIApplication.sharedApplication.connectedScenes.allObjects) {
delegate = scene.delegate;
if (external != (scene.session.role == UIWindowSceneSessionRoleApplication)) {
break;
}
}
return [delegate window];
}
UIWindow* currentWindow() {
return currentWindowInScene(0);
}
UIViewController* currentVC() {
return currentWindow().visibleViewController;
}