mirror of https://github.com/opa334/TrollStore.git
1.3
This commit is contained in:
parent
daacab0c8b
commit
029a80f4b9
24
README.md
24
README.md
|
@ -38,7 +38,7 @@ This installation method unfortunately does **NOT** work on arm64 (A8 - A11) iOS
|
|||
|
||||
4. If this app has not appeared, that's a stock iOS bug, reboot your device and the app will appear.
|
||||
|
||||
5. Launch the app, and tap "Install TrollStore"
|
||||
5. Launch the app, tap "Install TrollStore"
|
||||
|
||||
6. Wait a few seconds, your device should respring and TrollStore will be installed.
|
||||
|
||||
|
@ -56,7 +56,7 @@ Supports jailbroken devices running 14.0 and above.
|
|||
|
||||
1. Open your package manager, and make sure Havoc repo (https://havoc.app) is added under Sources, then search for "TrollStore Helper" and install it.
|
||||
|
||||
2. After the installation, respring and a "TrollHelper" app should be on your home screen, launch it.
|
||||
2. After the installation, respring and the "TrollHelper" app should have appeared on your home screen.
|
||||
|
||||
3. Launch the app, tap "Install TrollStore"
|
||||
|
||||
|
@ -66,6 +66,18 @@ Supports jailbroken devices running 14.0 and above.
|
|||
|
||||
6. Done, you can now share IPA files with TrollStore and they will be permanently installed on your device.
|
||||
|
||||
### Unjailbreaking while retaining TrollStore
|
||||
|
||||
Some people might prefer to use TrollStore in an unjailbroken environment, if that applies to you, follow this guide.
|
||||
|
||||
1. Uninstall TrollHelper from your package manager
|
||||
|
||||
2. Now when you launch TrollStore, it will have an option to install the persistence helper into a System app like on iOS 15, do so.
|
||||
|
||||
3. Now restore rootFS through your jailbreak app, afterwards use the System app to refresh app registrations.
|
||||
|
||||
4. Done, your device will be jailed, but TrollStore will still work.
|
||||
|
||||
## Updating TrollStore
|
||||
|
||||
When a new TrollStore update is available, a button to install it will appear at the top in the TrollStore settings. After tapping the button, TrollStore will automatically download the update, install it, and respring.
|
||||
|
@ -84,6 +96,14 @@ The only way to work around this is to install a persistence helper into a syste
|
|||
|
||||
On jailbroken iOS 14 when TrollHelper is used for installation, it is located in /Applications and will persist as a "System" app through icon cache reloads, therefore TrollHelper is used as the persistence helper on iOS 14.
|
||||
|
||||
## URL Scheme
|
||||
|
||||
As of version 1.3, TrollStore replaces the system URL scheme "apple-magnifier" (this is done so "jailbreak" detections can't detect TrollStore like they could if TrollStore had a unique URL scheme). This URL scheme can be used to install applications right from the browser, the format goes as follows:
|
||||
|
||||
`apple-magnifier://install?url=<URL_to_IPA>`
|
||||
|
||||
On devices that don't have TrollStore (1.3+) installed, this will just open the magnifier app.
|
||||
|
||||
## Features
|
||||
|
||||
The binaries inside an IPA can have arbitrary entitlements, fakesign them with ldid and the entitlements you want (`ldid -S<path/to/entitlements.plist> <path/to/binary>`) and TrollStore will preserve the entitlements when resigning them with the fake root certificate on installation. This gives you a lot of possibilities, some of which are explained below.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Package: com.opa334.trollstoreroothelper
|
||||
Name: trollstoreroothelper
|
||||
Version: 1.2.2
|
||||
Version: 1.3
|
||||
Architecture: iphoneos-arm
|
||||
Description: An awesome tool of some sort!!
|
||||
Maintainer: opa334
|
||||
|
|
|
@ -27,27 +27,6 @@ extern NSString* BKSOpenApplicationOptionKeyActivateForEvent;
|
|||
|
||||
extern void BKSTerminateApplicationForReasonAndReportWithDescription(NSString *bundleID, int reasonID, bool report, NSString *description);
|
||||
|
||||
typedef CF_OPTIONS(uint32_t, SecCSFlags) {
|
||||
kSecCSDefaultFlags = 0
|
||||
};
|
||||
|
||||
|
||||
#define kSecCSRequirementInformation 1 << 2
|
||||
#define kSecCSSigningInformation 1 << 1
|
||||
|
||||
typedef struct __SecCode const *SecStaticCodeRef;
|
||||
|
||||
extern CFStringRef kSecCodeInfoEntitlementsDict;
|
||||
extern CFStringRef kSecCodeInfoCertificates;
|
||||
extern CFStringRef kSecPolicyAppleiPhoneApplicationSigning;
|
||||
extern CFStringRef kSecPolicyAppleiPhoneProfileApplicationSigning;
|
||||
extern CFStringRef kSecPolicyLeafMarkerOid;
|
||||
|
||||
OSStatus SecStaticCodeCreateWithPathAndAttributes(CFURLRef path, SecCSFlags flags, CFDictionaryRef attributes, SecStaticCodeRef *staticCode);
|
||||
OSStatus SecCodeCopySigningInformation(SecStaticCodeRef code, SecCSFlags flags, CFDictionaryRef *information);
|
||||
CFDataRef SecCertificateCopyExtensionValue(SecCertificateRef certificate, CFTypeRef extensionOID, bool *isCritical);
|
||||
void SecPolicySetOptionsValue(SecPolicyRef policy, CFStringRef key, CFTypeRef value);
|
||||
|
||||
#define kCFPreferencesNoContainer CFSTR("kCFPreferencesNoContainer")
|
||||
|
||||
typedef CFPropertyListRef (*_CFPreferencesCopyValueWithContainerType)(CFStringRef key, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath);
|
||||
|
@ -222,97 +201,6 @@ int runLdid(NSArray* args, NSString** output, NSString** errorOutput)
|
|||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
SecStaticCodeRef getStaticCodeRef(NSString *binaryPath)
|
||||
{
|
||||
if(binaryPath == nil)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CFURLRef binaryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)binaryPath, kCFURLPOSIXPathStyle, false);
|
||||
if(binaryURL == NULL)
|
||||
{
|
||||
NSLog(@"[getStaticCodeRef] failed to get URL to binary %@", binaryPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SecStaticCodeRef codeRef = NULL;
|
||||
OSStatus result;
|
||||
|
||||
result = SecStaticCodeCreateWithPathAndAttributes(binaryURL, kSecCSDefaultFlags, NULL, &codeRef);
|
||||
|
||||
CFRelease(binaryURL);
|
||||
|
||||
if(result != errSecSuccess)
|
||||
{
|
||||
NSLog(@"[getStaticCodeRef] failed to create static code for binary %@", binaryPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return codeRef;
|
||||
}
|
||||
|
||||
NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef)
|
||||
{
|
||||
if(codeRef == NULL)
|
||||
{
|
||||
NSLog(@"[dumpEntitlements] attempting to dump entitlements without a StaticCodeRef");
|
||||
return nil;
|
||||
}
|
||||
|
||||
CFDictionaryRef signingInfo = NULL;
|
||||
OSStatus result;
|
||||
|
||||
result = SecCodeCopySigningInformation(codeRef, kSecCSRequirementInformation, &signingInfo);
|
||||
|
||||
if(result != errSecSuccess)
|
||||
{
|
||||
NSLog(@"[dumpEntitlements] failed to copy signing info from static code");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSDictionary *entitlementsNSDict = nil;
|
||||
|
||||
CFDictionaryRef entitlements = CFDictionaryGetValue(signingInfo, kSecCodeInfoEntitlementsDict);
|
||||
if(entitlements == NULL)
|
||||
{
|
||||
NSLog(@"[dumpEntitlements] no entitlements specified");
|
||||
}
|
||||
else if(CFGetTypeID(entitlements) != CFDictionaryGetTypeID())
|
||||
{
|
||||
NSLog(@"[dumpEntitlements] invalid entitlements");
|
||||
}
|
||||
else
|
||||
{
|
||||
entitlementsNSDict = (__bridge NSDictionary *)(entitlements);
|
||||
NSLog(@"[dumpEntitlements] dumped %@", entitlementsNSDict);
|
||||
}
|
||||
|
||||
CFRelease(signingInfo);
|
||||
return entitlementsNSDict;
|
||||
}
|
||||
|
||||
NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString *binaryPath)
|
||||
{
|
||||
// This function is intended for one-shot checks. Main-event functions should retain/release their own SecStaticCodeRefs
|
||||
|
||||
if(binaryPath == nil)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
SecStaticCodeRef codeRef = getStaticCodeRef(binaryPath);
|
||||
if(codeRef == NULL)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSDictionary *entitlements = dumpEntitlements(codeRef);
|
||||
CFRelease(codeRef);
|
||||
|
||||
return entitlements;
|
||||
}
|
||||
|
||||
BOOL certificateHasDataForExtensionOID(SecCertificateRef certificate, CFStringRef oidString)
|
||||
{
|
||||
if(certificate == NULL || oidString == NULL)
|
||||
|
@ -762,7 +650,7 @@ int installApp(NSString* appPath, BOOL sign, BOOL force)
|
|||
if(suc)
|
||||
{
|
||||
NSLog(@"[installApp] App %@ installed, adding to icon cache now...", appId);
|
||||
registerPath((char*)newAppPath.UTF8String, 0);
|
||||
registerPath((char*)newAppPath.UTF8String, 0, YES);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
|
@ -816,7 +704,7 @@ int uninstallApp(NSString* appPath, NSString* appId)
|
|||
}
|
||||
|
||||
// unregister app
|
||||
registerPath((char*)appPath.UTF8String, 1);
|
||||
registerPath((char*)appPath.UTF8String, 1, YES);
|
||||
|
||||
NSLog(@"[uninstallApp] deleting %@", [appPath stringByDeletingLastPathComponent]);
|
||||
// delete app
|
||||
|
@ -930,7 +818,7 @@ BOOL uninstallTrollStore(BOOL unregister)
|
|||
|
||||
if(unregister)
|
||||
{
|
||||
registerPath((char*)trollStoreAppPath().UTF8String, 1);
|
||||
registerPath((char*)trollStoreAppPath().UTF8String, 1, YES);
|
||||
}
|
||||
|
||||
return [[NSFileManager defaultManager] removeItemAtPath:trollStore error:nil];
|
||||
|
@ -988,13 +876,13 @@ BOOL installTrollStore(NSString* pathToTar)
|
|||
|
||||
void refreshAppRegistrations()
|
||||
{
|
||||
//registerPath((char*)trollStoreAppPath().UTF8String, 1);
|
||||
registerPath((char*)trollStoreAppPath().UTF8String, 0);
|
||||
//registerPath((char*)trollStoreAppPath().UTF8String, 1, YES);
|
||||
registerPath((char*)trollStoreAppPath().UTF8String, 0, YES);
|
||||
|
||||
for(NSString* appPath in trollStoreInstalledAppBundlePaths())
|
||||
{
|
||||
//registerPath((char*)appPath.UTF8String, 1);
|
||||
registerPath((char*)appPath.UTF8String, 0);
|
||||
//registerPath((char*)appPath.UTF8String, 1, YES);
|
||||
registerPath((char*)appPath.UTF8String, 0, YES);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1128,6 +1016,59 @@ void registerUserPersistenceHelper(NSString* userAppId)
|
|||
[[NSFileManager defaultManager] createFileAtPath:markPath contents:[NSData data] attributes:nil];
|
||||
}
|
||||
|
||||
// Apparently there is some odd behaviour where TrollStore installed apps sometimes get restricted
|
||||
// This works around that issue at least and is triggered when rebuilding icon cache
|
||||
void cleanRestrictions(void)
|
||||
{
|
||||
NSString* clientTruthPath = @"/private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles/Library/ConfigurationProfiles/ClientTruth.plist";
|
||||
NSURL* clientTruthURL = [NSURL fileURLWithPath:clientTruthPath];
|
||||
NSDictionary* clientTruthDictionary = [NSDictionary dictionaryWithContentsOfURL:clientTruthURL];
|
||||
|
||||
if(!clientTruthDictionary) return;
|
||||
|
||||
NSArray* valuesArr;
|
||||
|
||||
NSDictionary* lsdAppRemoval = clientTruthDictionary[@"com.apple.lsd.appremoval"];
|
||||
if(lsdAppRemoval && [lsdAppRemoval isKindOfClass:NSDictionary.class])
|
||||
{
|
||||
NSDictionary* clientRestrictions = lsdAppRemoval[@"clientRestrictions"];
|
||||
if(clientRestrictions && [clientRestrictions isKindOfClass:NSDictionary.class])
|
||||
{
|
||||
NSDictionary* unionDict = clientRestrictions[@"union"];
|
||||
if(unionDict && [unionDict isKindOfClass:NSDictionary.class])
|
||||
{
|
||||
NSDictionary* removedSystemAppBundleIDs = unionDict[@"removedSystemAppBundleIDs"];
|
||||
if(removedSystemAppBundleIDs && [removedSystemAppBundleIDs isKindOfClass:NSDictionary.class])
|
||||
{
|
||||
valuesArr = removedSystemAppBundleIDs[@"values"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!valuesArr || !valuesArr.count) return;
|
||||
|
||||
NSMutableArray* valuesArrM = valuesArr.mutableCopy;
|
||||
__block BOOL changed = NO;
|
||||
|
||||
[valuesArrM enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSString* value, NSUInteger idx, BOOL *stop)
|
||||
{
|
||||
if(![value hasPrefix:@"com.apple."])
|
||||
{
|
||||
[valuesArrM removeObjectAtIndex:idx];
|
||||
changed = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
if(!changed) return;
|
||||
|
||||
NSMutableDictionary* clientTruthDictionaryM = (__bridge_transfer NSMutableDictionary*)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (__bridge CFDictionaryRef)clientTruthDictionary, kCFPropertyListMutableContainersAndLeaves);
|
||||
|
||||
clientTruthDictionaryM[@"com.apple.lsd.appremoval"][@"clientRestrictions"][@"union"][@"removedSystemAppBundleIDs"][@"values"] = valuesArrM;
|
||||
|
||||
[clientTruthDictionaryM writeToURL:clientTruthURL error:nil];
|
||||
}
|
||||
|
||||
int MAIN_NAME(int argc, char *argv[], char *envp[])
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
@ -1142,12 +1083,10 @@ int MAIN_NAME(int argc, char *argv[], char *envp[])
|
|||
NSString* cmd = [NSString stringWithUTF8String:argv[1]];
|
||||
if([cmd isEqualToString:@"install"])
|
||||
{
|
||||
NSLog(@"argc = %d", argc);
|
||||
BOOL force = NO;
|
||||
if(argc <= 2) return -3;
|
||||
if(argc > 3)
|
||||
{
|
||||
NSLog(@"argv3 = %s", argv[3]);
|
||||
if(!strcmp(argv[3], "force"))
|
||||
{
|
||||
force = YES;
|
||||
|
@ -1190,8 +1129,10 @@ int MAIN_NAME(int argc, char *argv[], char *envp[])
|
|||
refreshAppRegistrations();
|
||||
} else if([cmd isEqualToString:@"refresh-all"])
|
||||
{
|
||||
cleanRestrictions();
|
||||
[[LSApplicationWorkspace defaultWorkspace] _LSPrivateRebuildApplicationDatabasesForSystemApps:YES internal:YES user:YES];
|
||||
refreshAppRegistrations();
|
||||
killall(@"backboardd");
|
||||
} else if([cmd isEqualToString:@"install-persistence-helper"])
|
||||
{
|
||||
if(argc <= 2) return -3;
|
||||
|
@ -1205,6 +1146,17 @@ int MAIN_NAME(int argc, char *argv[], char *envp[])
|
|||
if(argc <= 2) return -3;
|
||||
NSString* userAppId = [NSString stringWithUTF8String:argv[2]];
|
||||
registerUserPersistenceHelper(userAppId);
|
||||
} else if([cmd isEqualToString:@"modify-registration"])
|
||||
{
|
||||
if(argc <= 3) return -3;
|
||||
NSString* appPath = [NSString stringWithUTF8String:argv[2]];
|
||||
NSString* newRegistration = [NSString stringWithUTF8String:argv[3]];
|
||||
|
||||
NSString* trollStoreMark = [[appPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"_TrollStore"];
|
||||
if([[NSFileManager defaultManager] fileExistsAtPath:trollStoreMark])
|
||||
{
|
||||
registerPath((char*)appPath.UTF8String, 0, [newRegistration isEqualToString:@"System"]);
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"returning %d", ret);
|
||||
|
|
|
@ -1 +1 @@
|
|||
extern void registerPath(char *path, int unregister);
|
||||
extern void registerPath(char *path, int unregister, BOOL system);
|
|
@ -89,7 +89,7 @@ NSDictionary* constructEnvironmentVariablesForContainerPath(NSString* containerP
|
|||
};
|
||||
}
|
||||
|
||||
void registerPath(char* cPath, int unregister)
|
||||
void registerPath(char* cPath, int unregister, BOOL system)
|
||||
{
|
||||
if(!cPath) return;
|
||||
NSString* path = [NSString stringWithUTF8String:cPath];
|
||||
|
@ -129,7 +129,7 @@ void registerPath(char* cPath, int unregister)
|
|||
|
||||
// Misc
|
||||
|
||||
dictToRegister[@"ApplicationType"] = @"System";
|
||||
dictToRegister[@"ApplicationType"] = system ? @"System" : @"User";
|
||||
dictToRegister[@"CFBundleIdentifier"] = appBundleID;
|
||||
dictToRegister[@"CodeInfoIdentifier"] = appBundleID;
|
||||
dictToRegister[@"CompatibilityState"] = @0;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
@interface LSApplicationProxy : LSBundleProxy
|
||||
+ (instancetype)applicationProxyForIdentifier:(NSString*)identifier;
|
||||
+ (instancetype)applicationProxyForBundleURL:(NSURL*)bundleURL;
|
||||
@property NSURL* bundleURL;
|
||||
@property NSString* bundleType;
|
||||
@property NSString* canonicalExecutablePath;
|
||||
|
@ -14,7 +15,8 @@
|
|||
@property (getter=isInstalled,nonatomic,readonly) BOOL installed;
|
||||
@property (getter=isPlaceholder,nonatomic,readonly) BOOL placeholder;
|
||||
@property (getter=isRestricted,nonatomic,readonly) BOOL restricted;
|
||||
@property (nonatomic,readonly) NSSet * claimedURLSchemes;
|
||||
@property (nonatomic,readonly) NSSet* claimedURLSchemes;
|
||||
@property (nonatomic,readonly) NSString* applicationType;
|
||||
@end
|
||||
|
||||
@interface LSApplicationWorkspace : NSObject
|
||||
|
|
|
@ -9,10 +9,6 @@
|
|||
|
||||
- (BOOL)isTrollStore;
|
||||
- (NSString*)getTrollStoreVersion;
|
||||
|
||||
- (void)startActivity:(NSString*)activity;
|
||||
- (void)stopActivityWithCompletion:(void (^)(void))completion;
|
||||
|
||||
- (void)downloadTrollStoreAndDo:(void (^)(NSString* localTrollStoreTarPath))doHandler;
|
||||
- (void)installTrollStorePressed;
|
||||
- (void)updateTrollStorePressed;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#import "TSListControllerShared.h"
|
||||
#import "TSUtil.h"
|
||||
#import "TSPresentationDelegate.h"
|
||||
|
||||
@implementation TSListControllerShared
|
||||
|
||||
|
@ -24,34 +25,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (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)downloadTrollStoreAndDo:(void (^)(NSString* localTrollStoreTarPath))doHandler
|
||||
{
|
||||
NSURL* trollStoreURL = [NSURL URLWithString:@"https://github.com/opa334/TrollStore/releases/latest/download/TrollStore.tar"];
|
||||
|
@ -67,9 +40,9 @@
|
|||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self stopActivityWithCompletion:^
|
||||
[TSPresentationDelegate stopActivityWithCompletion:^
|
||||
{
|
||||
[self presentViewController:errorAlert animated:YES completion:nil];
|
||||
[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
@ -89,11 +62,11 @@
|
|||
{
|
||||
if(update)
|
||||
{
|
||||
[self startActivity:@"Updating TrollStore"];
|
||||
[TSPresentationDelegate startActivity:@"Updating TrollStore"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self startActivity:@"Installing TrollStore"];
|
||||
[TSPresentationDelegate startActivity:@"Installing TrollStore"];
|
||||
}
|
||||
|
||||
[self downloadTrollStoreAndDo:^(NSString* tmpTarPath)
|
||||
|
@ -113,7 +86,7 @@
|
|||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self stopActivityWithCompletion:^
|
||||
[TSPresentationDelegate stopActivityWithCompletion:^
|
||||
{
|
||||
[self reloadSpecifiers];
|
||||
}];
|
||||
|
@ -124,12 +97,12 @@
|
|||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self stopActivityWithCompletion:^
|
||||
[TSPresentationDelegate 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];
|
||||
[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
@ -148,7 +121,7 @@
|
|||
|
||||
- (void)rebuildIconCachePressed
|
||||
{
|
||||
[self startActivity:@"Rebuilding Icon Cache"];
|
||||
[TSPresentationDelegate startActivity:@"Rebuilding Icon Cache"];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
|
||||
{
|
||||
|
@ -156,14 +129,14 @@
|
|||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self stopActivityWithCompletion:nil];
|
||||
[TSPresentationDelegate stopActivityWithCompletion:nil];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (void)refreshAppRegistrationsPressed
|
||||
{
|
||||
[self startActivity:@"Refreshing"];
|
||||
[TSPresentationDelegate startActivity:@"Refreshing"];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
|
||||
{
|
||||
|
@ -172,7 +145,7 @@
|
|||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self stopActivityWithCompletion:nil];
|
||||
[TSPresentationDelegate stopActivityWithCompletion:nil];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -198,7 +171,7 @@
|
|||
}];
|
||||
[uninstallWarningAlert addAction:continueAction];
|
||||
|
||||
[self presentViewController:uninstallWarningAlert animated:YES completion:nil];
|
||||
[TSPresentationDelegate presentViewController:uninstallWarningAlert animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,7 +201,7 @@
|
|||
}];
|
||||
[uninstallWarningAlert addAction:continueAction];
|
||||
|
||||
[self presentViewController:uninstallWarningAlert animated:YES completion:nil];
|
||||
[TSPresentationDelegate presentViewController:uninstallWarningAlert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,10 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface TSPresentationDelegate : NSObject
|
||||
@property (class) UIViewController* presentationViewController;
|
||||
@property (class) UIAlertController* activityController;
|
||||
+ (void)startActivity:(NSString*)activity withCancelHandler:(void (^)(void))cancelHandler;
|
||||
+ (void)startActivity:(NSString*)activity;
|
||||
+ (void)stopActivityWithCompletion:(void (^)(void))completion;
|
||||
+ (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion;
|
||||
@end
|
|
@ -0,0 +1,81 @@
|
|||
#import "TSPresentationDelegate.h"
|
||||
|
||||
@implementation TSPresentationDelegate
|
||||
|
||||
static UIViewController* g_presentationViewController;
|
||||
static UIAlertController* g_activityController;
|
||||
|
||||
+ (UIViewController*)presentationViewController
|
||||
{
|
||||
return g_presentationViewController;
|
||||
}
|
||||
|
||||
+ (void)setPresentationViewController:(UIViewController*)vc
|
||||
{
|
||||
g_presentationViewController = vc;
|
||||
}
|
||||
|
||||
+ (UIAlertController*)activityController
|
||||
{
|
||||
return g_activityController;
|
||||
}
|
||||
|
||||
+ (void)setActivityController:(UIAlertController*)ac
|
||||
{
|
||||
g_activityController = ac;
|
||||
}
|
||||
|
||||
+ (void)startActivity:(NSString*)activity withCancelHandler:(void (^)(void))cancelHandler
|
||||
{
|
||||
if(self.activityController)
|
||||
{
|
||||
self.activityController.title = activity;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.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];
|
||||
[self.activityController.view addSubview:activityIndicator];
|
||||
|
||||
if(cancelHandler)
|
||||
{
|
||||
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action)
|
||||
{
|
||||
self.activityController = nil;
|
||||
cancelHandler();
|
||||
}];
|
||||
[self.activityController addAction:cancelAction];
|
||||
}
|
||||
|
||||
[self presentViewController:self.activityController animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)startActivity:(NSString*)activity
|
||||
{
|
||||
[self startActivity:activity withCancelHandler:nil];
|
||||
}
|
||||
|
||||
+ (void)stopActivityWithCompletion:(void (^)(void))completion
|
||||
{
|
||||
if(!self.activityController) return;
|
||||
|
||||
[self.activityController dismissViewControllerAnimated:YES completion:^
|
||||
{
|
||||
self.activityController = nil;
|
||||
if(completion)
|
||||
{
|
||||
completion();
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
|
||||
{
|
||||
[self.presentationViewController presentViewController:viewControllerToPresent animated:flag completion:completion];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,6 +1,8 @@
|
|||
@import Foundation;
|
||||
#import "CoreServices.h"
|
||||
|
||||
#define TrollStoreErrorDomain @"TrollStoreErrorDomain"
|
||||
|
||||
extern void chineseWifiFixup(void);
|
||||
extern void loadMCMFramework(void);
|
||||
extern NSString* safe_getExecutablePath();
|
||||
|
@ -8,6 +10,7 @@ extern NSString* rootHelperPath(void);
|
|||
extern NSString* getNSStringFromFile(int fd);
|
||||
extern void printMultilineNSString(NSString* stringToPrint);
|
||||
extern int spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString** stdErr);
|
||||
extern void killall(NSString* processName);
|
||||
extern void respring(void);
|
||||
extern void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVersion));
|
||||
|
||||
|
@ -16,6 +19,14 @@ extern NSArray* trollStoreInstalledAppContainerPaths();
|
|||
extern NSString* trollStorePath();
|
||||
extern NSString* trollStoreAppPath();
|
||||
|
||||
#import <UIKit/UIAlertController.h>
|
||||
|
||||
@interface UIAlertController (Private)
|
||||
@property (setter=_setAttributedTitle:,getter=_attributedTitle,nonatomic,copy) NSAttributedString* attributedTitle;
|
||||
@property (setter=_setAttributedMessage:,getter=_attributedMessage,nonatomic,copy) NSAttributedString* attributedMessage;
|
||||
@property (nonatomic,retain) UIImage* image;
|
||||
@end
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PERSISTENCE_HELPER_TYPE_USER = 1 << 0,
|
||||
|
@ -24,3 +35,27 @@ typedef enum
|
|||
} PERSISTENCE_HELPER_TYPE;
|
||||
|
||||
extern LSApplicationProxy* findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE allowedTypes);
|
||||
|
||||
typedef struct __SecCode const *SecStaticCodeRef;
|
||||
|
||||
typedef CF_OPTIONS(uint32_t, SecCSFlags) {
|
||||
kSecCSDefaultFlags = 0
|
||||
};
|
||||
#define kSecCSRequirementInformation 1 << 2
|
||||
#define kSecCSSigningInformation 1 << 1
|
||||
|
||||
OSStatus SecStaticCodeCreateWithPathAndAttributes(CFURLRef path, SecCSFlags flags, CFDictionaryRef attributes, SecStaticCodeRef *staticCode);
|
||||
OSStatus SecCodeCopySigningInformation(SecStaticCodeRef code, SecCSFlags flags, CFDictionaryRef *information);
|
||||
CFDataRef SecCertificateCopyExtensionValue(SecCertificateRef certificate, CFTypeRef extensionOID, bool *isCritical);
|
||||
void SecPolicySetOptionsValue(SecPolicyRef policy, CFStringRef key, CFTypeRef value);
|
||||
|
||||
extern CFStringRef kSecCodeInfoEntitlementsDict;
|
||||
extern CFStringRef kSecCodeInfoCertificates;
|
||||
extern CFStringRef kSecPolicyAppleiPhoneApplicationSigning;
|
||||
extern CFStringRef kSecPolicyAppleiPhoneProfileApplicationSigning;
|
||||
extern CFStringRef kSecPolicyLeafMarkerOid;
|
||||
|
||||
extern SecStaticCodeRef getStaticCodeRef(NSString *binaryPath);
|
||||
extern NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef);
|
||||
extern NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString *binaryPath);
|
||||
extern NSDictionary* dumpEntitlementsFromBinaryData(NSData* binaryData);
|
104
Shared/TSUtil.m
104
Shared/TSUtil.m
|
@ -344,3 +344,107 @@ LSApplicationProxy* findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE allowedType
|
|||
|
||||
return outProxy;
|
||||
}
|
||||
|
||||
SecStaticCodeRef getStaticCodeRef(NSString *binaryPath)
|
||||
{
|
||||
if(binaryPath == nil)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CFURLRef binaryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)binaryPath, kCFURLPOSIXPathStyle, false);
|
||||
if(binaryURL == NULL)
|
||||
{
|
||||
NSLog(@"[getStaticCodeRef] failed to get URL to binary %@", binaryPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SecStaticCodeRef codeRef = NULL;
|
||||
OSStatus result;
|
||||
|
||||
result = SecStaticCodeCreateWithPathAndAttributes(binaryURL, kSecCSDefaultFlags, NULL, &codeRef);
|
||||
|
||||
CFRelease(binaryURL);
|
||||
|
||||
if(result != errSecSuccess)
|
||||
{
|
||||
NSLog(@"[getStaticCodeRef] failed to create static code for binary %@", binaryPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return codeRef;
|
||||
}
|
||||
|
||||
NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef)
|
||||
{
|
||||
if(codeRef == NULL)
|
||||
{
|
||||
NSLog(@"[dumpEntitlements] attempting to dump entitlements without a StaticCodeRef");
|
||||
return nil;
|
||||
}
|
||||
|
||||
CFDictionaryRef signingInfo = NULL;
|
||||
OSStatus result;
|
||||
|
||||
result = SecCodeCopySigningInformation(codeRef, kSecCSRequirementInformation, &signingInfo);
|
||||
|
||||
if(result != errSecSuccess)
|
||||
{
|
||||
NSLog(@"[dumpEntitlements] failed to copy signing info from static code");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSDictionary *entitlementsNSDict = nil;
|
||||
|
||||
CFDictionaryRef entitlements = CFDictionaryGetValue(signingInfo, kSecCodeInfoEntitlementsDict);
|
||||
if(entitlements == NULL)
|
||||
{
|
||||
NSLog(@"[dumpEntitlements] no entitlements specified");
|
||||
}
|
||||
else if(CFGetTypeID(entitlements) != CFDictionaryGetTypeID())
|
||||
{
|
||||
NSLog(@"[dumpEntitlements] invalid entitlements");
|
||||
}
|
||||
else
|
||||
{
|
||||
entitlementsNSDict = (__bridge NSDictionary *)(entitlements);
|
||||
NSLog(@"[dumpEntitlements] dumped %@", entitlementsNSDict);
|
||||
}
|
||||
|
||||
CFRelease(signingInfo);
|
||||
return entitlementsNSDict;
|
||||
}
|
||||
|
||||
NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString *binaryPath)
|
||||
{
|
||||
// This function is intended for one-shot checks. Main-event functions should retain/release their own SecStaticCodeRefs
|
||||
|
||||
if(binaryPath == nil)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
SecStaticCodeRef codeRef = getStaticCodeRef(binaryPath);
|
||||
if(codeRef == NULL)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSDictionary *entitlements = dumpEntitlements(codeRef);
|
||||
CFRelease(codeRef);
|
||||
|
||||
return entitlements;
|
||||
}
|
||||
|
||||
NSDictionary* dumpEntitlementsFromBinaryData(NSData* binaryData)
|
||||
{
|
||||
NSDictionary* entitlements;
|
||||
NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString];
|
||||
NSURL* tmpURL = [NSURL fileURLWithPath:tmpPath];
|
||||
if([binaryData writeToURL:tmpURL options:NSDataWritingAtomic error:nil])
|
||||
{
|
||||
entitlements = dumpEntitlementsFromBinaryAtPath(tmpPath);
|
||||
[[NSFileManager defaultManager] removeItemAtURL:tmpURL error:nil];
|
||||
}
|
||||
return entitlements;
|
||||
}
|
|
@ -52,7 +52,7 @@
|
|||
<string>iPhoneOS</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.2.2</string>
|
||||
<string>1.3</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIDeviceFamily</key>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#import "TSHRootViewController.h"
|
||||
#import <TSUtil.h>
|
||||
#import <TSPresentationDelegate.h>
|
||||
|
||||
@implementation TSHRootViewController
|
||||
|
||||
|
@ -11,6 +12,8 @@
|
|||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
TSPresentationDelegate.presentationViewController = self;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadSpecifiers) name:UIApplicationWillEnterForegroundNotification object:nil];
|
||||
|
||||
fetchLatestTrollStoreVersion(^(NSString* latestVersion)
|
||||
|
@ -123,9 +126,7 @@
|
|||
[_specifiers addObject:installTrollStoreSpecifier];
|
||||
}
|
||||
|
||||
NSString* executableName = NSBundle.mainBundle.bundleURL.lastPathComponent;
|
||||
NSString* backupExecutableName = [executableName stringByAppendingString:@"_TROLLSTORE_BACKUP"];
|
||||
NSString* backupPath = [[NSBundle.mainBundle.bundleURL.path stringByDeletingLastPathComponent] stringByAppendingPathComponent:backupExecutableName];
|
||||
NSString* backupPath = [safe_getExecutablePath() stringByAppendingString:@"_TROLLSTORE_BACKUP"];
|
||||
if([[NSFileManager defaultManager] fileExistsAtPath:backupPath])
|
||||
{
|
||||
PSSpecifier* uninstallHelperGroupSpecifier = [PSSpecifier emptyGroupSpecifier];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Package: com.opa334.trollstorehelper
|
||||
Name: TrollStore Helper
|
||||
Version: 1.2.2
|
||||
Version: 1.3
|
||||
Architecture: iphoneos-arm
|
||||
Description: Helper utility to install and manage TrollStore!
|
||||
Maintainer: opa334
|
||||
|
|
|
@ -7,7 +7,8 @@ APPLICATION_NAME = TrollStore
|
|||
|
||||
TrollStore_FILES = $(wildcard *.m) $(wildcard ../Shared/*.m)
|
||||
TrollStore_FRAMEWORKS = UIKit CoreGraphics CoreServices
|
||||
TrollStore_PRIVATE_FRAMEWORKS = Preferences
|
||||
TrollStore_PRIVATE_FRAMEWORKS = Preferences MobileIcons
|
||||
TrollStore_LIBRARIES = archive
|
||||
TrollStore_CFLAGS = -fobjc-arc -I../Shared
|
||||
TrollStore_CODESIGN_FLAGS = -Sentitlements.plist -K../cert.p12
|
||||
|
||||
|
|
Binary file not shown.
BIN
TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/Kx4-55-vNS-view-9BB-B5-Vbi.nib
generated
Normal file
BIN
TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/Kx4-55-vNS-view-9BB-B5-Vbi.nib
generated
Normal file
Binary file not shown.
BIN
TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/UITabBarController-9el-pn-lH0.nib
generated
Normal file
BIN
TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/UITabBarController-9el-pn-lH0.nib
generated
Normal file
Binary file not shown.
BIN
TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/X3T-Aa-nEE-view-vAu-RC-m7d.nib
generated
Normal file
BIN
TrollStore/Resources/Base.lproj/LaunchScreen.storyboardc/X3T-Aa-nEE-view-vAu-RC-m7d.nib
generated
Normal file
Binary file not shown.
|
@ -50,7 +50,7 @@
|
|||
<string>iPhoneOS</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.2.2</string>
|
||||
<string>1.3</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIDeviceFamily</key>
|
||||
|
@ -62,81 +62,8 @@
|
|||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UILaunchImageFile</key>
|
||||
<string>LaunchImage</string>
|
||||
<key>UILaunchImages</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UILaunchImageMinimumOSVersion</key>
|
||||
<string>7.0</string>
|
||||
<key>UILaunchImageName</key>
|
||||
<string>LaunchImage</string>
|
||||
<key>UILaunchImageOrientation</key>
|
||||
<string>Portrait</string>
|
||||
<key>UILaunchImageSize</key>
|
||||
<string>{320, 480}</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UILaunchImageMinimumOSVersion</key>
|
||||
<string>7.0</string>
|
||||
<key>UILaunchImageName</key>
|
||||
<string>LaunchImage-700-568h</string>
|
||||
<key>UILaunchImageOrientation</key>
|
||||
<string>Portrait</string>
|
||||
<key>UILaunchImageSize</key>
|
||||
<string>{320, 568}</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UILaunchImageMinimumOSVersion</key>
|
||||
<string>7.0</string>
|
||||
<key>UILaunchImageName</key>
|
||||
<string>LaunchImage-Portrait</string>
|
||||
<key>UILaunchImageOrientation</key>
|
||||
<string>Portrait</string>
|
||||
<key>UILaunchImageSize</key>
|
||||
<string>{768, 1024}</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UILaunchImageMinimumOSVersion</key>
|
||||
<string>7.0</string>
|
||||
<key>UILaunchImageName</key>
|
||||
<string>LaunchImage-Landscape</string>
|
||||
<key>UILaunchImageOrientation</key>
|
||||
<string>Landscape</string>
|
||||
<key>UILaunchImageSize</key>
|
||||
<string>{768, 1024}</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UILaunchImageMinimumOSVersion</key>
|
||||
<string>8.0</string>
|
||||
<key>UILaunchImageName</key>
|
||||
<string>LaunchImage-800-667h</string>
|
||||
<key>UILaunchImageOrientation</key>
|
||||
<string>Portrait</string>
|
||||
<key>UILaunchImageSize</key>
|
||||
<string>{375, 667}</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UILaunchImageMinimumOSVersion</key>
|
||||
<string>8.0</string>
|
||||
<key>UILaunchImageName</key>
|
||||
<string>LaunchImage-800-Portrait-736h</string>
|
||||
<key>UILaunchImageOrientation</key>
|
||||
<string>Portrait</string>
|
||||
<key>UILaunchImageSize</key>
|
||||
<string>{414, 736}</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UILaunchImageMinimumOSVersion</key>
|
||||
<string>8.0</string>
|
||||
<key>UILaunchImageName</key>
|
||||
<string>LaunchImage-800-Landscape-736h</string>
|
||||
<key>UILaunchImageOrientation</key>
|
||||
<string>Landscape</string>
|
||||
<key>UILaunchImageSize</key>
|
||||
<string>{414, 736}</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
|
@ -248,6 +175,17 @@
|
|||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>com.apple.Magnifier</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>apple-magnifier</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<false/>
|
||||
<key>TSRootBinaries</key>
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// TSIPAInfo.h
|
||||
// IPAInfo
|
||||
//
|
||||
// Created by Lars Fröder on 22.10.22.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <libarchive/archive.h>
|
||||
#import <libarchive/archive_entry.h>
|
||||
@import UIKit;
|
||||
|
||||
@interface TSAppInfo : NSObject
|
||||
{
|
||||
NSString* _path;
|
||||
BOOL _isArchive;
|
||||
struct archive* _archive;
|
||||
|
||||
NSString* _cachedAppBundleName;
|
||||
NSString* _cachedRegistrationState;
|
||||
NSDictionary* _cachedInfoDictionary;
|
||||
NSDictionary* _cachedInfoDictionariesByPluginSubpaths;
|
||||
NSDictionary* _cachedEntitlementsByBinarySubpaths;
|
||||
UIImage* _cachedPreviewIcon;
|
||||
int64_t _cachedSize;
|
||||
}
|
||||
|
||||
- (instancetype)initWithIPAPath:(NSString*)ipaPath;
|
||||
- (instancetype)initWithAppBundlePath:(NSString*)bundlePath;
|
||||
- (NSError*)determineAppBundleName;
|
||||
- (NSError*)loadInfoDictionary;
|
||||
- (NSError*)loadEntitlements;
|
||||
- (NSError*)loadPreviewIcon;
|
||||
|
||||
- (NSError*)sync_loadBasicInfo;
|
||||
- (NSError*)sync_loadInfo;
|
||||
|
||||
- (void)loadBasicInfoWithCompletion:(void (^)(NSError*))completionHandler;
|
||||
- (void)loadInfoWithCompletion:(void (^)(NSError*))completionHandler;
|
||||
|
||||
- (NSString*)displayName;
|
||||
- (NSString*)bundleIdentifier;
|
||||
- (NSString*)versionString;
|
||||
- (NSString*)sizeString;
|
||||
- (NSString*)bundlePath;
|
||||
- (NSString*)registrationState;
|
||||
|
||||
- (UIImage*)iconForSize:(CGSize)size;
|
||||
|
||||
- (NSAttributedString*)detailedInfoTitle;
|
||||
- (NSAttributedString*)detailedInfoDescription;
|
||||
//- (UIImage*)image;
|
||||
- (void)log;
|
||||
|
||||
@end
|
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +1,13 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
#import "TSAppInfo.h"
|
||||
|
||||
@interface TSAppTableViewController : UITableViewController
|
||||
@interface TSAppTableViewController : UITableViewController <UISearchResultsUpdating, UIDocumentPickerDelegate>
|
||||
{
|
||||
UIImage* _placeholderIcon;
|
||||
NSArray* _cachedAppPaths;
|
||||
NSArray<TSAppInfo*>* _cachedAppInfos;
|
||||
NSMutableDictionary* _cachedIcons;
|
||||
UISearchController* _searchController;
|
||||
NSString* _searchKey;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,6 +1,10 @@
|
|||
#import "TSAppTableViewController.h"
|
||||
|
||||
#import "TSApplicationsManager.h"
|
||||
#import <TSPresentationDelegate.h>
|
||||
#import "TSInstallationController.h"
|
||||
#import "TSUtil.h"
|
||||
@import UniformTypeIdentifiers;
|
||||
|
||||
#define ICON_FORMAT_IPAD 8
|
||||
#define ICON_FORMAT_IPHONE 10
|
||||
|
@ -34,16 +38,37 @@ UIImage* imageWithSize(UIImage* image, CGSize size)
|
|||
|
||||
@implementation TSAppTableViewController
|
||||
|
||||
- (void)loadCachedAppPaths
|
||||
- (void)loadAppInfos
|
||||
{
|
||||
NSArray* appPaths = [[TSApplicationsManager sharedInstance] installedAppPaths];
|
||||
NSMutableArray<TSAppInfo*>* appInfos = [NSMutableArray new];
|
||||
|
||||
_cachedAppPaths = [appPaths sortedArrayUsingComparator:^NSComparisonResult(NSString* appPathA, NSString* appPathB) {
|
||||
NSString* displayNameA = [[TSApplicationsManager sharedInstance] displayNameForAppPath:appPathA];
|
||||
NSString* displayNameB = [[TSApplicationsManager sharedInstance] displayNameForAppPath:appPathB];
|
||||
for(NSString* appPath in appPaths)
|
||||
{
|
||||
TSAppInfo* appInfo = [[TSAppInfo alloc] initWithAppBundlePath:appPath];
|
||||
[appInfo sync_loadBasicInfo];
|
||||
[appInfos addObject:appInfo];
|
||||
}
|
||||
|
||||
return [displayNameA localizedStandardCompare:displayNameB];
|
||||
if(_searchKey && ![_searchKey isEqualToString:@""])
|
||||
{
|
||||
[appInfos enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(TSAppInfo* appInfo, NSUInteger idx, BOOL* stop)
|
||||
{
|
||||
NSString* appName = [appInfo displayName];
|
||||
BOOL nameMatch = [appName rangeOfString:_searchKey options:NSCaseInsensitiveSearch range:NSMakeRange(0, [appName length]) locale:[NSLocale currentLocale]].location != NSNotFound;
|
||||
if(!nameMatch)
|
||||
{
|
||||
[appInfos removeObjectAtIndex:idx];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
[appInfos sortUsingComparator:^(TSAppInfo* appInfoA, TSAppInfo* appInfoB)
|
||||
{
|
||||
return [[appInfoA displayName] localizedStandardCompare:[appInfoB displayName]];
|
||||
}];
|
||||
|
||||
_cachedAppInfos = appInfos.copy;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
|
@ -51,7 +76,7 @@ UIImage* imageWithSize(UIImage* image, CGSize size)
|
|||
self = [super init];
|
||||
if(self)
|
||||
{
|
||||
[self loadCachedAppPaths];
|
||||
[self loadAppInfos];
|
||||
_placeholderIcon = [UIImage _applicationIconImageForBundleIdentifier:@"com.apple.WebSheet" format:iconFormatToUse() scale:[UIScreen mainScreen].scale];
|
||||
_cachedIcons = [NSMutableDictionary new];
|
||||
}
|
||||
|
@ -60,7 +85,7 @@ UIImage* imageWithSize(UIImage* image, CGSize size)
|
|||
|
||||
- (void)reloadTable
|
||||
{
|
||||
[self loadCachedAppPaths];
|
||||
[self loadAppInfos];
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self.tableView reloadData];
|
||||
|
@ -70,42 +95,187 @@ UIImage* imageWithSize(UIImage* image, CGSize size)
|
|||
- (void)loadView
|
||||
{
|
||||
[super loadView];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(reloadTable)
|
||||
name:@"ApplicationsChanged"
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadTable) name:@"ApplicationsChanged" object:nil];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.tableView.allowsMultipleSelectionDuringEditing = NO;
|
||||
|
||||
[self _setUpNavigationBar];
|
||||
[self _setUpSearchBar];
|
||||
}
|
||||
|
||||
- (void)showError:(NSError*)error
|
||||
- (void)_setUpNavigationBar
|
||||
{
|
||||
UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Error %ld", error.code] message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil];
|
||||
[errorAlert addAction:closeAction];
|
||||
[self presentViewController:errorAlert animated:YES completion:nil];
|
||||
UIAction* installFromFileAction = [UIAction actionWithTitle:@"Install IPA File" image:[UIImage systemImageNamed:@"doc.badge.plus"] identifier:@"InstallIPAFile" handler:^(__kindof UIAction *action)
|
||||
{
|
||||
UTType* ipaType = [UTType typeWithFilenameExtension:@"ipa" conformingToType:UTTypeData];
|
||||
UTType* tipaType = [UTType typeWithFilenameExtension:@"tipa" conformingToType:UTTypeData];
|
||||
|
||||
UIDocumentPickerViewController* documentPickerVC = [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:@[ipaType, tipaType]];
|
||||
//UIDocumentPickerViewController* documentPickerVC = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"com.apple.itunes.ipa", @"com.opa334.trollstore.tipa"] inMode:UIDocumentPickerModeOpen];
|
||||
documentPickerVC.allowsMultipleSelection = NO;
|
||||
documentPickerVC.delegate = self;
|
||||
|
||||
[TSPresentationDelegate presentViewController:documentPickerVC animated:YES completion:nil];
|
||||
}];
|
||||
|
||||
UIAction* installFromURLAction = [UIAction actionWithTitle:@"Install from URL" image:[UIImage systemImageNamed:@"link.badge.plus"] identifier:@"InstallFromURL" handler:^(__kindof UIAction *action)
|
||||
{
|
||||
UIAlertController* installURLController = [UIAlertController alertControllerWithTitle:@"Install from URL" message:@"" preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[installURLController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
textField.placeholder = @"URL";
|
||||
}];
|
||||
|
||||
UIAlertAction* installAction = [UIAlertAction actionWithTitle:@"Install" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
|
||||
{
|
||||
NSString* URLString = installURLController.textFields.firstObject.text;
|
||||
NSURL* remoteURL = [NSURL URLWithString:URLString];
|
||||
|
||||
[TSInstallationController handleAppInstallFromRemoteURL:remoteURL completion:nil];
|
||||
}];
|
||||
[installURLController addAction:installAction];
|
||||
|
||||
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
|
||||
[installURLController addAction:cancelAction];
|
||||
|
||||
[TSPresentationDelegate presentViewController:installURLController animated:YES completion:nil];
|
||||
}];
|
||||
|
||||
UIMenu* installMenu = [UIMenu menuWithChildren:@[installFromFileAction, installFromURLAction]];
|
||||
|
||||
UIBarButtonItem* installBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"plus"] menu:installMenu];
|
||||
|
||||
self.navigationItem.rightBarButtonItems = @[installBarButtonItem];
|
||||
}
|
||||
|
||||
- (void)openAppPressedForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (void)_setUpSearchBar
|
||||
{
|
||||
_searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
|
||||
_searchController.searchResultsUpdater = self;
|
||||
_searchController.obscuresBackgroundDuringPresentation = NO;
|
||||
self.navigationItem.searchController = _searchController;
|
||||
self.navigationItem.hidesSearchBarWhenScrolling = YES;
|
||||
}
|
||||
|
||||
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
|
||||
{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
_searchKey = searchController.searchBar.text;
|
||||
[self reloadTable];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls
|
||||
{
|
||||
NSString* pathToIPA = urls.firstObject.path;
|
||||
[TSInstallationController presentInstallationAlertForFile:pathToIPA completion:nil];
|
||||
}
|
||||
|
||||
- (void)openAppPressedForRowAtIndexPath:(NSIndexPath*)indexPath
|
||||
{
|
||||
TSApplicationsManager* appsManager = [TSApplicationsManager sharedInstance];
|
||||
|
||||
NSString* appPath = _cachedAppPaths[indexPath.row];
|
||||
NSString* appId = [appsManager appIdForAppPath:appPath];
|
||||
TSAppInfo* appInfo = _cachedAppInfos[indexPath.row];
|
||||
NSString* appId = [appInfo bundleIdentifier];
|
||||
BOOL didOpen = [appsManager openApplicationWithBundleID:appId];
|
||||
|
||||
// if we failed to open the app, show an alert
|
||||
if (!didOpen) {
|
||||
NSString *failMessage = [NSString stringWithFormat: @"Failed to open %@", appId];
|
||||
UIAlertController* didFailController = [UIAlertController alertControllerWithTitle:failMessage message: nil preferredStyle:UIAlertControllerStyleAlert];
|
||||
if(!didOpen)
|
||||
{
|
||||
NSString* failMessage = @"";
|
||||
if([[appInfo registrationState] isEqualToString:@"User"])
|
||||
{
|
||||
failMessage = @"This app was not able to launch because it has a \"User\" registration state, register it as \"System\" and try again.";
|
||||
}
|
||||
|
||||
NSString* failTitle = [NSString stringWithFormat:@"Failed to open %@", appId];
|
||||
UIAlertController* didFailController = [UIAlertController alertControllerWithTitle:failTitle message:failMessage preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
|
||||
|
||||
[didFailController addAction: cancelAction];
|
||||
[self presentViewController:didFailController animated:YES completion:nil];
|
||||
[didFailController addAction:cancelAction];
|
||||
[TSPresentationDelegate presentViewController:didFailController animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showDetailsPressedForRowAtIndexPath:(NSIndexPath*)indexPath
|
||||
{
|
||||
TSAppInfo* appInfo = _cachedAppInfos[indexPath.row];
|
||||
|
||||
[appInfo loadInfoWithCompletion:^(NSError* error)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
if(!error)
|
||||
{
|
||||
UIAlertController* detailsAlert = [UIAlertController alertControllerWithTitle:@"" message:@"" preferredStyle:UIAlertControllerStyleAlert];
|
||||
detailsAlert.attributedTitle = [appInfo detailedInfoTitle];
|
||||
detailsAlert.attributedMessage = [appInfo detailedInfoDescription];
|
||||
|
||||
UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil];
|
||||
[detailsAlert addAction:closeAction];
|
||||
|
||||
[TSPresentationDelegate presentViewController:detailsAlert animated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Parse Error %ld", error.code] message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil];
|
||||
[errorAlert addAction:closeAction];
|
||||
|
||||
[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)changeAppRegistrationForRowAtIndexPath:(NSIndexPath*)indexPath toState:(NSString*)newState
|
||||
{
|
||||
TSAppInfo* appInfo = _cachedAppInfos[indexPath.row];
|
||||
|
||||
if([newState isEqualToString:@"User"])
|
||||
{
|
||||
NSString* title = [NSString stringWithFormat:@"Switching '%@' to \"User\" Registration", [appInfo displayName]];
|
||||
UIAlertController* confirmationAlert = [UIAlertController alertControllerWithTitle:title message:@"Switching this app to a \"User\" registration will make it unlaunchable after the next respring because the bugs exploited in TrollStore only affect apps registered as \"System\".\nThe purpose of this option is to make the app temporarily show up in settings, so you can adjust the settings and then switch it back to a \"System\" registration (TrollStore installed apps do not show up in settings otherwise). Additionally, the \"User\" registration state is also useful to temporarily fix iTunes file sharing, which also doesn't work for TrollStore installed apps otherwise.\nWhen you're done making the changes you need and want the app to become launchable again, you will need to switch it back to \"System\" state in TrollStore." preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction* switchToUserAction = [UIAlertAction actionWithTitle:@"Switch to \"User\"" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
|
||||
{
|
||||
[[TSApplicationsManager sharedInstance] changeAppRegistration:[appInfo bundlePath] toState:newState];
|
||||
[appInfo sync_loadBasicInfo];
|
||||
}];
|
||||
|
||||
[confirmationAlert addAction:switchToUserAction];
|
||||
|
||||
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
|
||||
|
||||
[confirmationAlert addAction:cancelAction];
|
||||
|
||||
[TSPresentationDelegate presentViewController:confirmationAlert animated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[TSApplicationsManager sharedInstance] changeAppRegistration:[appInfo bundlePath] toState:newState];
|
||||
[appInfo sync_loadBasicInfo];
|
||||
|
||||
NSString* title = [NSString stringWithFormat:@"Switched '%@' to \"System\" Registration", [appInfo displayName]];
|
||||
|
||||
UIAlertController* infoAlert = [UIAlertController alertControllerWithTitle:title message:@"The app has been switched to the \"System\" registration state and will become launchable again after a respring." preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction* respringAction = [UIAlertAction actionWithTitle:@"Respring" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
|
||||
{
|
||||
respring();
|
||||
}];
|
||||
|
||||
[infoAlert addAction:respringAction];
|
||||
|
||||
UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil];
|
||||
|
||||
[infoAlert addAction:closeAction];
|
||||
|
||||
[TSPresentationDelegate presentViewController:infoAlert animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,9 +283,11 @@ UIImage* imageWithSize(UIImage* image, CGSize size)
|
|||
{
|
||||
TSApplicationsManager* appsManager = [TSApplicationsManager sharedInstance];
|
||||
|
||||
NSString* appPath = _cachedAppPaths[indexPath.row];
|
||||
NSString* appId = [appsManager appIdForAppPath:appPath];
|
||||
NSString* appName = [appsManager displayNameForAppPath:appPath];
|
||||
TSAppInfo* appInfo = _cachedAppInfos[indexPath.row];
|
||||
|
||||
NSString* appPath = [appInfo bundlePath];
|
||||
NSString* appId = [appInfo bundleIdentifier];
|
||||
NSString* appName = [appInfo displayName];
|
||||
|
||||
UIAlertController* confirmAlert = [UIAlertController alertControllerWithTitle:@"Confirm Uninstallation" message:[NSString stringWithFormat:@"Uninstalling the app '%@' will delete the app and all data associated to it.", appName] preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
|
@ -135,7 +307,7 @@ UIImage* imageWithSize(UIImage* image, CGSize size)
|
|||
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
|
||||
[confirmAlert addAction:cancelAction];
|
||||
|
||||
[self presentViewController:confirmAlert animated:YES completion:nil];
|
||||
[TSPresentationDelegate presentViewController:confirmAlert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)deselectRow
|
||||
|
@ -150,7 +322,7 @@ UIImage* imageWithSize(UIImage* image, CGSize size)
|
|||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return _cachedAppPaths.count;
|
||||
return _cachedAppInfos.count;
|
||||
}
|
||||
|
||||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
|
||||
|
@ -164,12 +336,12 @@ UIImage* imageWithSize(UIImage* image, CGSize size)
|
|||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"ApplicationCell"];
|
||||
}
|
||||
|
||||
NSString* appPath = _cachedAppPaths[indexPath.row];
|
||||
NSString* appId = [[TSApplicationsManager sharedInstance] appIdForAppPath:appPath];
|
||||
NSString* appVersion = [[TSApplicationsManager sharedInstance] versionStringForAppPath:appPath];
|
||||
TSAppInfo* appInfo = _cachedAppInfos[indexPath.row];
|
||||
NSString* appId = [appInfo bundleIdentifier];
|
||||
NSString* appVersion = [appInfo versionString];
|
||||
|
||||
// Configure the cell...
|
||||
cell.textLabel.text = [[TSApplicationsManager sharedInstance] displayNameForAppPath:appPath];
|
||||
cell.textLabel.text = [appInfo displayName];
|
||||
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ • %@", appVersion, appId];
|
||||
cell.imageView.layer.borderWidth = 1;
|
||||
cell.imageView.layer.borderColor = [UIColor.labelColor colorWithAlphaComponent:0.1].CGColor;
|
||||
|
@ -228,32 +400,47 @@ UIImage* imageWithSize(UIImage* image, CGSize size)
|
|||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TSApplicationsManager* appsManager = [TSApplicationsManager sharedInstance];
|
||||
TSAppInfo* appInfo = _cachedAppInfos[indexPath.row];
|
||||
|
||||
NSString* appPath = _cachedAppPaths[indexPath.row];
|
||||
NSString* appId = [appsManager appIdForAppPath:appPath];
|
||||
NSString* appName = [appsManager displayNameForAppPath:appPath];
|
||||
|
||||
UIAlertController* appSelectAlert = [UIAlertController alertControllerWithTitle:appName message:appId?:@"" preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
|
||||
/*UIAlertAction* detachAction = [UIAlertAction actionWithTitle:@"Detach from TrollStore" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
|
||||
{
|
||||
int detachRet = [appsManager detachFromApp:appId];
|
||||
if(detachRet != 0)
|
||||
{
|
||||
[self showError:[appsManager errorForCode:detachRet]];
|
||||
}
|
||||
[self deselectRow];
|
||||
}];
|
||||
[appSelectAlert addAction:detachAction];*/
|
||||
NSString* appId = [appInfo bundleIdentifier];
|
||||
NSString* appName = [appInfo displayName];
|
||||
|
||||
UIAlertController* appSelectAlert = [UIAlertController alertControllerWithTitle:appName?:@"" message:appId?:@"" preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
|
||||
UIAlertAction* openAction = [UIAlertAction actionWithTitle:@"Open" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
|
||||
{
|
||||
[self openAppPressedForRowAtIndexPath:indexPath];
|
||||
[self deselectRow];
|
||||
}];
|
||||
[appSelectAlert addAction: openAction];
|
||||
[appSelectAlert addAction:openAction];
|
||||
|
||||
UIAlertAction* showDetailsAction = [UIAlertAction actionWithTitle:@"Show Details" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
|
||||
{
|
||||
[self showDetailsPressedForRowAtIndexPath:indexPath];
|
||||
[self deselectRow];
|
||||
}];
|
||||
[appSelectAlert addAction:showDetailsAction];
|
||||
|
||||
NSString* switchState;
|
||||
NSString* registrationState = [appInfo registrationState];
|
||||
UIAlertActionStyle switchActionStyle = 0;
|
||||
if([registrationState isEqualToString:@"System"])
|
||||
{
|
||||
switchState = @"User";
|
||||
switchActionStyle = UIAlertActionStyleDestructive;
|
||||
}
|
||||
else if([registrationState isEqualToString:@"User"])
|
||||
{
|
||||
switchState = @"System";
|
||||
switchActionStyle = UIAlertActionStyleDefault;
|
||||
}
|
||||
|
||||
UIAlertAction* switchRegistrationAction = [UIAlertAction actionWithTitle:[NSString stringWithFormat:@"Switch to \"%@\" Registration", switchState] style:switchActionStyle handler:^(UIAlertAction* action)
|
||||
{
|
||||
[self changeAppRegistrationForRowAtIndexPath:indexPath toState:switchState];
|
||||
[self deselectRow];
|
||||
}];
|
||||
[appSelectAlert addAction:switchRegistrationAction];
|
||||
|
||||
UIAlertAction* uninstallAction = [UIAlertAction actionWithTitle:@"Uninstall App" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)
|
||||
{
|
||||
|
@ -271,7 +458,7 @@ UIImage* imageWithSize(UIImage* image, CGSize size)
|
|||
appSelectAlert.popoverPresentationController.sourceView = tableView;
|
||||
appSelectAlert.popoverPresentationController.sourceRect = [tableView rectForRowAtIndexPath:indexPath];
|
||||
|
||||
[self presentViewController:appSelectAlert animated:YES completion:nil];
|
||||
[TSPresentationDelegate presentViewController:appSelectAlert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
|
@ -9,10 +9,6 @@
|
|||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (NSArray*)installedAppPaths;
|
||||
- (NSDictionary*)infoDictionaryForAppPath:(NSString*)appPath;
|
||||
- (NSString*)appIdForAppPath:(NSString*)appPath;
|
||||
- (NSString*)displayNameForAppPath:(NSString*)appPath;
|
||||
- (NSString*)versionStringForAppPath:(NSString*)appPath;
|
||||
|
||||
- (NSError*)errorForCode:(int)code;
|
||||
- (int)installIpa:(NSString*)pathToIpa force:(BOOL)force log:(NSString**)logOut;
|
||||
|
@ -20,6 +16,6 @@
|
|||
- (int)uninstallApp:(NSString*)appId;
|
||||
- (int)uninstallAppByPath:(NSString*)path;
|
||||
- (BOOL)openApplicationWithBundleID:(NSString *)appID;
|
||||
//- (int)detachFromApp:(NSString*)appId;
|
||||
- (int)changeAppRegistration:(NSString*)appPath toState:(NSString*)newState;
|
||||
|
||||
@end
|
|
@ -1,8 +1,6 @@
|
|||
#import "TSApplicationsManager.h"
|
||||
#import <TSUtil.h>
|
||||
|
||||
#define TrollStoreErrorDomain @"TrollStoreErrorDomain"
|
||||
|
||||
@implementation TSApplicationsManager
|
||||
|
||||
+ (instancetype)sharedInstance
|
||||
|
@ -20,55 +18,6 @@
|
|||
return trollStoreInstalledAppBundlePaths();
|
||||
}
|
||||
|
||||
- (NSDictionary*)infoDictionaryForAppPath:(NSString*)appPath
|
||||
{
|
||||
NSString* infoPlistPath = [appPath stringByAppendingPathComponent:@"Info.plist"];
|
||||
NSError* error;
|
||||
NSDictionary* infoDict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:infoPlistPath] error:&error];
|
||||
if(error)
|
||||
{
|
||||
NSLog(@"error getting info dict: %@", error);
|
||||
}
|
||||
return infoDict;
|
||||
}
|
||||
|
||||
- (NSString*)appIdForAppPath:(NSString*)appPath
|
||||
{
|
||||
return [self infoDictionaryForAppPath:appPath][@"CFBundleIdentifier"];
|
||||
}
|
||||
|
||||
- (NSString*)displayNameForAppPath:(NSString*)appPath
|
||||
{
|
||||
NSDictionary* infoDict = [self infoDictionaryForAppPath:appPath];
|
||||
NSString* displayName = infoDict[@"CFBundleDisplayName"];
|
||||
if(![displayName isKindOfClass:[NSString class]]) displayName = nil;
|
||||
if(!displayName || [displayName isEqualToString:@""])
|
||||
{
|
||||
displayName = infoDict[@"CFBundleName"];
|
||||
if(![displayName isKindOfClass:[NSString class]]) displayName = nil;
|
||||
if(!displayName || [displayName isEqualToString:@""])
|
||||
{
|
||||
displayName = infoDict[@"CFBundleExecutable"];
|
||||
if(![displayName isKindOfClass:[NSString class]]) displayName = [appPath lastPathComponent];
|
||||
}
|
||||
}
|
||||
|
||||
return displayName;
|
||||
}
|
||||
|
||||
- (NSString*)versionStringForAppPath:(NSString*)appPath
|
||||
{
|
||||
NSDictionary* infoDict = [self infoDictionaryForAppPath:appPath];
|
||||
NSString* versionString = infoDict[@"CFBundleShortVersionString"];
|
||||
|
||||
if(!versionString)
|
||||
{
|
||||
versionString = infoDict[@"CFBundleVersion"];
|
||||
}
|
||||
|
||||
return versionString;
|
||||
}
|
||||
|
||||
- (NSError*)errorForCode:(int)code
|
||||
{
|
||||
NSString* errorDescription = @"Unknown Error";
|
||||
|
@ -163,12 +112,10 @@
|
|||
return [[LSApplicationWorkspace defaultWorkspace] openApplicationWithBundleID:appId];
|
||||
}
|
||||
|
||||
/*- (int)detachFromApp:(NSString*)appId
|
||||
- (int)changeAppRegistration:(NSString*)appPath toState:(NSString*)newState
|
||||
{
|
||||
if(!appId) return -200;
|
||||
int ret = spawnRoot(rootHelperPath(), @[@"detach", appId], nil, nil);
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"ApplicationsChanged" object:nil];
|
||||
return ret;
|
||||
}*/
|
||||
if(!appPath || !newState) return -200;
|
||||
return spawnRoot(rootHelperPath(), @[@"modify-registration", appPath, newState], nil, nil);
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,12 @@
|
|||
@import Foundation;
|
||||
|
||||
@interface TSInstallationController : NSObject
|
||||
|
||||
+ (void)presentInstallationAlertForFile:(NSString*)pathToIPA completion:(void (^)(BOOL, NSError*))completion;
|
||||
|
||||
+ (void)handleAppInstallFromFile:(NSString*)pathToIPA forceInstall:(BOOL)force completion:(void (^)(BOOL, NSError*))completion;
|
||||
+ (void)handleAppInstallFromFile:(NSString*)pathToIPA completion:(void (^)(BOOL, NSError*))completion;
|
||||
|
||||
+ (void)handleAppInstallFromRemoteURL:(NSURL*)remoteURL completion:(void (^)(BOOL, NSError*))completion;
|
||||
|
||||
@end
|
|
@ -0,0 +1,168 @@
|
|||
#import "TSInstallationController.h"
|
||||
|
||||
#import "TSApplicationsManager.h"
|
||||
#import "TSAppInfo.h"
|
||||
#import <TSUtil.h>
|
||||
#import <TSPresentationDelegate.h>
|
||||
|
||||
@implementation TSInstallationController
|
||||
|
||||
+ (void)handleAppInstallFromFile:(NSString*)pathToIPA forceInstall:(BOOL)force completion:(void (^)(BOOL, NSError*))completionBlock
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[TSPresentationDelegate startActivity:@"Installing"];
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
|
||||
{
|
||||
// Install IPA
|
||||
//NSString* log;
|
||||
int ret = [[TSApplicationsManager sharedInstance] installIpa:pathToIPA force:force log:nil];
|
||||
|
||||
NSError* error;
|
||||
if(ret != 0)
|
||||
{
|
||||
error = [[TSApplicationsManager sharedInstance] errorForCode:ret];
|
||||
}
|
||||
|
||||
NSLog(@"installed app! ret:%d, error: %@", ret, error);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[TSPresentationDelegate stopActivityWithCompletion:^
|
||||
{
|
||||
if(ret != 0)
|
||||
{
|
||||
UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Install Error %d", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
|
||||
{
|
||||
if(ret == 171)
|
||||
{
|
||||
if(completionBlock) completionBlock(NO, error);
|
||||
}
|
||||
}];
|
||||
[errorAlert addAction:closeAction];
|
||||
|
||||
if(ret == 171)
|
||||
{
|
||||
UIAlertAction* forceInstallAction = [UIAlertAction actionWithTitle:@"Force Installation" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
|
||||
{
|
||||
[self handleAppInstallFromFile:pathToIPA forceInstall:YES completion:completionBlock];
|
||||
}];
|
||||
[errorAlert addAction:forceInstallAction];
|
||||
}
|
||||
else
|
||||
{
|
||||
/*UIAlertAction* copyLogAction = [UIAlertAction actionWithTitle:@"Copy Log" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
|
||||
{
|
||||
UIPasteboard* pasteboard = [UIPasteboard generalPasteboard];
|
||||
pasteboard.string = log;
|
||||
}];
|
||||
[errorAlert addAction:copyLogAction];*/
|
||||
}
|
||||
|
||||
[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
if(ret != 171)
|
||||
{
|
||||
if(completionBlock) completionBlock((BOOL)error, error);
|
||||
}
|
||||
}];
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)presentInstallationAlertForFile:(NSString*)pathToIPA completion:(void (^)(BOOL, NSError*))completionBlock
|
||||
{
|
||||
TSAppInfo* appInfo = [[TSAppInfo alloc] initWithIPAPath:pathToIPA];
|
||||
[appInfo loadInfoWithCompletion:^(NSError* error)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
if(!error)
|
||||
{
|
||||
UIAlertController* installAlert = [UIAlertController alertControllerWithTitle:@"" message:@"" preferredStyle:UIAlertControllerStyleAlert];
|
||||
installAlert.attributedTitle = [appInfo detailedInfoTitle];
|
||||
installAlert.attributedMessage = [appInfo detailedInfoDescription];
|
||||
UIAlertAction* installAction = [UIAlertAction actionWithTitle:@"Install" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action)
|
||||
{
|
||||
[self handleAppInstallFromFile:pathToIPA completion:completionBlock];
|
||||
}];
|
||||
[installAlert addAction:installAction];
|
||||
|
||||
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action)
|
||||
{
|
||||
completionBlock(NO, nil);
|
||||
}];
|
||||
[installAlert addAction:cancelAction];
|
||||
|
||||
[TSPresentationDelegate presentViewController:installAlert animated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Parse Error %ld", error.code] message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil];
|
||||
[errorAlert addAction:closeAction];
|
||||
|
||||
[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)handleAppInstallFromFile:(NSString*)pathToIPA completion:(void (^)(BOOL, NSError*))completionBlock
|
||||
{
|
||||
[self handleAppInstallFromFile:pathToIPA forceInstall:NO completion:completionBlock];
|
||||
}
|
||||
|
||||
+ (void)handleAppInstallFromRemoteURL:(NSURL*)remoteURL completion:(void (^)(BOOL, NSError*))completionBlock
|
||||
{
|
||||
NSURLRequest* downloadRequest = [NSURLRequest requestWithURL:remoteURL];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
NSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:downloadRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[TSPresentationDelegate stopActivityWithCompletion:^
|
||||
{
|
||||
if(error)
|
||||
{
|
||||
UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"Error downloading app: %@", error] preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil];
|
||||
[errorAlert addAction:closeAction];
|
||||
|
||||
[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:^
|
||||
{
|
||||
if(completionBlock) completionBlock(NO, error);
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString* tmpIpaPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.ipa"];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:tmpIpaPath error:nil];
|
||||
[[NSFileManager defaultManager] moveItemAtPath:location.path toPath:tmpIpaPath error:nil];
|
||||
[self presentInstallationAlertForFile:tmpIpaPath completion:^(BOOL success, NSError* error)
|
||||
{
|
||||
[[NSFileManager defaultManager] removeItemAtPath:tmpIpaPath error:nil];
|
||||
if(completionBlock) completionBlock(success, error);
|
||||
}];
|
||||
}
|
||||
}];
|
||||
});
|
||||
}];
|
||||
|
||||
[TSPresentationDelegate startActivity:@"Downloading" withCancelHandler:^
|
||||
{
|
||||
[downloadTask cancel];
|
||||
}];
|
||||
|
||||
[downloadTask resume];
|
||||
});
|
||||
}
|
||||
|
||||
//+ (void)showInstallAppAlertForFile:(NSString*)pathToIPA
|
||||
|
||||
@end
|
|
@ -1,6 +1,7 @@
|
|||
#import "TSRootViewController.h"
|
||||
#import "TSAppTableViewController.h"
|
||||
#import "TSSettingsListController.h"
|
||||
#import <TSPresentationDelegate.h>
|
||||
|
||||
@implementation TSRootViewController
|
||||
|
||||
|
@ -23,4 +24,11 @@
|
|||
self.viewControllers = @[appNavigationController, settingsNavigationController];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
TSPresentationDelegate.presentationViewController = self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,98 +1,24 @@
|
|||
#import "TSSceneDelegate.h"
|
||||
#import "TSRootViewController.h"
|
||||
#import "TSUtil.h"
|
||||
#import "TSApplicationsManager.h"
|
||||
#import "TSInstallationController.h"
|
||||
#import <TSPresentationDelegate.h>
|
||||
|
||||
@implementation TSSceneDelegate
|
||||
|
||||
- (void)doIPAInstall:(NSString*)ipaPath scene:(UIWindowScene*)scene force:(BOOL)force completion:(void (^)(void))completion
|
||||
{
|
||||
UIWindow* keyWindow = nil;
|
||||
for(UIWindow* window in scene.windows)
|
||||
{
|
||||
if(window.isKeyWindow)
|
||||
{
|
||||
keyWindow = window;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UIAlertController* infoAlert = [UIAlertController alertControllerWithTitle:@"Installing" message:@"" preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(5,5,50,50)];
|
||||
activityIndicator.hidesWhenStopped = YES;
|
||||
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium;
|
||||
[activityIndicator startAnimating];
|
||||
[infoAlert.view addSubview:activityIndicator];
|
||||
|
||||
[keyWindow.rootViewController presentViewController:infoAlert animated:YES completion:nil];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
|
||||
{
|
||||
// Install IPA
|
||||
//NSString* log;
|
||||
int ret = [[TSApplicationsManager sharedInstance] installIpa:ipaPath force:force log:nil];
|
||||
|
||||
NSError* error;
|
||||
if(ret != 0)
|
||||
{
|
||||
error = [[TSApplicationsManager sharedInstance] errorForCode:ret];
|
||||
}
|
||||
|
||||
NSLog(@"installed app! ret:%d, error: %@", ret, error);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[infoAlert dismissViewControllerAnimated:YES completion:^
|
||||
{
|
||||
if(ret != 0)
|
||||
{
|
||||
UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Install Error %d", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
|
||||
{
|
||||
if(ret == 171)
|
||||
{
|
||||
completion();
|
||||
}
|
||||
}];
|
||||
[errorAlert addAction:closeAction];
|
||||
|
||||
if(ret == 171)
|
||||
{
|
||||
UIAlertAction* forceInstallAction = [UIAlertAction actionWithTitle:@"Force Installation" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
|
||||
{
|
||||
[self doIPAInstall:ipaPath scene:scene force:YES completion:completion];
|
||||
}];
|
||||
[errorAlert addAction:forceInstallAction];
|
||||
}
|
||||
else
|
||||
{
|
||||
/*UIAlertAction* copyLogAction = [UIAlertAction actionWithTitle:@"Copy Log" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
|
||||
{
|
||||
UIPasteboard* pasteboard = [UIPasteboard generalPasteboard];
|
||||
pasteboard.string = log;
|
||||
}];
|
||||
[errorAlert addAction:copyLogAction];*/
|
||||
}
|
||||
|
||||
[keyWindow.rootViewController presentViewController:errorAlert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
if(ret != 171)
|
||||
{
|
||||
completion();
|
||||
}
|
||||
}];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (void)handleURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts scene:(UIWindowScene*)scene
|
||||
- (void)handleURLContexts:(NSSet<UIOpenURLContext*>*)URLContexts scene:(UIWindowScene*)scene
|
||||
{
|
||||
for(UIOpenURLContext* context in URLContexts)
|
||||
{
|
||||
NSLog(@"openURLContexts %@", context.URL);
|
||||
NSURL* url = context.URL;
|
||||
if (url != nil && [url isFileURL]) {
|
||||
|
||||
if(url)
|
||||
{
|
||||
NSLog(@"ts_test url: %@", url);
|
||||
NSLog(@"ts_test url.scheme: %@", url.scheme);
|
||||
if([url isFileURL])
|
||||
{
|
||||
[url startAccessingSecurityScopedResource];
|
||||
void (^doneBlock)(BOOL) = ^(BOOL shouldExit)
|
||||
{
|
||||
|
@ -109,7 +35,7 @@
|
|||
|
||||
if ([url.pathExtension.lowercaseString isEqualToString:@"ipa"] || [url.pathExtension.lowercaseString isEqualToString:@"tipa"])
|
||||
{
|
||||
[self doIPAInstall:url.path scene:(UIWindowScene*)scene force:NO completion:^{
|
||||
[TSInstallationController presentInstallationAlertForFile:url.path completion:^(BOOL success, NSError* error){
|
||||
doneBlock(NO);
|
||||
}];
|
||||
}
|
||||
|
@ -122,6 +48,31 @@
|
|||
NSLog(@"Updated TrollStore!");
|
||||
}
|
||||
}
|
||||
else if([url.scheme isEqualToString:@"apple-magnifier"])
|
||||
{
|
||||
NSURLComponents* components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
||||
if([components.host isEqualToString:@"install"])
|
||||
{
|
||||
NSString* URLStringToInstall;
|
||||
|
||||
for(NSURLQueryItem* queryItem in components.queryItems)
|
||||
{
|
||||
NSLog(@"ts_test queryItem %@ = %@", queryItem.name, queryItem.value);
|
||||
if([queryItem.name isEqualToString:@"url"])
|
||||
{
|
||||
URLStringToInstall = queryItem.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(URLStringToInstall && [URLStringToInstall isKindOfClass:NSString.class])
|
||||
{
|
||||
NSURL* URLToInstall = [NSURL URLWithString:URLStringToInstall];
|
||||
[TSInstallationController handleAppInstallFromRemoteURL:URLToInstall completion:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#import "TSSettingsListController.h"
|
||||
#import <TSUtil.h>
|
||||
#import <Preferences/PSSpecifier.h>
|
||||
#import <TSPresentationDelegate.h>
|
||||
|
||||
@implementation TSSettingsListController
|
||||
|
||||
|
@ -239,7 +240,7 @@
|
|||
NSURL* ldidURL = [NSURL URLWithString:@"https://github.com/opa334/ldid/releases/download/v2.1.5-procursus5/ldid"];
|
||||
NSURLRequest* ldidRequest = [NSURLRequest requestWithURL:ldidURL];
|
||||
|
||||
[self startActivity:@"Installing ldid"];
|
||||
[TSPresentationDelegate startActivity:@"Installing ldid"];
|
||||
|
||||
NSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:ldidRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error)
|
||||
{
|
||||
|
@ -251,9 +252,9 @@
|
|||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self stopActivityWithCompletion:^
|
||||
[TSPresentationDelegate stopActivityWithCompletion:^
|
||||
{
|
||||
[self presentViewController:errorAlert animated:YES completion:nil];
|
||||
[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
@ -262,7 +263,7 @@
|
|||
spawnRoot(rootHelperPath(), @[@"install-ldid", location.path], nil, nil);
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self stopActivityWithCompletion:nil];
|
||||
[TSPresentationDelegate stopActivityWithCompletion:nil];
|
||||
[self reloadSpecifiers];
|
||||
});
|
||||
}
|
||||
|
@ -309,7 +310,7 @@
|
|||
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
|
||||
[selectAppAlert addAction:cancelAction];
|
||||
|
||||
[self presentViewController:selectAppAlert animated:YES completion:nil];
|
||||
[TSPresentationDelegate presentViewController:selectAppAlert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)doTheDashPressed
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Package: com.opa334.trollstore
|
||||
Name: TrollStore
|
||||
Version: 1.2.2
|
||||
Version: 1.3
|
||||
Architecture: iphoneos-arm
|
||||
Description: An awesome application!
|
||||
Maintainer: opa334
|
||||
|
|
|
@ -37,5 +37,7 @@
|
|||
<string>data-allowed-write</string>
|
||||
<string>preferences-write</string>
|
||||
</array>
|
||||
<key>com.apple.springboard.opensensitiveurl</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
Loading…
Reference in New Issue