mirror of
https://github.com/opa334/TrollStore.git
synced 2026-08-17 23:36:56 +08:00
1.3
This commit is contained in:
+2
-1
@@ -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
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
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
|
||||
|
||||
+57
-106
@@ -1,125 +1,76 @@
|
||||
#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]) {
|
||||
[url startAccessingSecurityScopedResource];
|
||||
void (^doneBlock)(BOOL) = ^(BOOL shouldExit)
|
||||
{
|
||||
[url stopAccessingSecurityScopedResource];
|
||||
[[NSFileManager defaultManager] removeItemAtURL:url error:nil];
|
||||
|
||||
if(shouldExit)
|
||||
if(url)
|
||||
{
|
||||
NSLog(@"ts_test url: %@", url);
|
||||
NSLog(@"ts_test url.scheme: %@", url.scheme);
|
||||
if([url isFileURL])
|
||||
{
|
||||
[url startAccessingSecurityScopedResource];
|
||||
void (^doneBlock)(BOOL) = ^(BOOL shouldExit)
|
||||
{
|
||||
NSLog(@"Respring + Exit");
|
||||
respring();
|
||||
exit(0);
|
||||
[url stopAccessingSecurityScopedResource];
|
||||
[[NSFileManager defaultManager] removeItemAtURL:url error:nil];
|
||||
|
||||
if(shouldExit)
|
||||
{
|
||||
NSLog(@"Respring + Exit");
|
||||
respring();
|
||||
exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
if ([url.pathExtension.lowercaseString isEqualToString:@"ipa"] || [url.pathExtension.lowercaseString isEqualToString:@"tipa"])
|
||||
{
|
||||
[TSInstallationController presentInstallationAlertForFile:url.path completion:^(BOOL success, NSError* error){
|
||||
doneBlock(NO);
|
||||
}];
|
||||
}
|
||||
else if([url.pathExtension.lowercaseString isEqualToString:@"tar"])
|
||||
{
|
||||
// Update TrollStore itself
|
||||
NSLog(@"Updating TrollStore...");
|
||||
int ret = spawnRoot(rootHelperPath(), @[@"install-trollstore", url.path], nil, nil);
|
||||
doneBlock(ret == 0);
|
||||
NSLog(@"Updated TrollStore!");
|
||||
}
|
||||
};
|
||||
|
||||
if ([url.pathExtension.lowercaseString isEqualToString:@"ipa"] || [url.pathExtension.lowercaseString isEqualToString:@"tipa"])
|
||||
{
|
||||
[self doIPAInstall:url.path scene:(UIWindowScene*)scene force:NO completion:^{
|
||||
doneBlock(NO);
|
||||
}];
|
||||
}
|
||||
else if([url.pathExtension.lowercaseString isEqualToString:@"tar"])
|
||||
else if([url.scheme isEqualToString:@"apple-magnifier"])
|
||||
{
|
||||
// Update TrollStore itself
|
||||
NSLog(@"Updating TrollStore...");
|
||||
int ret = spawnRoot(rootHelperPath(), @[@"install-trollstore", url.path], nil, nil);
|
||||
doneBlock(ret == 0);
|
||||
NSLog(@"Updated TrollStore!");
|
||||
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
-1
@@ -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>
|
||||
Reference in New Issue
Block a user