diff --git a/Helper/control b/Helper/control index cc03a1a..4c4200b 100644 --- a/Helper/control +++ b/Helper/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstoreroothelper Name: trollstoreroothelper -Version: 1.0.10 +Version: 1.1 Architecture: iphoneos-arm Description: An awesome tool of some sort!! Maintainer: opa334 diff --git a/Helper/main.m b/Helper/main.m index 411c640..6e10e6a 100644 --- a/Helper/main.m +++ b/Helper/main.m @@ -730,6 +730,11 @@ int installApp(NSString* appPath, BOOL sign, BOOL force) // Wipe old version if needed if(existed) { + if(![appId isEqualToString:@"com.opa334.TrollStore"]) + { + BKSTerminateApplicationForReasonAndReportWithDescription(appId, 5, false, @"TrollStore - App updated"); + } + NSLog(@"[installApp] found existing TrollStore app, cleaning directory"); NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:appContainer.url includingPropertiesForKeys:nil options:0 errorHandler:nil]; NSURL* fileURL; @@ -738,7 +743,7 @@ int installApp(NSString* appPath, BOOL sign, BOOL force) // do not under any circumstance delete this file as it makes iOS loose the app registration if([fileURL.lastPathComponent isEqualToString:@".com.apple.mobile_container_manager.metadata.plist"] || [fileURL.lastPathComponent isEqualToString:@"_TrollStore"]) { - NSLog(@"[installApp] skip removal of %@", fileURL); + NSLog(@"[installApp] skipping removal of %@", fileURL); continue; } @@ -767,6 +772,8 @@ int installApp(NSString* appPath, BOOL sign, BOOL force) int uninstallApp(NSString* appPath, NSString* appId) { + BKSTerminateApplicationForReasonAndReportWithDescription(appId, 5, false, @"TrollStore - App uninstalled"); + LSApplicationProxy* appProxy = [LSApplicationProxy applicationProxyForIdentifier:appId]; MCMContainer *appContainer = [objc_getClass("MCMAppDataContainer") containerWithIdentifier:appId createIfNecessary:NO existed:nil error:nil]; NSString *containerPath = [appContainer url].path; @@ -1156,6 +1163,14 @@ int main(int argc, char *argv[], char *envp[]) { } else if([cmd isEqualToString:@"uninstall-persistence-helper"]) { uninstallPersistenceHelper(); + } else if([cmd isEqualToString:@"dash"]) + { + LSApplicationProxy* appProxy = findPersistenceHelperApp(); + if(appProxy) + { + NSString* executablePath = appProxy.canonicalExecutablePath; + registerPath((char*)executablePath.UTF8String, 1); + } } NSLog(@"returning %d", ret); diff --git a/Helper/uicache.m b/Helper/uicache.m index 73eb782..f50e214 100644 --- a/Helper/uicache.m +++ b/Helper/uicache.m @@ -127,7 +127,6 @@ void registerPath(char* cPath, int unregister) // Misc dictToRegister[@"ApplicationType"] = @"System"; - dictToRegister[@"BundleNameIsLocalized"] = @1; dictToRegister[@"CFBundleIdentifier"] = appBundleID; dictToRegister[@"CodeInfoIdentifier"] = appBundleID; dictToRegister[@"CompatibilityState"] = @0; @@ -144,6 +143,10 @@ void registerPath(char* cPath, int unregister) dictToRegister[@"SignerIdentity"] = @"Apple iPhone OS Application Signing"; dictToRegister[@"IsAdHocSigned"] = @YES; dictToRegister[@"LSInstallType"] = @1; + dictToRegister[@"HasMIDBasedSINF"] = @0; + dictToRegister[@"MissingSINF"] = @0; + dictToRegister[@"FamilyID"] = @0; + dictToRegister[@"IsOnDemandInstallCapable"] = @0; NSString* teamIdentifier = constructTeamIdentifierForEntitlements(entitlements); if(teamIdentifier) dictToRegister[@"TeamIdentifier"] = teamIdentifier; @@ -199,7 +202,6 @@ void registerPath(char* cPath, int unregister) // Misc pluginDict[@"ApplicationType"] = @"PluginKitPlugin"; - pluginDict[@"BundleNameIsLocalized"] = @1; pluginDict[@"CFBundleIdentifier"] = pluginBundleID; pluginDict[@"CodeInfoIdentifier"] = pluginBundleID; pluginDict[@"CompatibilityState"] = @0; diff --git a/PersistenceHelper/Makefile b/PersistenceHelper/Makefile index baa9eb2..6d5bdad 100644 --- a/PersistenceHelper/Makefile +++ b/PersistenceHelper/Makefile @@ -5,7 +5,7 @@ include $(THEOS)/makefiles/common.mk APPLICATION_NAME = TrollStorePersistenceHelper -TrollStorePersistenceHelper_FILES = $(wildcard *.m) ../Helper/Shared.m ../Store/TSUtil.m +TrollStorePersistenceHelper_FILES = $(wildcard *.m) ../Helper/Shared.m ../Store/TSUtil.m ../Store/TSListControllerShared.m TrollStorePersistenceHelper_FRAMEWORKS = UIKit CoreGraphics CoreServices TrollStorePersistenceHelper_PRIVATE_FRAMEWORKS = Preferences TrollStorePersistenceHelper_CFLAGS = -fobjc-arc diff --git a/PersistenceHelper/Resources/Info.plist b/PersistenceHelper/Resources/Info.plist index 17c7756..8830808 100644 --- a/PersistenceHelper/Resources/Info.plist +++ b/PersistenceHelper/Resources/Info.plist @@ -52,7 +52,7 @@ iPhoneOS CFBundleVersion - 1.0.10 + 1.1 LSRequiresIPhoneOS UIDeviceFamily diff --git a/PersistenceHelper/TSPHRootViewController.h b/PersistenceHelper/TSPHRootViewController.h index f7cdc13..d2921c8 100644 --- a/PersistenceHelper/TSPHRootViewController.h +++ b/PersistenceHelper/TSPHRootViewController.h @@ -1,9 +1,7 @@ -#import -#import -#import +#import "../Store/TSListControllerShared.h" -@interface TSPHRootViewController : PSListController +@interface TSPHRootViewController : TSListControllerShared { - UIAlertController* _activityController; + NSString* _newerVersion; } @end diff --git a/PersistenceHelper/TSPHRootViewController.m b/PersistenceHelper/TSPHRootViewController.m index 503e8f5..6dc1c80 100644 --- a/PersistenceHelper/TSPHRootViewController.m +++ b/PersistenceHelper/TSPHRootViewController.m @@ -4,38 +4,29 @@ @implementation TSPHRootViewController -- (void)loadView +- (BOOL)isTrollStore { - [super loadView]; + return NO; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadSpecifiers) name:UIApplicationWillEnterForegroundNotification object:nil]; -} -- (void)startActivity:(NSString*)activity -{ - if(_activityController) return; - - _activityController = [UIAlertController alertControllerWithTitle:activity message:@"" preferredStyle:UIAlertControllerStyleAlert]; - UIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(5,5,50,50)]; - activityIndicator.hidesWhenStopped = YES; - activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium; - [activityIndicator startAnimating]; - [_activityController.view addSubview:activityIndicator]; - - [self presentViewController:_activityController animated:YES completion:nil]; -} - -- (void)stopActivityWithCompletion:(void (^)(void))completion -{ - if(!_activityController) return; - - [_activityController dismissViewControllerAnimated:YES completion:^ + fetchLatestTrollStoreVersion(^(NSString* latestVersion) { - _activityController = nil; - if(completion) + NSString* currentVersion = [self getTrollStoreVersion]; + NSComparisonResult result = [currentVersion compare:latestVersion options:NSNumericSearch]; + if(result == NSOrderedAscending) { - completion(); + _newerVersion = latestVersion; + dispatch_async(dispatch_get_main_queue(), ^ + { + [self reloadSpecifiers]; + }); } - }]; + }); } - (NSMutableArray*)specifiers @@ -60,10 +51,28 @@ [_specifiers addObject:infoSpecifier]; + BOOL isInstalled = trollStoreAppPath(); + + if(_newerVersion && isInstalled) + { + // Update TrollStore + PSSpecifier* updateTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:[NSString stringWithFormat:@"Update TrollStore to %@", _newerVersion] + target:self + set:nil + get:nil + detail:nil + cell:PSButtonCell + edit:nil]; + updateTrollStoreSpecifier.identifier = @"updateTrollStore"; + [updateTrollStoreSpecifier setProperty:@YES forKey:@"enabled"]; + updateTrollStoreSpecifier.buttonAction = @selector(updateTrollStorePressed); + [_specifiers addObject:updateTrollStoreSpecifier]; + } + PSSpecifier* utilitiesGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; [_specifiers addObject:utilitiesGroupSpecifier]; - if(trollStoreAppPath()) + if(isInstalled) { PSSpecifier* refreshAppRegistrationsSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Refresh App Registrations" target:self @@ -74,7 +83,7 @@ edit:nil]; refreshAppRegistrationsSpecifier.identifier = @"refreshAppRegistrations"; [refreshAppRegistrationsSpecifier setProperty:@YES forKey:@"enabled"]; - refreshAppRegistrationsSpecifier.buttonAction = @selector(refreshAppRegistrations); + refreshAppRegistrationsSpecifier.buttonAction = @selector(refreshAppRegistrationsPressed); [_specifiers addObject:refreshAppRegistrationsSpecifier]; PSSpecifier* uninstallTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Uninstall TrollStore" @@ -130,122 +139,21 @@ - (NSString*)getTrollStoreInfoString { - NSString* trollStore = trollStoreAppPath(); - if(!trollStore) + NSString* version = [self getTrollStoreVersion]; + if(!version) { return @"Not Installed"; } else { - NSBundle* trollStoreBundle = [NSBundle bundleWithPath:trollStore]; - NSString* version = [trollStoreBundle objectForInfoDictionaryKey:@"CFBundleVersion"]; return [NSString stringWithFormat:@"Installed, %@", version]; } } -- (void)refreshAppRegistrations +- (void)handleUninstallation { - [self startActivity:@"Refreshing"]; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ - { - spawnRoot(helperPath(), @[@"refresh"], nil, nil); - respring(); - - dispatch_async(dispatch_get_main_queue(), ^ - { - [self stopActivityWithCompletion:nil]; - }); - }); -} - -- (void)installTrollStorePressed -{ - NSURL* trollStoreURL = [NSURL URLWithString:@"https://github.com/opa334/TrollStore/releases/latest/download/TrollStore.tar"]; - NSURLRequest* trollStoreRequest = [NSURLRequest requestWithURL:trollStoreURL]; - - [self startActivity:@"Installing TrollStore"]; - - NSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:trollStoreRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) - { - if(error) - { - UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"Error downloading TrollStore: %@", error] preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; - [errorAlert addAction:closeAction]; - - dispatch_async(dispatch_get_main_queue(), ^ - { - [self stopActivityWithCompletion:^ - { - [self presentViewController:errorAlert animated:YES completion:nil]; - }]; - }); - } - else - { - NSString* tarTmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"TrollStore.tar"]; - [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:tarTmpPath error:nil]; - - int ret = spawnRoot(helperPath(), @[@"install-trollstore", tarTmpPath], nil, nil); - dispatch_async(dispatch_get_main_queue(), ^ - { - [[NSFileManager defaultManager] removeItemAtPath:tarTmpPath error:nil]; - [self stopActivityWithCompletion:^ - { - [self reloadSpecifiers]; - - if(ret == 0) - { - respring(); - } - else - { - UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"Error installing TrollStore: trollstorehelper returned %d", ret] preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; - [errorAlert addAction:closeAction]; - [self presentViewController:errorAlert animated:YES completion:nil]; - } - }]; - }); - } - }]; - - [downloadTask resume]; -} - -- (void)uninstallTrollStorePressed -{ - UIAlertController* uninstallWarningAlert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"About to uninstall TrollStore and all of the apps installed by it. Continue?" preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; - [uninstallWarningAlert addAction:cancelAction]; - - UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) - { - spawnRoot(helperPath(), @[@"uninstall-trollstore"], nil, nil); - [self reloadSpecifiers]; - }]; - [uninstallWarningAlert addAction:continueAction]; - - [self presentViewController:uninstallWarningAlert animated:YES completion:nil]; -} - -- (void)uninstallPersistenceHelperPressed -{ - UIAlertController* uninstallWarningAlert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"Uninstalling the persistence helper will revert this app back to it's original state, you will however no longer be able to persistently refresh the TrollStore app registrations. Continue?" preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; - [uninstallWarningAlert addAction:cancelAction]; - - UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) - { - spawnRoot(helperPath(), @[@"uninstall-persistence-helper"], nil, nil); - exit(0); - }]; - [uninstallWarningAlert addAction:continueAction]; - - [self presentViewController:uninstallWarningAlert animated:YES completion:nil]; + _newerVersion = nil; + [super handleUninstallation]; } @end diff --git a/PersistenceHelper/control b/PersistenceHelper/control index 065abf8..ce555b4 100644 --- a/PersistenceHelper/control +++ b/PersistenceHelper/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstorehelper Name: TrollStore Helper -Version: 1.0.10 +Version: 1.1 Architecture: iphoneos-arm Description: Helper utility to install and manage TrollStore! Maintainer: opa334 diff --git a/Store/Resources/Info.plist b/Store/Resources/Info.plist index e332bd4..2b31b54 100644 --- a/Store/Resources/Info.plist +++ b/Store/Resources/Info.plist @@ -50,7 +50,7 @@ iPhoneOS CFBundleVersion - 1.0.10 + 1.1 LSRequiresIPhoneOS UIDeviceFamily diff --git a/Store/TSAppTableViewController.m b/Store/TSAppTableViewController.m index a58c26d..6268a61 100644 --- a/Store/TSAppTableViewController.m +++ b/Store/TSAppTableViewController.m @@ -2,6 +2,32 @@ #import "TSApplicationsManager.h" +#define ICON_FORMAT_IPAD 8 +#define ICON_FORMAT_IPHONE 10 + +NSInteger iconFormatToUse(void) +{ + if(UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) + { + return ICON_FORMAT_IPAD; + } + else + { + return ICON_FORMAT_IPHONE; + } +} + +UIImage* imageWithSize(UIImage* image, CGSize size) +{ + if(CGSizeEqualToSize(image.size, size)) return image; + UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale); + CGRect imageRect = CGRectMake(0.0, 0.0, size.width, size.height); + [image drawInRect:imageRect]; + UIImage* outImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return outImage; +} + @interface UIImage () + (UIImage *)_applicationIconImageForBundleIdentifier:(NSString *)id format:(NSInteger)format scale:(double)scale; @end @@ -10,7 +36,14 @@ - (void)loadCachedAppPaths { - _cachedAppPaths = [[TSApplicationsManager sharedInstance] installedAppPaths]; + NSArray* appPaths = [[TSApplicationsManager sharedInstance] installedAppPaths]; + + _cachedAppPaths = [appPaths sortedArrayUsingComparator:^NSComparisonResult(NSString* appPathA, NSString* appPathB) { + NSString* displayNameA = [[TSApplicationsManager sharedInstance] displayNameForAppPath:appPathA]; + NSString* displayNameB = [[TSApplicationsManager sharedInstance] displayNameForAppPath:appPathB]; + + return [displayNameA localizedStandardCompare:displayNameB]; + }]; } - (instancetype)init @@ -19,7 +52,7 @@ if(self) { [self loadCachedAppPaths]; - _placeholderIcon = [UIImage _applicationIconImageForBundleIdentifier:@"com.apple.WebSheet" format:10 scale:[UIScreen mainScreen].scale]; + _placeholderIcon = [UIImage _applicationIconImageForBundleIdentifier:@"com.apple.WebSheet" format:iconFormatToUse() scale:[UIScreen mainScreen].scale]; _cachedIcons = [NSMutableDictionary new]; } return self; @@ -120,6 +153,11 @@ return _cachedAppPaths.count; } +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection +{ + [self reloadTable]; +} + - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ApplicationCell"]; if (!cell) { @@ -133,9 +171,11 @@ // Configure the cell... cell.textLabel.text = [[TSApplicationsManager sharedInstance] displayNameForAppPath:appPath]; cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ • %@", appVersion, appId]; - cell.imageView.layer.borderWidth = 0.34; - cell.imageView.layer.borderColor = [UIColor separatorColor].CGColor; - cell.imageView.layer.cornerRadius = 13.8; + cell.imageView.layer.borderWidth = 1; + cell.imageView.layer.borderColor = [UIColor.labelColor colorWithAlphaComponent:0.1].CGColor; + cell.imageView.layer.cornerRadius = 13.5; + cell.imageView.layer.masksToBounds = YES; + cell.imageView.layer.cornerCurve = kCACornerCurveContinuous; if(appId) { @@ -150,12 +190,13 @@ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ { //usleep(1000 * 5000); // (test delay for debugging) - UIImage* iconImage = [UIImage _applicationIconImageForBundleIdentifier:appId format:10 scale:[UIScreen mainScreen].scale]; + UIImage* iconImage = imageWithSize([UIImage _applicationIconImageForBundleIdentifier:appId format:iconFormatToUse() scale:[UIScreen mainScreen].scale], _placeholderIcon.size); _cachedIcons[appId] = iconImage; dispatch_async(dispatch_get_main_queue(), ^{ if([tableView.indexPathsForVisibleRows containsObject:indexPath]) { cell.imageView.image = iconImage; + [cell setNeedsLayout]; } }); }); @@ -207,7 +248,7 @@ [appSelectAlert addAction:detachAction];*/ - UIAlertAction* openAction = [UIAlertAction actionWithTitle: @"Open" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) + UIAlertAction* openAction = [UIAlertAction actionWithTitle:@"Open" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) { [self openAppPressedForRowAtIndexPath:indexPath]; [self deselectRow]; diff --git a/Store/TSListControllerShared.h b/Store/TSListControllerShared.h new file mode 100644 index 0000000..59b65e3 --- /dev/null +++ b/Store/TSListControllerShared.h @@ -0,0 +1,23 @@ +#import +#import +#import + +@interface TSListControllerShared : PSListController +{ + UIAlertController* _activityController; +} + +- (BOOL)isTrollStore; +- (NSString*)getTrollStoreVersion; + +- (void)startActivity:(NSString*)activity; +- (void)stopActivityWithCompletion:(void (^)(void))completion; + +- (void)installTrollStorePressed; +- (void)updateTrollStorePressed; +- (void)rebuildIconCachePressed; +- (void)refreshAppRegistrationsPressed; +- (void)uninstallPersistenceHelperPressed; +- (void)handleUninstallation; +- (void)uninstallTrollStorePressed; +@end \ No newline at end of file diff --git a/Store/TSListControllerShared.m b/Store/TSListControllerShared.m new file mode 100644 index 0000000..9bea3b0 --- /dev/null +++ b/Store/TSListControllerShared.m @@ -0,0 +1,227 @@ +#import "TSListControllerShared.h" +#import "TSUtil.h" +#import "../Helper/Shared.h" + +@implementation TSListControllerShared + +- (BOOL)isTrollStore +{ + return YES; +} + +- (NSString*)getTrollStoreVersion +{ + if([self isTrollStore]) + { + return [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleVersion"]; + } + else + { + NSString* trollStorePath = trollStoreAppPath(); + if(!trollStorePath) return nil; + + NSBundle* trollStoreBundle = [NSBundle bundleWithPath:trollStorePath]; + return [trollStoreBundle objectForInfoDictionaryKey:@"CFBundleVersion"]; + } +} + +- (void)startActivity:(NSString*)activity +{ + if(_activityController) return; + + _activityController = [UIAlertController alertControllerWithTitle:activity message:@"" preferredStyle:UIAlertControllerStyleAlert]; + UIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(5,5,50,50)]; + activityIndicator.hidesWhenStopped = YES; + activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium; + [activityIndicator startAnimating]; + [_activityController.view addSubview:activityIndicator]; + + [self presentViewController:_activityController animated:YES completion:nil]; +} + +- (void)stopActivityWithCompletion:(void (^)(void))completion +{ + if(!_activityController) return; + + [_activityController dismissViewControllerAnimated:YES completion:^ + { + _activityController = nil; + if(completion) + { + completion(); + } + }]; +} + +- (void)_updateOrInstallTrollStore:(BOOL)update +{ + NSURL* trollStoreURL = [NSURL URLWithString:@"https://github.com/opa334/TrollStore/releases/latest/download/TrollStore.tar"]; + NSURLRequest* trollStoreRequest = [NSURLRequest requestWithURL:trollStoreURL]; + + if(update) + { + [self startActivity:@"Updating TrollStore"]; + } + else + { + [self startActivity:@"Installing TrollStore"]; + } + + NSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:trollStoreRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) + { + if(error) + { + UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"Error downloading TrollStore: %@", error] preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; + [errorAlert addAction:closeAction]; + + dispatch_async(dispatch_get_main_queue(), ^ + { + [self stopActivityWithCompletion:^ + { + [self presentViewController:errorAlert animated:YES completion:nil]; + }]; + }); + } + else + { + NSString* tarTmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"TrollStore.tar"]; + [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:tarTmpPath error:nil]; + + int ret = spawnRoot(helperPath(), @[@"install-trollstore", tarTmpPath], nil, nil); + [[NSFileManager defaultManager] removeItemAtPath:tarTmpPath error:nil]; + + if(ret == 0) + { + respring(); + + if([self isTrollStore]) + { + exit(0); + } + else + { + dispatch_async(dispatch_get_main_queue(), ^ + { + [self stopActivityWithCompletion:^ + { + [self reloadSpecifiers]; + }]; + }); + } + } + else + { + dispatch_async(dispatch_get_main_queue(), ^ + { + [self stopActivityWithCompletion:^ + { + UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"Error installing TrollStore: trollstorehelper returned %d", ret] preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]; + [errorAlert addAction:closeAction]; + [self presentViewController:errorAlert animated:YES completion:nil]; + }]; + }); + } + } + }]; + + [downloadTask resume]; +} + +- (void)installTrollStorePressed +{ + [self _updateOrInstallTrollStore:NO]; +} + +- (void)updateTrollStorePressed +{ + [self _updateOrInstallTrollStore:YES]; +} + +- (void)rebuildIconCachePressed +{ + [self startActivity:@"Rebuilding Icon Cache"]; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ + { + spawnRoot(helperPath(), @[@"refresh-all"], nil, nil); + + dispatch_async(dispatch_get_main_queue(), ^ + { + [self stopActivityWithCompletion:nil]; + }); + }); +} + +- (void)refreshAppRegistrationsPressed +{ + [self startActivity:@"Refreshing"]; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ + { + spawnRoot(helperPath(), @[@"refresh"], nil, nil); + respring(); + + dispatch_async(dispatch_get_main_queue(), ^ + { + [self stopActivityWithCompletion:nil]; + }); + }); +} + +- (void)uninstallPersistenceHelperPressed +{ + if([self isTrollStore]) + { + spawnRoot(helperPath(), @[@"uninstall-persistence-helper"], nil, nil); + [self reloadSpecifiers]; + } + else + { + UIAlertController* uninstallWarningAlert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"Uninstalling the persistence helper will revert this app back to it's original state, you will however no longer be able to persistently refresh the TrollStore app registrations. Continue?" preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; + [uninstallWarningAlert addAction:cancelAction]; + + UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) + { + spawnRoot(helperPath(), @[@"uninstall-persistence-helper"], nil, nil); + exit(0); + }]; + [uninstallWarningAlert addAction:continueAction]; + + [self presentViewController:uninstallWarningAlert animated:YES completion:nil]; + } +} + +- (void)handleUninstallation +{ + if([self isTrollStore]) + { + exit(0); + } + else + { + [self reloadSpecifiers]; + } +} + +- (void)uninstallTrollStorePressed +{ + UIAlertController* uninstallWarningAlert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"About to uninstall TrollStore and all of the apps installed by it. Continue?" preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; + [uninstallWarningAlert addAction:cancelAction]; + + UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) + { + spawnRoot(helperPath(), @[@"uninstall-trollstore"], nil, nil); + [self handleUninstallation]; + }]; + [uninstallWarningAlert addAction:continueAction]; + + [self presentViewController:uninstallWarningAlert animated:YES completion:nil]; +} + +@end \ No newline at end of file diff --git a/Store/TSSettingsListController.h b/Store/TSSettingsListController.h index 39fdd47..959d405 100644 --- a/Store/TSSettingsListController.h +++ b/Store/TSSettingsListController.h @@ -1,9 +1,8 @@ -#import -#import +#import "TSListControllerShared.h" -@interface TSSettingsListController : PSListController +@interface TSSettingsListController : TSListControllerShared { - UIAlertController* _activityController; PSSpecifier* _installPersistenceHelperSpecifier; + NSString* _newerVersion; } @end \ No newline at end of file diff --git a/Store/TSSettingsListController.m b/Store/TSSettingsListController.m index 9782ecb..9b2d2b0 100644 --- a/Store/TSSettingsListController.m +++ b/Store/TSSettingsListController.m @@ -6,38 +6,24 @@ @implementation TSSettingsListController -- (void)loadView +- (void)viewDidLoad { - [super loadView]; + [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadSpecifiers) name:UIApplicationWillEnterForegroundNotification object:nil]; -} -- (void)startActivity:(NSString*)activity -{ - if(_activityController) return; - - _activityController = [UIAlertController alertControllerWithTitle:activity message:@"" preferredStyle:UIAlertControllerStyleAlert]; - UIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(5,5,50,50)]; - activityIndicator.hidesWhenStopped = YES; - activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium; - [activityIndicator startAnimating]; - [_activityController.view addSubview:activityIndicator]; - - [self presentViewController:_activityController animated:YES completion:nil]; -} - -- (void)stopActivityWithCompletion:(void (^)(void))completion -{ - if(!_activityController) return; - - [_activityController dismissViewControllerAnimated:YES completion:^ + fetchLatestTrollStoreVersion(^(NSString* latestVersion) { - _activityController = nil; - if(completion) + NSString* currentVersion = [self getTrollStoreVersion]; + NSComparisonResult result = [currentVersion compare:latestVersion options:NSNumericSearch]; + if(result == NSOrderedAscending) { - completion(); + _newerVersion = latestVersion; + dispatch_async(dispatch_get_main_queue(), ^ + { + [self reloadSpecifiers]; + }); } - }]; + }); } - (NSMutableArray*)specifiers @@ -46,6 +32,25 @@ { _specifiers = [NSMutableArray new]; + if(_newerVersion) + { + PSSpecifier* updateTrollStoreGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; + updateTrollStoreGroupSpecifier.name = @"Update Available"; + [_specifiers addObject:updateTrollStoreGroupSpecifier]; + + PSSpecifier* updateTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:[NSString stringWithFormat:@"Update TrollStore to %@", _newerVersion] + target:self + set:nil + get:nil + detail:nil + cell:PSButtonCell + edit:nil]; + updateTrollStoreSpecifier.identifier = @"updateTrollStore"; + [updateTrollStoreSpecifier setProperty:@YES forKey:@"enabled"]; + updateTrollStoreSpecifier.buttonAction = @selector(updateTrollStorePressed); + [_specifiers addObject:updateTrollStoreSpecifier]; + } + PSSpecifier* utilitiesGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; utilitiesGroupSpecifier.name = @"Utilities"; [utilitiesGroupSpecifier setProperty:@"If an app does not immediately appear after installation, respring here and it should appear afterwards." forKey:@"footerText"]; @@ -192,7 +197,7 @@ } PSSpecifier* otherGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; - [otherGroupSpecifier setProperty:[NSString stringWithFormat:@"TrollStore %@\n\n© 2022 Lars Fröder (opa334)\n\nCredits:\n@LinusHenze: CoreTrust bug\n@zhuowei: CoreTrust bug writeup and cert\n@lunotech11, @SerenaKit, @tylinux: Various contributions\n@ProcursusTeam: uicache and ldid build\n@cstar_ow: uicache\n@saurik: ldid", getTrollStoreVersion()] forKey:@"footerText"]; + [otherGroupSpecifier setProperty:[NSString stringWithFormat:@"TrollStore %@\n\n© 2022 Lars Fröder (opa334)\n\nCredits:\n@LinusHenze: CoreTrust bug\n@zhuowei: CoreTrust bug writeup and cert\n@lunotech11, @SerenaKit, @tylinux: Various contributions\n@ProcursusTeam: uicache and ldid build\n@cstar_ow: uicache\n@saurik: ldid", [self getTrollStoreVersion]] forKey:@"footerText"]; [_specifiers addObject:otherGroupSpecifier]; // Uninstall TrollStore @@ -208,6 +213,18 @@ [uninstallTrollStoreSpecifier setProperty:NSClassFromString(@"PSDeleteButtonCell") forKey:@"cellClass"]; uninstallTrollStoreSpecifier.buttonAction = @selector(uninstallTrollStorePressed); [_specifiers addObject:uninstallTrollStoreSpecifier]; + + /*PSSpecifier* doTheDashSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Do the Dash" + target:self + set:nil + get:nil + detail:nil + cell:PSButtonCell + edit:nil]; + doTheDashSpecifier.identifier = @"doTheDash"; + [doTheDashSpecifier setProperty:@YES forKey:@"enabled"]; + uninstallTrollStoreSpecifier.buttonAction = @selector(doTheDashPressed); + [_specifiers addObject:doTheDashSpecifier];*/ } [(UINavigationItem *)self.navigationItem setTitle:@"Settings"]; @@ -219,21 +236,6 @@ respring(); } -- (void)rebuildIconCachePressed -{ - [self startActivity:@"Rebuilding Icon Cache"]; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ - { - spawnRoot(helperPath(), @[@"refresh-all"], nil, nil); - - dispatch_async(dispatch_get_main_queue(), ^ - { - [self stopActivityWithCompletion:nil]; - }); - }); -} - - (void)installLdidPressed { NSURL* ldidURL = [NSURL URLWithString:@"https://github.com/opa334/ldid/releases/download/v2.1.5-procursus5/ldid"]; @@ -312,27 +314,9 @@ [self presentViewController:selectAppAlert animated:YES completion:nil]; } -- (void)uninstallPersistenceHelperPressed +- (void)doTheDashPressed { - spawnRoot(helperPath(), @[@"uninstall-persistence-helper"], nil, nil); - [self reloadSpecifiers]; -} - -- (void)uninstallTrollStorePressed -{ - UIAlertController* uninstallWarningAlert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"About to uninstall TrollStore and all of the apps installed by it. Continue?" preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; - [uninstallWarningAlert addAction:cancelAction]; - - UIAlertAction* continueAction = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) - { - spawnRoot(helperPath(), @[@"uninstall-trollstore"], nil, nil); - exit(0); - }]; - [uninstallWarningAlert addAction:continueAction]; - - [self presentViewController:uninstallWarningAlert animated:YES completion:nil]; + spawnRoot(helperPath(), @[@"dash"], nil, nil); } @end \ No newline at end of file diff --git a/Store/TSUtil.h b/Store/TSUtil.h index 55afe23..af54257 100644 --- a/Store/TSUtil.h +++ b/Store/TSUtil.h @@ -4,4 +4,4 @@ extern NSString* helperPath(void); extern void printMultilineNSString(NSString* stringToPrint); extern int spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString** stdErr); extern void respring(void); -extern NSString* getTrollStoreVersion(void); \ No newline at end of file +extern void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVersion)); \ No newline at end of file diff --git a/Store/TSUtil.m b/Store/TSUtil.m index aee6b86..1a26cf6 100644 --- a/Store/TSUtil.m +++ b/Store/TSUtil.m @@ -186,7 +186,26 @@ void respring(void) exit(0); } -NSString* getTrollStoreVersion(void) +void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVersion)) { - return [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleVersion"]; -} + NSURL* githubLatestAPIURL = [NSURL URLWithString:@"https://api.github.com/repos/opa334/TrollStore/releases/latest"]; + + NSURLSessionDataTask* task = [NSURLSession.sharedSession dataTaskWithURL:githubLatestAPIURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) + { + if(!error) + { + if ([response isKindOfClass:[NSHTTPURLResponse class]]) + { + NSError *jsonError; + NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; + + if (!jsonError) + { + completionHandler(jsonResponse[@"tag_name"]); + } + } + } + }]; + + [task resume]; +} \ No newline at end of file diff --git a/Store/control b/Store/control index 6747b25..1013550 100644 --- a/Store/control +++ b/Store/control @@ -1,6 +1,6 @@ Package: com.opa334.trollstore Name: TrollStore -Version: 1.0.10 +Version: 1.1 Architecture: iphoneos-arm Description: An awesome application! Maintainer: opa334 diff --git a/_compile/build_full.sh b/_compile/build_full.sh index 67ac02d..b22b22f 100755 --- a/_compile/build_full.sh +++ b/_compile/build_full.sh @@ -48,18 +48,20 @@ COPYFILE_DISABLE=1 tar -czvf TrollStore.tar ./TrollStore.app rm -rf ./TrollStore.app cd - -# Step five: compile installer -xcodebuild -project ../Installer/TrollInstaller/TrollInstaller.xcodeproj -scheme TrollInstaller -destination generic/platform=iOS -archivePath ./out/Installer.xcarchive archive +if [[ $1 == "installer" ]]; then + # Step five: compile installer + xcodebuild -project ../Installer/TrollInstaller/TrollInstaller.xcodeproj -scheme TrollInstaller -destination generic/platform=iOS -archivePath ./out/Installer.xcarchive archive -if [[ -f "./out/Installer.xcarchive/Products/Applications/TrollInstaller.app/embedded.mobileprovision" ]]; then - rm ./out/Installer.xcarchive/Products/Applications/TrollInstaller.app/embedded.mobileprovision -fi + if [[ -f "./out/Installer.xcarchive/Products/Applications/TrollInstaller.app/embedded.mobileprovision" ]]; then + rm ./out/Installer.xcarchive/Products/Applications/TrollInstaller.app/embedded.mobileprovision + fi -ldid -s ./out/Installer.xcarchive/Products/Applications/TrollInstaller.app -mkdir ./out/Payload -mv ./out/Installer.xcarchive/Products/Applications/TrollInstaller.app ./out/Payload/TrollInstaller.app -cd out -zip -vr TrollInstaller.ipa Payload -cd - -rm -rf ./out/Payload -rm -rf ./out/Installer.xcarchive \ No newline at end of file + ldid -s ./out/Installer.xcarchive/Products/Applications/TrollInstaller.app + mkdir ./out/Payload + mv ./out/Installer.xcarchive/Products/Applications/TrollInstaller.app ./out/Payload/TrollInstaller.app + cd out + zip -vr TrollInstaller.ipa Payload + cd - + rm -rf ./out/Payload + rm -rf ./out/Installer.xcarchive +fi \ No newline at end of file