From 499cae4030a76506a9c4f8c90e3e1b46e69bea26 Mon Sep 17 00:00:00 2001 From: khanhduytran0 Date: Mon, 5 Aug 2024 12:19:11 +0700 Subject: [PATCH] Allow adding app shortcuts to home screen (rewrite #62) Thanks @Vishram1123 for the idea --- AppInfo.h | 1 + AppInfo.m | 47 ++++++++++++++++++++++++++++++++++++++++++ LCAppDelegate.m | 17 +++++++++++++++ LCRootViewController.m | 36 +++++++++++++++++--------------- LCUtils.h | 2 ++ LCUtils.m | 21 +++++++++++++++++++ 6 files changed, 107 insertions(+), 17 deletions(-) diff --git a/AppInfo.h b/AppInfo.h index c83005d..91b0e5a 100644 --- a/AppInfo.h +++ b/AppInfo.h @@ -17,5 +17,6 @@ - (void)setDataUUID:(NSString *)uuid; - (void)setTweakFolder:(NSString *)tweakFolder; - (instancetype)initWithBundlePath:(NSString*)bundlePath; +- (NSDictionary *)generateWebClipConfig; - (void)save; @end \ No newline at end of file diff --git a/AppInfo.m b/AppInfo.m index 16c133a..a8f964b 100644 --- a/AppInfo.m +++ b/AppInfo.m @@ -73,6 +73,53 @@ - (UIImage*)icon { return icon; } +- (UIImage *)generateLiveContainerWrappedIcon { + UIImage *lcIcon = [UIImage imageNamed:@"AppIcon76x76"]; + UIImage *icon = self.icon; + CGFloat iconXY = (lcIcon.size.width - 40) / 2; + UIGraphicsBeginImageContextWithOptions(lcIcon.size, NO, 0.0); + [lcIcon drawInRect:CGRectMake(0, 0, lcIcon.size.width, lcIcon.size.height)]; + CGRect rect = CGRectMake(iconXY, iconXY, 40, 40); + [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:7] addClip]; + [icon drawInRect:rect]; + UIImage *newIcon = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return newIcon; +} + +- (NSDictionary *)generateWebClipConfig { + NSDictionary *payload = @{ + @"FullScreen": @YES, + @"Icon": UIImagePNGRepresentation(self.generateLiveContainerWrappedIcon), + @"IgnoreManifestScope": @YES, + @"IsRemovable": @YES, + @"Label": self.displayName, + @"PayloadDescription": [NSString stringWithFormat:@"Web Clip for launching %@ (%@) in LiveContainer", self.displayName, self.bundlePath.lastPathComponent], + @"PayloadDisplayName": self.displayName, + @"PayloadIdentifier": self.bundleIdentifier, + @"PayloadType": @"com.apple.webClip.managed", + @"PayloadUUID": self.dataUUID, + @"PayloadVersion": @(1), + @"Precomposed": @NO, + @"toPayloadOrganization": @"LiveContainer", + @"URL": [NSString stringWithFormat:@"livecontainer://livecontainer-launch?bundle-name=%@", self.bundlePath.lastPathComponent] + }; + return @{ + @"ConsentText": @{ + @"default": [NSString stringWithFormat:@"This profile installs a web clip which opens %@ (%@) in LiveContainer", self.displayName, self.bundlePath.lastPathComponent] + }, + @"PayloadContent": @[payload], + @"PayloadDescription": payload[@"PayloadDescription"], + @"PayloadDisplayName": self.displayName, + @"PayloadIdentifier": self.bundleIdentifier, + @"PayloadOrganization": @"LiveContainer", + @"PayloadRemovalDisallowed": @(NO), + @"PayloadType": @"Configuration", + @"PayloadUUID": @"345097fb-d4f7-4a34-ab90-2e3f1ad62eed", + @"PayloadVersion": @(1), + }; +} + - (void)save { [_info writeToFile:[NSString stringWithFormat:@"%@/Info.plist", _bundlePath] atomically:YES]; } diff --git a/LCAppDelegate.m b/LCAppDelegate.m index ddcda1f..05e8a45 100644 --- a/LCAppDelegate.m +++ b/LCAppDelegate.m @@ -1,6 +1,7 @@ #import "LCAppDelegate.h" #import "LCJITLessSetupViewController.h" #import "LCTabBarController.h" +#import "LCUtils.h" @implementation LCAppDelegate @@ -18,4 +19,20 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( return YES; } +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { + NSURLComponents* components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; + if(![components.host isEqualToString:@"livecontainer-launch"]) return false; + + for (NSURLQueryItem* queryItem in components.queryItems) { + if ([queryItem.name isEqualToString:@"bundle-name"]) { + [NSUserDefaults.standardUserDefaults setObject:queryItem.value forKey:@"selected"]; + + // Attempt to restart LiveContainer with the selected guest app + [LCUtils launchToGuestApp]; + break; + } + } + return true; +} + @end diff --git a/LCRootViewController.m b/LCRootViewController.m index 76663c0..8caa1ae 100644 --- a/LCRootViewController.m +++ b/LCRootViewController.m @@ -1,4 +1,5 @@ #import +#import #import #import "LCRootViewController.h" #import "LCUtils.h" @@ -8,7 +9,6 @@ #import "unarchive.h" #import "AppInfo.h" - #include #include #include @@ -78,6 +78,13 @@ static void patchExecSlice(const char *path, struct mach_header_64 *header) { } } +@implementation NSURL(hack) +- (BOOL)safari_isHTTPFamilyURL { + // Screw it, Apple + return YES; +} +@end + @interface LCRootViewController () @property(atomic) NSMutableArray *objects; @property(nonatomic) NSString *bundlePath, *docPath, *tweakPath; @@ -164,22 +171,7 @@ - (void)launchButtonTapped { return; } - NSString *urlScheme; - NSString *tsPath = [NSString stringWithFormat:@"%@/../_TrollStore", NSBundle.mainBundle.bundlePath]; - if (!access(tsPath.UTF8String, F_OK)) { - urlScheme = @"apple-magnifier://enable-jit?bundle-id=%@"; - } else if (LCUtils.certificatePassword) { - urlScheme = @"livecontainer://open?un_used=%@"; - } else { - urlScheme = @"sidestore://sidejit-enable?bid=%@"; - } - NSURL *sidejitURL = [NSURL URLWithString:[NSString stringWithFormat:urlScheme, NSBundle.mainBundle.bundleIdentifier]]; - if ([UIApplication.sharedApplication canOpenURL:sidejitURL]) { - [UIApplication.sharedApplication openURL:sidejitURL options:@{} completionHandler:^(BOOL b) { - exit(0); - }]; - return; - } + if ([LCUtils launchToGuestApp]) return; [self showDialogTitle:@"Instruction" message:@"To use this button, you need a build of SideStore that supports enabling JIT through URL scheme. Otherwise, you need to manually enable it." handler:^(UIAlertAction * action) { @@ -619,6 +611,16 @@ - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuCo } NSArray *menuItems = @[ + [UIAction + actionWithTitle:@"Add to Home Screen" + image:[UIImage systemImageNamed:@"plus.app"] + identifier:nil + handler:^(UIAction *action) { + NSData *data = [NSPropertyListSerialization dataWithPropertyList:appInfo.generateWebClipConfig format:NSPropertyListXMLFormat_v1_0 options:0 error:0]; + NSString *url = [NSString stringWithFormat:@"data:application/x-apple-aspen-config;base64,%@", [data base64EncodedStringWithOptions:0]]; + SFSafariViewController *svc = [[SFSafariViewController alloc] initWithURL:[NSURL URLWithString:url]]; + [self presentViewController:svc animated:YES completion:nil]; + }], [UIMenu menuWithTitle:@"Change tweak folder" image:[UIImage systemImageNamed:@"gearshape.2"] diff --git a/LCUtils.h b/LCUtils.h index 96d43ab..e006f93 100644 --- a/LCUtils.h +++ b/LCUtils.h @@ -17,7 +17,9 @@ + (NSProgress *)signAppBundle:(NSURL *)path completionHandler:(void (^)(BOOL success, NSError *error))completionHandler; + (BOOL)isAppGroupSideStore; ++ (BOOL)launchToGuestApp; + (NSURL *)archiveIPAWithSetupMode:(BOOL)setup error:(NSError **)error; + @end diff --git a/LCUtils.m b/LCUtils.m index ef93249..06e7798 100644 --- a/LCUtils.m +++ b/LCUtils.m @@ -1,5 +1,6 @@ @import Darwin; @import MachO; +@import UIKit; #import "AltStoreCore/ALTSigner.h" #import "LCUtils.h" @@ -247,4 +248,24 @@ + (NSURL *)archiveIPAWithSetupMode:(BOOL)setup error:(NSError **)error { return tmpIPAPath; } ++ (BOOL)launchToGuestApp { + NSString *urlScheme; + NSString *tsPath = [NSString stringWithFormat:@"%@/../_TrollStore", NSBundle.mainBundle.bundlePath]; + if (!access(tsPath.UTF8String, F_OK)) { + urlScheme = @"apple-magnifier://enable-jit?bundle-id=%@"; + } else if (LCUtils.certificatePassword) { + urlScheme = @"livecontainer://livecontainer-launch?unused=%@"; + } else { + urlScheme = @"sidestore://sidejit-enable?bid=%@"; + } + NSURL *launchURL = [NSURL URLWithString:[NSString stringWithFormat:urlScheme, NSBundle.mainBundle.bundleIdentifier]]; + if ([UIApplication.sharedApplication canOpenURL:launchURL]) { + [UIApplication.sharedApplication openURL:launchURL options:@{} completionHandler:^(BOOL b) { + exit(0); + }]; + return true; + } + return false; +} + @end