From 7c8a5320ba22a0c5d884c156dad0223da218276f Mon Sep 17 00:00:00 2001 From: Huge_Black Date: Sun, 18 Aug 2024 10:45:04 +0800 Subject: [PATCH 1/7] user can input url --- LiveContainerUI/LCAppInfo.h | 3 ++- LiveContainerUI/LCAppInfo.m | 21 ++++++++++++++- LiveContainerUI/LCAppListViewController.m | 31 +++++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/LiveContainerUI/LCAppInfo.h b/LiveContainerUI/LCAppInfo.h index c143acf..f4a76b0 100644 --- a/LiveContainerUI/LCAppInfo.h +++ b/LiveContainerUI/LCAppInfo.h @@ -14,9 +14,10 @@ - (NSString*)version; - (NSString*)dataUUID; - (NSString*)tweakFolder; +@property NSMutableArray* urlSchemes; - (void)setDataUUID:(NSString *)uuid; - (void)setTweakFolder:(NSString *)tweakFolder; - (instancetype)initWithBundlePath:(NSString*)bundlePath; - (NSDictionary *)generateWebClipConfig; - (void)save; -@end \ No newline at end of file +@end diff --git a/LiveContainerUI/LCAppInfo.m b/LiveContainerUI/LCAppInfo.m index 68e0523..3bf1103 100644 --- a/LiveContainerUI/LCAppInfo.m +++ b/LiveContainerUI/LCAppInfo.m @@ -5,9 +5,28 @@ @implementation LCAppInfo - (instancetype)initWithBundlePath:(NSString*)bundlePath { self = [super init]; + self.urlSchemes = [[NSMutableArray alloc] init]; if(self) { _bundlePath = bundlePath; _info = [NSMutableDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Info.plist", bundlePath]]; + + // find all url schemes + int nowSchemeCount = 0; + if (_info[@"CFBundleURLTypes"]) { + NSMutableArray* urlTypes = _info[@"CFBundleURLTypes"]; + + for(int i = 0; i < [urlTypes count]; ++i) { + NSMutableDictionary* nowUrlType = [urlTypes objectAtIndex:i]; + if (!nowUrlType[@"CFBundleURLSchemes"]){ + continue; + } + NSMutableArray *schemes = nowUrlType[@"CFBundleURLSchemes"]; + for(int j = 0; j < [schemes count]; ++j) { + [self.urlSchemes insertObject:[schemes objectAtIndex:j] atIndex:nowSchemeCount]; + ++nowSchemeCount; + } + } + } } return self; } @@ -127,4 +146,4 @@ - (NSDictionary *)generateWebClipConfig { - (void)save { [_info writeToFile:[NSString stringWithFormat:@"%@/Info.plist", _bundlePath] atomically:YES]; } -@end \ No newline at end of file +@end diff --git a/LiveContainerUI/LCAppListViewController.m b/LiveContainerUI/LCAppListViewController.m index 08caf03..48848da 100644 --- a/LiveContainerUI/LCAppListViewController.m +++ b/LiveContainerUI/LCAppListViewController.m @@ -24,6 +24,8 @@ @interface LCAppListViewController () @property(nonatomic) NSString *bundlePath, *docPath, *tweakPath; @property(nonatomic) MBRoundProgressView *progressView; + +@property(nonatomic) UIButton *openLinkButton; @end @implementation LCAppListViewController @@ -56,6 +58,16 @@ - (void)loadView { [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay target:self action:@selector(launchButtonTapped)], [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addButtonTapped)] ]; + + UIButton* openLinkButton = [UIButton buttonWithType:UIButtonTypeCustom]; + openLinkButton.enabled = !!LCUtils.certificatePassword; + openLinkButton.frame = CGRectMake(0, 0, 40, 40); + [openLinkButton setImage:[UIImage systemImageNamed:@"link"] forState:UIControlStateNormal]; + [openLinkButton addTarget:self action:@selector(openUrlButtonTapped) forControlEvents:UIControlEventTouchUpInside]; + + self.navigationItem.leftBarButtonItems = @[ + [[UIBarButtonItem alloc] initWithCustomView:openLinkButton] + ]; self.progressView = [[MBRoundProgressView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)]; } @@ -615,4 +627,23 @@ - (UIMenu *)destructiveActionWithTitle:(NSString *)title image:(UIImage *)image return menu; } + +- (void) openUrlButtonTapped { + [self showInputDialogTitle:@"Input URL" message:@"Input URL scheme or URL to a web page" placeholder:@"scheme://" callback:^(NSString *ans) { + NSLog(@"[LiveContainer] got url: %@", ans); + NSURL* url = [NSURL URLWithString: ans]; + dispatch_async(dispatch_get_main_queue(), ^(void){ + if(!url) { + [self showDialogTitle:@"Invalid URL" message:@"The given URL is invalid. Check it and try again."]; + } else { + [self showDialogTitle:@"Valid URL" message: @"Good."]; + } + }); + return (NSString*)nil; + }]; + + + +} + @end From b443e88c0ed6a2544a4b24573765088feb5a370e Mon Sep 17 00:00:00 2001 From: Huge_Black Date: Sun, 18 Aug 2024 21:57:25 +0800 Subject: [PATCH 2/7] almost working --- LiveContainerUI/LCAppInfo.h | 4 +- LiveContainerUI/LCAppInfo.m | 44 ++++--- LiveContainerUI/LCAppListViewController.m | 61 ++++++++- LiveContainerUI/LCWebView.h | 11 ++ LiveContainerUI/LCWebview.m | 150 ++++++++++++++++++++++ LiveContainerUI/Makefile | 2 +- TweakLoader/UIKit+GuestHooks.m | 25 +++- UIKitPrivate.h | 1 + main.m | 16 +++ 9 files changed, 288 insertions(+), 26 deletions(-) create mode 100644 LiveContainerUI/LCWebView.h create mode 100644 LiveContainerUI/LCWebview.m diff --git a/LiveContainerUI/LCAppInfo.h b/LiveContainerUI/LCAppInfo.h index f4a76b0..145c775 100644 --- a/LiveContainerUI/LCAppInfo.h +++ b/LiveContainerUI/LCAppInfo.h @@ -5,7 +5,7 @@ NSMutableDictionary* _info; NSString* _bundlePath; } - +@property NSString* relativeBundlePath; - (NSMutableDictionary*)info; - (UIImage*)icon; - (NSString*)displayName; @@ -14,7 +14,7 @@ - (NSString*)version; - (NSString*)dataUUID; - (NSString*)tweakFolder; -@property NSMutableArray* urlSchemes; +- (NSMutableArray*) urlSchemes; - (void)setDataUUID:(NSString *)uuid; - (void)setTweakFolder:(NSString *)tweakFolder; - (instancetype)initWithBundlePath:(NSString*)bundlePath; diff --git a/LiveContainerUI/LCAppInfo.m b/LiveContainerUI/LCAppInfo.m index 3bf1103..cd6f85b 100644 --- a/LiveContainerUI/LCAppInfo.m +++ b/LiveContainerUI/LCAppInfo.m @@ -5,32 +5,38 @@ @implementation LCAppInfo - (instancetype)initWithBundlePath:(NSString*)bundlePath { self = [super init]; - self.urlSchemes = [[NSMutableArray alloc] init]; + if(self) { _bundlePath = bundlePath; _info = [NSMutableDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Info.plist", bundlePath]]; - - // find all url schemes - int nowSchemeCount = 0; - if (_info[@"CFBundleURLTypes"]) { - NSMutableArray* urlTypes = _info[@"CFBundleURLTypes"]; - - for(int i = 0; i < [urlTypes count]; ++i) { - NSMutableDictionary* nowUrlType = [urlTypes objectAtIndex:i]; - if (!nowUrlType[@"CFBundleURLSchemes"]){ - continue; - } - NSMutableArray *schemes = nowUrlType[@"CFBundleURLSchemes"]; - for(int j = 0; j < [schemes count]; ++j) { - [self.urlSchemes insertObject:[schemes objectAtIndex:j] atIndex:nowSchemeCount]; - ++nowSchemeCount; - } - } - } + } return self; } +- (NSMutableArray*)urlSchemes { + // find all url schemes + NSMutableArray* urlSchemes = [[NSMutableArray alloc] init]; + int nowSchemeCount = 0; + if (_info[@"CFBundleURLTypes"]) { + NSMutableArray* urlTypes = _info[@"CFBundleURLTypes"]; + + for(int i = 0; i < [urlTypes count]; ++i) { + NSMutableDictionary* nowUrlType = [urlTypes objectAtIndex:i]; + if (!nowUrlType[@"CFBundleURLSchemes"]){ + continue; + } + NSMutableArray *schemes = nowUrlType[@"CFBundleURLSchemes"]; + for(int j = 0; j < [schemes count]; ++j) { + [urlSchemes insertObject:[schemes objectAtIndex:j] atIndex:nowSchemeCount]; + ++nowSchemeCount; + } + } + } + + return urlSchemes; +} + - (NSString*)displayName { if (_info[@"CFBundleDisplayName"]) { return _info[@"CFBundleDisplayName"]; diff --git a/LiveContainerUI/LCAppListViewController.m b/LiveContainerUI/LCAppListViewController.m index 48848da..5e09fc1 100644 --- a/LiveContainerUI/LCAppListViewController.m +++ b/LiveContainerUI/LCAppListViewController.m @@ -11,6 +11,7 @@ #import "UIKitPrivate.h" #import "UIViewController+LCAlert.h" #import "unarchive.h" +#import "LCWebView.h" @implementation NSURL(hack) - (BOOL)safari_isHTTPFamilyURL { @@ -25,7 +26,6 @@ @interface LCAppListViewController () @property(nonatomic) MBRoundProgressView *progressView; -@property(nonatomic) UIButton *openLinkButton; @end @implementation LCAppListViewController @@ -629,14 +629,33 @@ - (UIMenu *)destructiveActionWithTitle:(NSString *)title image:(UIImage *)image - (void) openUrlButtonTapped { + NSMutableArray* apps = [[NSMutableArray alloc] init]; + for(int i = 0; i < [self.objects count]; ++i) { + LCAppInfo* appInfo = [[LCAppInfo alloc] initWithBundlePath: [NSString stringWithFormat:@"%@/%@", self.bundlePath, self.objects[i]]]; + appInfo.relativeBundlePath = self.objects[i]; + [apps insertObject:appInfo atIndex:i]; + } + [self showInputDialogTitle:@"Input URL" message:@"Input URL scheme or URL to a web page" placeholder:@"scheme://" callback:^(NSString *ans) { NSLog(@"[LiveContainer] got url: %@", ans); - NSURL* url = [NSURL URLWithString: ans]; + NSURLComponents* url = [[NSURLComponents alloc] initWithString:ans]; + dispatch_async(dispatch_get_main_queue(), ^(void){ if(!url) { [self showDialogTitle:@"Invalid URL" message:@"The given URL is invalid. Check it and try again."]; } else { - [self showDialogTitle:@"Valid URL" message: @"Good."]; + // use https for http and empty scheme + if([url.scheme length ] == 0 || [url.scheme isEqualToString:@"http"]) { + url.scheme = @"https"; + } else if (![url.scheme isEqualToString: @"https"]){ + [self launchAppByScheme:url apps:apps]; + } + LCWebView *webViewController = [[LCWebView alloc] initWithURL:url.URL apps:apps]; + UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:webViewController]; + navController.navigationBar.translucent = NO; + + navController.modalPresentationStyle = UIModalPresentationFullScreen; + [self presentViewController:navController animated:YES completion:nil]; } }); return (NSString*)nil; @@ -646,4 +665,40 @@ - (void) openUrlButtonTapped { } +- (void) launchAppByScheme:(NSURLComponents*)schemeURL apps:(NSMutableArray*)apps { + // find app + NSString* appId = nil; + for(int i = 0; i < [apps count] && !appId; ++i) { + LCAppInfo* nowApp = apps[i]; + NSMutableArray* schemes = [nowApp urlSchemes]; + if(!schemes) continue; + for(int j = 0; j < [schemes count]; ++j) { + NSString* nowScheme = schemes[j]; + if([nowScheme isEqualToString:schemeURL.scheme]) { + appId = [nowApp relativeBundlePath]; + break; + } + } + } + if (!appId) { + [self showDialogTitle:@"Invalid Scheme" message:@"LiveContainer cannot find an app that supports this scheme."]; + return; + } + + + NSString* message = [NSString stringWithFormat:@"You are about to launch %@, continue?", appId]; + UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"LiveContainer" message:message preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { + [NSUserDefaults.standardUserDefaults setObject:appId forKey:@"selected"]; + [NSUserDefaults.standardUserDefaults setObject:schemeURL.string forKey:@"launchAppUrlScheme"]; + if ([LCUtils launchToGuestApp]) return; + }]; + [alert addAction:okAction]; + UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { + + }]; + [alert addAction:cancelAction]; + [self presentViewController:alert animated:YES completion:nil]; +} + @end diff --git a/LiveContainerUI/LCWebView.h b/LiveContainerUI/LCWebView.h new file mode 100644 index 0000000..a977783 --- /dev/null +++ b/LiveContainerUI/LCWebView.h @@ -0,0 +1,11 @@ +#import +#import +#import "LCAppInfo.h" + +@interface LCWebView : UIViewController +- (instancetype)initWithURL:(NSURL *)url apps:(NSMutableArray*)apps; +- (void)askIfLaunchApp:(NSString*)appId url:(NSURL*)launchUrl; +@property (nonatomic) NSURL *url; +@property (nonatomic) NSMutableArray* apps; +@property (strong, nonatomic) WKWebView *webView; +@end diff --git a/LiveContainerUI/LCWebview.m b/LiveContainerUI/LCWebview.m new file mode 100644 index 0000000..c26aa0f --- /dev/null +++ b/LiveContainerUI/LCWebview.m @@ -0,0 +1,150 @@ +// +// LCWebview.m +// jump +// +// Created by s s on 2024/8/18. +// + + +#import "LCWebView.h" +#import "LCUtils.h" + +@interface MySchemeHandler : NSObject +- (instancetype)initWithApp:(NSString*)appId viewController:(LCWebView*)lcController; +@property NSString* appId; +@property LCWebView* lcController; +@end + +@implementation MySchemeHandler + +- (instancetype)initWithApp:(NSString*)appId viewController:(LCWebView*)lcController{ + self = [super init]; + self.appId = appId; + self.lcController = lcController; + return self; +} + +- (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id)urlSchemeTask { + [self.lcController askIfLaunchApp:self.appId url: urlSchemeTask.request.URL]; +} + +- (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id)urlSchemeTask { + NSLog(@"stopURLScheme"); +} + +@end + + + +@implementation LCWebView + +- (instancetype)initWithURL:(NSURL *)url apps:(NSMutableArray*)apps { + self = [super init]; // Call the superclass's init method + if (self) { + self.apps = apps; + self.url = url; // Store the URL string + + } + return self; +} + + +- (void)viewDidLoad { + [super viewDidLoad]; + + if (!self.webView.superview) { + WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; + + + CGRect webViewSize = self.view.bounds; + webViewSize.size.height -= self.navigationController.navigationBar.frame.size.height; + webViewSize.size.height -= [self.view.window.windowScene.statusBarManager statusBarFrame].size.height; + webViewSize.size.height -= 30; + self.webView = [[WKWebView alloc] initWithFrame:webViewSize configuration:config]; + self.webView.navigationDelegate = self; + self.webView.customUserAgent = @"Mozilla/5.0 (iPhone; CPU iPhone OS 17_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Mobile/15E148 Safari/604.1"; + [self.view addSubview:self.webView]; + } + UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"chevron.backward"] style:UIBarButtonItemStylePlain target:self action:@selector(goBack)]; + UIBarButtonItem *forwardButton = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"chevron.forward"] style:UIBarButtonItemStylePlain target:self action:@selector(goForward)]; + self.navigationItem.leftBarButtonItems = @[backButton, forwardButton]; + + // Add a refresh button on the right + UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadWebView)]; + UIBarButtonItem *closeButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(close)]; + self.navigationItem.rightBarButtonItems = @[closeButton, refreshButton]; + + // Load the webpage passed via the initializer + if (self.url) { + NSURLRequest *request = [NSURLRequest requestWithURL:self.url]; + [self.webView loadRequest:request]; + } +} + +- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { + self.title = webView.title; +} + +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + // use private API, get rid of Universal link + decisionHandler((WKNavigationActionPolicy)(WKNavigationActionPolicyAllow + 2)); + NSString* scheme = navigationAction.request.URL.scheme; + if([scheme length] == 0 || [scheme isEqualToString:@"https"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"about"] || [scheme isEqualToString:@"itms-appss"]) { + return; + } + // add a unique urlHandler for each app + LCAppInfo* appToOpen = nil; + for(int i = 0; i < [self.apps count] && !appToOpen; ++i) { + LCAppInfo* nowAppInfo = self.apps[i]; + NSMutableArray* schemes = [nowAppInfo urlSchemes]; + if(!schemes) continue; + for(int j = 0; j < [schemes count]; ++j) { + if([scheme isEqualToString:schemes[j]]) { + appToOpen = nowAppInfo; + break; + } + } + } + if(!appToOpen){ + return; + } + [self askIfLaunchApp:appToOpen.relativeBundlePath url:navigationAction.request.URL]; +} + +- (void)goBack { + if ([self.webView canGoBack]) { + [self.webView goBack]; + } +} + +- (void)goForward { + if ([self.webView canGoForward]) { + [self.webView goForward]; + } +} + +- (void)reloadWebView { + [self.webView reload]; +} + +- (void)close { + [self dismissViewControllerAnimated:YES completion:nil]; +} + +- (void)askIfLaunchApp:(NSString*)appId url:(NSURL*)launchUrl { + NSString* message = [NSString stringWithFormat:@"This web page is trying to launch %@, continue?", appId]; + UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"LiveContainer" message:message preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { + [NSUserDefaults.standardUserDefaults setObject:appId forKey:@"selected"]; + [NSUserDefaults.standardUserDefaults setObject:launchUrl.absoluteString forKey:@"launchAppUrlScheme"]; + if ([LCUtils launchToGuestApp]) return; + }]; + [alert addAction:okAction]; + UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { + + }]; + [alert addAction:cancelAction]; + [self presentViewController:alert animated:YES completion:nil]; +} + +@end diff --git a/LiveContainerUI/Makefile b/LiveContainerUI/Makefile index 527f565..10556a2 100644 --- a/LiveContainerUI/Makefile +++ b/LiveContainerUI/Makefile @@ -2,7 +2,7 @@ include $(THEOS)/makefiles/common.mk FRAMEWORK_NAME = LiveContainerUI -LiveContainerUI_FILES = LCAppDelegate.m LCJITLessSetupViewController.m LCMachOUtils.m LCAppListViewController.m LCSettingsListController.m LCTabBarController.m LCTweakListViewController.m LCUtils.m MBRoundProgressView.m UIViewController+LCAlert.m unarchive.m LCAppInfo.m +LiveContainerUI_FILES = LCAppDelegate.m LCJITLessSetupViewController.m LCMachOUtils.m LCAppListViewController.m LCSettingsListController.m LCTabBarController.m LCTweakListViewController.m LCUtils.m MBRoundProgressView.m UIViewController+LCAlert.m unarchive.m LCAppInfo.m LCWebView.m LiveContainerUI_CFLAGS = \ -fobjc-arc \ -DCONFIG_TYPE=\"$(CONFIG_TYPE)\" \ diff --git a/TweakLoader/UIKit+GuestHooks.m b/TweakLoader/UIKit+GuestHooks.m index 30ac378..425a45f 100644 --- a/TweakLoader/UIKit+GuestHooks.m +++ b/TweakLoader/UIKit+GuestHooks.m @@ -42,6 +42,18 @@ - (void)hook__applicationOpenURLAction:(id)action payload:(NSDictionary *)payloa if ([url hasPrefix:@"livecontainer://livecontainer-relaunch"]) { // Ignore return; + } else if ([url hasPrefix:@"livecontainer://open-url"]) { + // pass url to guest app + NSURLComponents* lcUrl = [NSURLComponents componentsWithString:url]; + NSString* realUrlEncoded = lcUrl.queryItems[0].value; + if(!realUrlEncoded) return; + // Convert the base64 encoded url into String + NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:realUrlEncoded options:0]; + NSString *decodedUrl = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; + NSMutableDictionary* newPayload = [payload mutableCopy]; + newPayload[UIApplicationLaunchOptionsURLKey] = decodedUrl; + [self hook__applicationOpenURLAction:action payload:newPayload origin:origin]; + return; } else if (![url hasPrefix:@"livecontainer://livecontainer-launch?"]) { // Not what we're looking for, pass it [self hook__applicationOpenURLAction:action payload:payload origin:origin]; @@ -74,7 +86,18 @@ - (void)hook_scene:(id)scene didReceiveActions:(NSSet *)actions fromTransitionCo // Ignore } else if (![url hasPrefix:@"livecontainer://livecontainer-launch?"]) { // Not what we're looking for, pass it - [self hook_scene:scene didReceiveActions:actions fromTransitionContext:context]; + NSURLComponents* lcUrl = [NSURLComponents componentsWithString:url]; + NSString* realUrlEncoded = lcUrl.queryItems[0].value; + if(!realUrlEncoded) return; + // Convert the base64 encoded url into String + NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:realUrlEncoded options:0]; + NSString *decodedUrl = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; + + NSMutableSet *newActions = actions.mutableCopy; + [newActions removeObject:urlAction]; + UIOpenURLAction *newUrlAction = [[UIOpenURLAction alloc] initWithURL:[NSURL URLWithString:decodedUrl]]; + [newActions addObject:newUrlAction]; + [self hook_scene:scene didReceiveActions:newActions fromTransitionContext:context]; return; } else if (![url hasSuffix:NSBundle.mainBundle.bundlePath.lastPathComponent]) { LCShowSwitchAppConfirmation(urlAction.url); diff --git a/UIKitPrivate.h b/UIKitPrivate.h index dc64b5e..2f99c5e 100644 --- a/UIKitPrivate.h +++ b/UIKitPrivate.h @@ -31,6 +31,7 @@ @interface UIOpenURLAction : NSObject - (NSURL *)url; +- (instancetype)initWithURL:(NSURL *)arg1; @end @interface UITableViewHeaderFooterView(private) diff --git a/main.m b/main.m index 2a4a372..3705399 100644 --- a/main.m +++ b/main.m @@ -310,7 +310,23 @@ int LiveContainerMain(int argc, char *argv[]) { lcUserDefaults = NSUserDefaults.standardUserDefaults; NSString *selectedApp = [lcUserDefaults stringForKey:@"selected"]; if (selectedApp) { + NSString *launchUrl = [lcUserDefaults stringForKey:@"launchAppUrlScheme"]; [lcUserDefaults removeObjectForKey:@"selected"]; + // wait for app to launch so that it can receive the url + if(launchUrl) { + [lcUserDefaults removeObjectForKey:@"launchAppUrlScheme"]; + dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)); + dispatch_after(delay, dispatch_get_main_queue(), ^{ + // Base64 encode the data + NSData *data = [launchUrl dataUsingEncoding:NSUTF8StringEncoding]; + NSString *encodedUrl = [data base64EncodedStringWithOptions:0]; + + NSString* finalUrl = [NSString stringWithFormat:@"livecontainer://open-url?url=%@", encodedUrl]; + NSURL* url = [NSURL URLWithString: finalUrl]; + + [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; + }); + } NSSetUncaughtExceptionHandler(&exceptionHandler); setenv("LC_HOME_PATH", getenv("HOME"), 1); NSString *appError = invokeAppMain(selectedApp, argc, argv); From 8cc56af27801a2a3d2d8f3a35794e8656547614c Mon Sep 17 00:00:00 2001 From: Huge_Black Date: Mon, 19 Aug 2024 00:29:45 +0800 Subject: [PATCH 3/7] allow open web page with url schemes. --- LCSharedUtils.h | 2 +- LCSharedUtils.m | 4 ++ LiveContainerUI/LCAppDelegate.m | 14 ++++++ LiveContainerUI/LCAppListViewController.h | 1 + LiveContainerUI/LCAppListViewController.m | 60 +++++++++++++---------- LiveContainerUI/LCTabBarController.h | 3 ++ LiveContainerUI/LCTabBarController.m | 6 ++- TweakLoader/UIKit+GuestHooks.m | 33 +++++++++++++ 8 files changed, 94 insertions(+), 29 deletions(-) diff --git a/LCSharedUtils.h b/LCSharedUtils.h index 437ae1e..74a4ef3 100644 --- a/LCSharedUtils.h +++ b/LCSharedUtils.h @@ -5,5 +5,5 @@ + (NSString *)certificatePassword; + (BOOL)launchToGuestApp; + (BOOL)launchToGuestAppWithURL:(NSURL *)url; - ++ (void)setWebPageUrlForNextLaunch:(NSString*)urlString; @end diff --git a/LCSharedUtils.m b/LCSharedUtils.m index 0697a0f..9a4af1a 100644 --- a/LCSharedUtils.m +++ b/LCSharedUtils.m @@ -49,4 +49,8 @@ + (BOOL)launchToGuestAppWithURL:(NSURL *)url { return NO; } ++ (void)setWebPageUrlForNextLaunch:(NSString*) urlString { + [lcUserDefaults setObject:urlString forKey:@"webPageToOpen"]; +} + @end diff --git a/LiveContainerUI/LCAppDelegate.m b/LiveContainerUI/LCAppDelegate.m index 5cd6fc6..c75d63c 100644 --- a/LiveContainerUI/LCAppDelegate.m +++ b/LiveContainerUI/LCAppDelegate.m @@ -2,6 +2,7 @@ #import "LCJITLessSetupViewController.h" #import "LCTabBarController.h" #import "LCUtils.h" +#import @implementation LCAppDelegate @@ -20,6 +21,19 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { + NSLog(@"[LiveContainer]got url to open: %@", url); + NSLog(@"[LiveContainer]host: %@", url.host); + if([url.host isEqualToString:@"open-web-page"]) { + NSURLComponents* urlComponent = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; + if(urlComponent.queryItems.count == 0){ + return YES; + } + + NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:urlComponent.queryItems[0].value options:0]; + NSString *decodedUrl = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; + NSLog(@"[LiveContainer]Webpage to open: %@", decodedUrl); + [((LCTabBarController*)_rootViewController) openWebPage:decodedUrl]; + } return [LCUtils launchToGuestAppWithURL:url]; } diff --git a/LiveContainerUI/LCAppListViewController.h b/LiveContainerUI/LCAppListViewController.h index 94bcf1b..56822ed 100644 --- a/LiveContainerUI/LCAppListViewController.h +++ b/LiveContainerUI/LCAppListViewController.h @@ -2,4 +2,5 @@ @interface LCAppListViewController : UITableViewController @property(nonatomic) NSString* acError; +- (void) openWebViewByURLString:(NSString*) urlString; @end diff --git a/LiveContainerUI/LCAppListViewController.m b/LiveContainerUI/LCAppListViewController.m index 5e09fc1..795ea90 100644 --- a/LiveContainerUI/LCAppListViewController.m +++ b/LiveContainerUI/LCAppListViewController.m @@ -75,6 +75,12 @@ - (void)loadView { - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.tableView reloadData]; + + NSString* webpageToOpen = [NSUserDefaults.standardUserDefaults objectForKey:@"webPageToOpen"]; + if(webpageToOpen) { + [NSUserDefaults.standardUserDefaults removeObjectForKey:@"webPageToOpen"]; + [self openWebViewByURLString:webpageToOpen]; + } } - (void)addButtonTapped { @@ -629,40 +635,40 @@ - (UIMenu *)destructiveActionWithTitle:(NSString *)title image:(UIImage *)image - (void) openUrlButtonTapped { - NSMutableArray* apps = [[NSMutableArray alloc] init]; - for(int i = 0; i < [self.objects count]; ++i) { - LCAppInfo* appInfo = [[LCAppInfo alloc] initWithBundlePath: [NSString stringWithFormat:@"%@/%@", self.bundlePath, self.objects[i]]]; - appInfo.relativeBundlePath = self.objects[i]; - [apps insertObject:appInfo atIndex:i]; - } - [self showInputDialogTitle:@"Input URL" message:@"Input URL scheme or URL to a web page" placeholder:@"scheme://" callback:^(NSString *ans) { - NSLog(@"[LiveContainer] got url: %@", ans); - NSURLComponents* url = [[NSURLComponents alloc] initWithString:ans]; - dispatch_async(dispatch_get_main_queue(), ^(void){ - if(!url) { - [self showDialogTitle:@"Invalid URL" message:@"The given URL is invalid. Check it and try again."]; - } else { - // use https for http and empty scheme - if([url.scheme length ] == 0 || [url.scheme isEqualToString:@"http"]) { - url.scheme = @"https"; - } else if (![url.scheme isEqualToString: @"https"]){ - [self launchAppByScheme:url apps:apps]; - } - LCWebView *webViewController = [[LCWebView alloc] initWithURL:url.URL apps:apps]; - UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:webViewController]; - navController.navigationBar.translucent = NO; - - navController.modalPresentationStyle = UIModalPresentationFullScreen; - [self presentViewController:navController animated:YES completion:nil]; - } + [self openWebViewByURLString:ans]; }); return (NSString*)nil; }]; +} - +- (void) openWebViewByURLString:(NSString*) urlString { + NSURLComponents* url = [[NSURLComponents alloc] initWithString:urlString]; + if(!url) { + [self showDialogTitle:@"Invalid URL" message:@"The given URL is invalid. Check it and try again."]; + } else { + NSMutableArray* apps = [[NSMutableArray alloc] init]; + for(int i = 0; i < [self.objects count]; ++i) { + LCAppInfo* appInfo = [[LCAppInfo alloc] initWithBundlePath: [NSString stringWithFormat:@"%@/%@", self.bundlePath, self.objects[i]]]; + appInfo.relativeBundlePath = self.objects[i]; + [apps insertObject:appInfo atIndex:i]; + } + + // use https for http and empty scheme + if([url.scheme length ] == 0 || [url.scheme isEqualToString:@"http"]) { + url.scheme = @"https"; + } else if (![url.scheme isEqualToString: @"https"]){ + [self launchAppByScheme:url apps:apps]; + } + LCWebView *webViewController = [[LCWebView alloc] initWithURL:url.URL apps:apps]; + UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:webViewController]; + navController.navigationBar.translucent = NO; + + navController.modalPresentationStyle = UIModalPresentationFullScreen; + [self presentViewController:navController animated:YES completion:nil]; + } } - (void) launchAppByScheme:(NSURLComponents*)schemeURL apps:(NSMutableArray*)apps { diff --git a/LiveContainerUI/LCTabBarController.h b/LiveContainerUI/LCTabBarController.h index f3c2052..37d7652 100644 --- a/LiveContainerUI/LCTabBarController.h +++ b/LiveContainerUI/LCTabBarController.h @@ -1,4 +1,7 @@ #import +#import "LCAppListViewController.h" @interface LCTabBarController : UITabBarController +@property() LCAppListViewController* appTableVC; +- (void) openWebPage:(NSString*) urlString; @end diff --git a/LiveContainerUI/LCTabBarController.m b/LiveContainerUI/LCTabBarController.m index dee0375..b3e50a7 100644 --- a/LiveContainerUI/LCTabBarController.m +++ b/LiveContainerUI/LCTabBarController.m @@ -1,4 +1,3 @@ -#import "LCAppListViewController.h" #import "LCSettingsListController.h" #import "LCTweakListViewController.h" #import "LCTabBarController.h" @@ -10,6 +9,7 @@ - (void)loadView { LCAppListViewController* appTableVC = [LCAppListViewController new]; appTableVC.title = @"Apps"; + self.appTableVC = appTableVC; LCTweakListViewController* tweakTableVC = [LCTweakListViewController new]; tweakTableVC.title = @"Tweaks"; @@ -28,4 +28,8 @@ - (void)loadView { self.viewControllers = @[appNavigationController, tweakNavigationController, settingsNavigationController]; } +- (void) openWebPage:(NSString*) urlString { + [self.appTableVC openWebViewByURLString:urlString]; +} + @end diff --git a/TweakLoader/UIKit+GuestHooks.m b/TweakLoader/UIKit+GuestHooks.m index 425a45f..110ebb4 100644 --- a/TweakLoader/UIKit+GuestHooks.m +++ b/TweakLoader/UIKit+GuestHooks.m @@ -35,6 +35,29 @@ void LCShowSwitchAppConfirmation(NSURL *url) { objc_setAssociatedObject(alert, @"window", window, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } +void LCOpenWebPage(NSString* webPageUrlString) { + NSString *message = [NSString stringWithFormat:@"Are you sure you want to open the web page and launch an app? Doing so will terminate this app."]; + UIWindow *window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; + UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"LiveContainer" message:message preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { + [NSClassFromString(@"LCSharedUtils") setWebPageUrlForNextLaunch:webPageUrlString]; + [NSClassFromString(@"LCSharedUtils") launchToGuestApp]; + }]; + [alert addAction:okAction]; + UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { + window.windowScene = nil; + }]; + [alert addAction:cancelAction]; + window.rootViewController = [UIViewController new]; + window.windowLevel = UIApplication.sharedApplication.windows.lastObject.windowLevel + 1; + window.windowScene = (id)UIApplication.sharedApplication.connectedScenes.anyObject; + [window makeKeyAndVisible]; + [window.rootViewController presentViewController:alert animated:YES completion:nil]; + objc_setAssociatedObject(alert, @"window", window, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + + +} + // Handler for AppDelegate @implementation UIApplication(LiveContainerHook) - (void)hook__applicationOpenURLAction:(id)action payload:(NSDictionary *)payload origin:(id)origin { @@ -42,6 +65,8 @@ - (void)hook__applicationOpenURLAction:(id)action payload:(NSDictionary *)payloa if ([url hasPrefix:@"livecontainer://livecontainer-relaunch"]) { // Ignore return; + } else if ([url hasPrefix:@"livecontainer://open-web-page?"]) { + LCOpenWebPage(url); } else if ([url hasPrefix:@"livecontainer://open-url"]) { // pass url to guest app NSURLComponents* lcUrl = [NSURLComponents componentsWithString:url]; @@ -84,6 +109,14 @@ - (void)hook_scene:(id)scene didReceiveActions:(NSSet *)actions fromTransitionCo NSString *url = urlAction.url.absoluteString; if ([url hasPrefix:@"livecontainer://livecontainer-relaunch"]) { // Ignore + } else if ([url hasPrefix:@"livecontainer://open-web-page?"]) { + NSURLComponents* lcUrl = [NSURLComponents componentsWithString:url]; + NSString* realUrlEncoded = lcUrl.queryItems[0].value; + if(!realUrlEncoded) return; + // Convert the base64 encoded url into String + NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:realUrlEncoded options:0]; + NSString *decodedUrl = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; + LCOpenWebPage(decodedUrl); } else if (![url hasPrefix:@"livecontainer://livecontainer-launch?"]) { // Not what we're looking for, pass it NSURLComponents* lcUrl = [NSURLComponents componentsWithString:url]; From f5bad5003db28fe30958da3415131ba0b4dafdd8 Mon Sep 17 00:00:00 2001 From: Huge_Black Date: Mon, 19 Aug 2024 01:04:12 +0800 Subject: [PATCH 4/7] fix not working at first launch --- LiveContainerUI/LCAppListViewController.m | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/LiveContainerUI/LCAppListViewController.m b/LiveContainerUI/LCAppListViewController.m index 795ea90..f6d3341 100644 --- a/LiveContainerUI/LCAppListViewController.m +++ b/LiveContainerUI/LCAppListViewController.m @@ -23,7 +23,7 @@ - (BOOL)safari_isHTTPFamilyURL { @interface LCAppListViewController () @property(atomic) NSMutableArray *objects; @property(nonatomic) NSString *bundlePath, *docPath, *tweakPath; - +@property(atomic) NSString* pageUrlToOpen; @property(nonatomic) MBRoundProgressView *progressView; @end @@ -70,6 +70,10 @@ - (void)loadView { ]; self.progressView = [[MBRoundProgressView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)]; + if(self.pageUrlToOpen) { + [self openWebViewByURLString:self.pageUrlToOpen]; + self.pageUrlToOpen = nil; + } } - (void)viewWillAppear:(BOOL)animated { @@ -645,6 +649,12 @@ - (void) openUrlButtonTapped { } - (void) openWebViewByURLString:(NSString*) urlString { + // wait for the loadView to call again. + if(!self.isViewLoaded) { + self.pageUrlToOpen = urlString; + return; + } + NSURLComponents* url = [[NSURLComponents alloc] initWithString:urlString]; if(!url) { [self showDialogTitle:@"Invalid URL" message:@"The given URL is invalid. Check it and try again."]; From 91529ad7fb5bc9111acd4a3a0853c21d0e029a36 Mon Sep 17 00:00:00 2001 From: Huge_Black Date: Mon, 19 Aug 2024 13:30:18 +0800 Subject: [PATCH 5/7] Allow deleting data folder when uninstalling. Removed some debug NSLog. --- LiveContainerUI/LCAppDelegate.m | 4 +--- LiveContainerUI/LCAppListViewController.m | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/LiveContainerUI/LCAppDelegate.m b/LiveContainerUI/LCAppDelegate.m index c75d63c..4594e09 100644 --- a/LiveContainerUI/LCAppDelegate.m +++ b/LiveContainerUI/LCAppDelegate.m @@ -21,8 +21,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { - NSLog(@"[LiveContainer]got url to open: %@", url); - NSLog(@"[LiveContainer]host: %@", url.host); + // handle page open request from URL scheme if([url.host isEqualToString:@"open-web-page"]) { NSURLComponents* urlComponent = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; if(urlComponent.queryItems.count == 0){ @@ -31,7 +30,6 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(N NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:urlComponent.queryItems[0].value options:0]; NSString *decodedUrl = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; - NSLog(@"[LiveContainer]Webpage to open: %@", decodedUrl); [((LCTabBarController*)_rootViewController) openWebPage:decodedUrl]; } return [LCUtils launchToGuestAppWithURL:url]; diff --git a/LiveContainerUI/LCAppListViewController.m b/LiveContainerUI/LCAppListViewController.m index f6d3341..7ae26b7 100644 --- a/LiveContainerUI/LCAppListViewController.m +++ b/LiveContainerUI/LCAppListViewController.m @@ -324,6 +324,20 @@ - (void)deleteItemAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void(^ } else { [self.objects removeObjectAtIndex:indexPath.row]; [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; + [self showConfirmationDialogTitle:@"Delete Data Folder" + message:[NSString stringWithFormat:@"Do you also want to delete data folder of %@? You can keep it for future use.", appInfo.displayName] + destructive:YES + confirmButtonTitle:@"Delete" + handler:^(UIAlertAction * action) { + if (action.style != UIAlertActionStyleCancel) { + NSError *error = nil; + NSString* dataFolderPath = [NSString stringWithFormat:@"%@/Data/Application/%@", self.docPath, [appInfo dataUUID]]; + [NSFileManager.defaultManager removeItemAtPath:dataFolderPath error:&error]; + if (error) { + [self showDialogTitle:@"Error" message:error.localizedDescription]; + } + } + }]; } } handler(YES); From ef77c1ab8d673157018ad3bedf543100fa4ebb02 Mon Sep 17 00:00:00 2001 From: Huge_Black Date: Tue, 20 Aug 2024 21:02:39 +0800 Subject: [PATCH 6/7] fix some url handling logic --- LiveContainerUI/LCAppListViewController.m | 4 +-- TweakLoader/UIKit+GuestHooks.m | 40 ++++++++++++++++------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/LiveContainerUI/LCAppListViewController.m b/LiveContainerUI/LCAppListViewController.m index 7ae26b7..50131ff 100644 --- a/LiveContainerUI/LCAppListViewController.m +++ b/LiveContainerUI/LCAppListViewController.m @@ -681,9 +681,9 @@ - (void) openWebViewByURLString:(NSString*) urlString { } // use https for http and empty scheme - if([url.scheme length ] == 0 || [url.scheme isEqualToString:@"http"]) { + if([url.scheme length ] == 0) { url.scheme = @"https"; - } else if (![url.scheme isEqualToString: @"https"]){ + } else if (![url.scheme isEqualToString: @"https"] && ![url.scheme isEqualToString: @"http"]){ [self launchAppByScheme:url apps:apps]; } LCWebView *webViewController = [[LCWebView alloc] initWithURL:url.URL apps:apps]; diff --git a/TweakLoader/UIKit+GuestHooks.m b/TweakLoader/UIKit+GuestHooks.m index 110ebb4..7fd1875 100644 --- a/TweakLoader/UIKit+GuestHooks.m +++ b/TweakLoader/UIKit+GuestHooks.m @@ -66,7 +66,15 @@ - (void)hook__applicationOpenURLAction:(id)action payload:(NSDictionary *)payloa // Ignore return; } else if ([url hasPrefix:@"livecontainer://open-web-page?"]) { - LCOpenWebPage(url); + // launch to UI and open web page + NSURLComponents* lcUrl = [NSURLComponents componentsWithString:url]; + NSString* realUrlEncoded = lcUrl.queryItems[0].value; + if(!realUrlEncoded) return; + // Convert the base64 encoded url into String + NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:realUrlEncoded options:0]; + NSString *decodedUrl = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; + LCOpenWebPage(decodedUrl); + return; } else if ([url hasPrefix:@"livecontainer://open-url"]) { // pass url to guest app NSURLComponents* lcUrl = [NSURLComponents componentsWithString:url]; @@ -79,13 +87,16 @@ - (void)hook__applicationOpenURLAction:(id)action payload:(NSDictionary *)payloa newPayload[UIApplicationLaunchOptionsURLKey] = decodedUrl; [self hook__applicationOpenURLAction:action payload:newPayload origin:origin]; return; - } else if (![url hasPrefix:@"livecontainer://livecontainer-launch?"]) { - // Not what we're looking for, pass it - [self hook__applicationOpenURLAction:action payload:payload origin:origin]; + } else if ([url hasPrefix:@"livecontainer://livecontainer-launch?"]) { + if (![url hasSuffix:NSBundle.mainBundle.bundlePath.lastPathComponent]) { + LCShowSwitchAppConfirmation([NSURL URLWithString:url]); + } return; - } else if (![url hasSuffix:NSBundle.mainBundle.bundlePath.lastPathComponent]) { - LCShowSwitchAppConfirmation([NSURL URLWithString:url]); + // Not what we're looking for, pass it + } + [self hook__applicationOpenURLAction:action payload:payload origin:origin]; + return; } @end @@ -109,16 +120,18 @@ - (void)hook_scene:(id)scene didReceiveActions:(NSSet *)actions fromTransitionCo NSString *url = urlAction.url.absoluteString; if ([url hasPrefix:@"livecontainer://livecontainer-relaunch"]) { // Ignore + return; } else if ([url hasPrefix:@"livecontainer://open-web-page?"]) { NSURLComponents* lcUrl = [NSURLComponents componentsWithString:url]; NSString* realUrlEncoded = lcUrl.queryItems[0].value; if(!realUrlEncoded) return; - // Convert the base64 encoded url into String + // launch to UI and open web page NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:realUrlEncoded options:0]; NSString *decodedUrl = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; LCOpenWebPage(decodedUrl); - } else if (![url hasPrefix:@"livecontainer://livecontainer-launch?"]) { - // Not what we're looking for, pass it + return; + } else if ([url hasPrefix:@"livecontainer://open-url?"]) { + // Open guest app's URL scheme NSURLComponents* lcUrl = [NSURLComponents componentsWithString:url]; NSString* realUrlEncoded = lcUrl.queryItems[0].value; if(!realUrlEncoded) return; @@ -132,8 +145,13 @@ - (void)hook_scene:(id)scene didReceiveActions:(NSSet *)actions fromTransitionCo [newActions addObject:newUrlAction]; [self hook_scene:scene didReceiveActions:newActions fromTransitionContext:context]; return; - } else if (![url hasSuffix:NSBundle.mainBundle.bundlePath.lastPathComponent]) { - LCShowSwitchAppConfirmation(urlAction.url); + } else if ([url hasPrefix:@"livecontainer://livecontainer-launch?"]){ + // If it's not current app, then switch + if (![url hasSuffix:NSBundle.mainBundle.bundlePath.lastPathComponent]) { + LCShowSwitchAppConfirmation(urlAction.url); + } + return; + } NSMutableSet *newActions = actions.mutableCopy; From 65557d2276ba69fcd3178dcf53a634a23fc7e9b7 Mon Sep 17 00:00:00 2001 From: Huge_Black Date: Sat, 24 Aug 2024 21:48:24 +0800 Subject: [PATCH 7/7] allow other actions reach guest app --- TweakLoader/UIKit+GuestHooks.m | 39 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/TweakLoader/UIKit+GuestHooks.m b/TweakLoader/UIKit+GuestHooks.m index 7fd1875..56954e5 100644 --- a/TweakLoader/UIKit+GuestHooks.m +++ b/TweakLoader/UIKit+GuestHooks.m @@ -120,37 +120,38 @@ - (void)hook_scene:(id)scene didReceiveActions:(NSSet *)actions fromTransitionCo NSString *url = urlAction.url.absoluteString; if ([url hasPrefix:@"livecontainer://livecontainer-relaunch"]) { // Ignore - return; + } else if ([url hasPrefix:@"livecontainer://open-web-page?"]) { NSURLComponents* lcUrl = [NSURLComponents componentsWithString:url]; NSString* realUrlEncoded = lcUrl.queryItems[0].value; - if(!realUrlEncoded) return; - // launch to UI and open web page - NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:realUrlEncoded options:0]; - NSString *decodedUrl = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; - LCOpenWebPage(decodedUrl); - return; + if(realUrlEncoded) { + // launch to UI and open web page + NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:realUrlEncoded options:0]; + NSString *decodedUrl = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; + LCOpenWebPage(decodedUrl); + } + } else if ([url hasPrefix:@"livecontainer://open-url?"]) { // Open guest app's URL scheme NSURLComponents* lcUrl = [NSURLComponents componentsWithString:url]; NSString* realUrlEncoded = lcUrl.queryItems[0].value; - if(!realUrlEncoded) return; - // Convert the base64 encoded url into String - NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:realUrlEncoded options:0]; - NSString *decodedUrl = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; - - NSMutableSet *newActions = actions.mutableCopy; - [newActions removeObject:urlAction]; - UIOpenURLAction *newUrlAction = [[UIOpenURLAction alloc] initWithURL:[NSURL URLWithString:decodedUrl]]; - [newActions addObject:newUrlAction]; - [self hook_scene:scene didReceiveActions:newActions fromTransitionContext:context]; - return; + if(realUrlEncoded) { + // Convert the base64 encoded url into String + NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:realUrlEncoded options:0]; + NSString *decodedUrl = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; + + NSMutableSet *newActions = actions.mutableCopy; + [newActions removeObject:urlAction]; + UIOpenURLAction *newUrlAction = [[UIOpenURLAction alloc] initWithURL:[NSURL URLWithString:decodedUrl]]; + [newActions addObject:newUrlAction]; + [self hook_scene:scene didReceiveActions:newActions fromTransitionContext:context]; + return; + } } else if ([url hasPrefix:@"livecontainer://livecontainer-launch?"]){ // If it's not current app, then switch if (![url hasSuffix:NSBundle.mainBundle.bundlePath.lastPathComponent]) { LCShowSwitchAppConfirmation(urlAction.url); } - return; }