2022-09-02 23:19:48 +08:00
# import "TSAppTableViewController.h"
# import "TSApplicationsManager.h"
2022-09-22 23:38:58 +08:00
# define ICON_FORMAT _IPAD 8
# define ICON_FORMAT _IPHONE 10
NSInteger iconFormatToUse ( void )
{
if ( UIDevice . currentDevice . userInterfaceIdiom = = UIUserInterfaceIdiomPad )
{
return ICON_FORMAT _IPAD ;
}
else
{
return ICON_FORMAT _IPHONE ;
}
}
UIImage * imageWithSize ( UIImage * image , CGSize size )
{
if ( CGSizeEqualToSize ( image . size , size ) ) return image ;
UIGraphicsBeginImageContextWithOptions ( size , NO , UIScreen . mainScreen . scale ) ;
CGRect imageRect = CGRectMake ( 0.0 , 0.0 , size . width , size . height ) ;
[ image drawInRect : imageRect ] ;
UIImage * outImage = UIGraphicsGetImageFromCurrentImageContext ( ) ;
UIGraphicsEndImageContext ( ) ;
return outImage ;
}
2022-09-12 15:06:07 +08:00
@ interface UIImage ( )
+ ( UIImage * ) _applicationIconImageForBundleIdentifier : ( NSString * ) id format : ( NSInteger ) format scale : ( double ) scale ;
@ end
2022-09-02 23:19:48 +08:00
@ implementation TSAppTableViewController
2022-09-13 01:23:09 +08:00
- ( void ) loadCachedAppPaths
{
2022-09-22 23:38:58 +08:00
NSArray * appPaths = [ [ TSApplicationsManager sharedInstance ] installedAppPaths ] ;
_cachedAppPaths = [ appPaths sortedArrayUsingComparator : ^ NSComparisonResult ( NSString * appPathA , NSString * appPathB ) {
NSString * displayNameA = [ [ TSApplicationsManager sharedInstance ] displayNameForAppPath : appPathA ] ;
NSString * displayNameB = [ [ TSApplicationsManager sharedInstance ] displayNameForAppPath : appPathB ] ;
return [ displayNameA localizedStandardCompare : displayNameB ] ;
} ] ;
2022-09-13 01:23:09 +08:00
}
- ( instancetype ) init
{
self = [ super init ] ;
if ( self )
{
[ self loadCachedAppPaths ] ;
2022-09-22 23:38:58 +08:00
_placeholderIcon = [ UIImage _applicationIconImageForBundleIdentifier : @ "com.apple.WebSheet" format : iconFormatToUse ( ) scale : [ UIScreen mainScreen ] . scale ] ;
2022-09-13 02:52:04 +08:00
_cachedIcons = [ NSMutableDictionary new ] ;
2022-09-13 01:23:09 +08:00
}
return self ;
}
2022-09-02 23:19:48 +08:00
- ( void ) reloadTable
{
2022-09-13 01:23:09 +08:00
[ self loadCachedAppPaths ] ;
dispatch_async ( dispatch_get _main _queue ( ) , ^
{
[ self . tableView reloadData ] ;
} ) ;
2022-09-02 23:19:48 +08:00
}
- ( void ) loadView
{
2022-09-13 01:23:09 +08:00
[ super loadView ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( reloadTable )
name : @ "ApplicationsChanged"
object : nil ] ;
2022-09-02 23:19:48 +08:00
}
- ( void ) viewDidLoad {
2022-09-13 01:23:09 +08:00
[ super viewDidLoad ] ;
self . tableView . allowsMultipleSelectionDuringEditing = NO ;
2022-09-02 23:19:48 +08:00
}
2022-09-10 02:22:34 +08:00
- ( void ) showError : ( NSError * ) error
{
2022-09-13 01:23:09 +08:00
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 ] ;
2022-09-10 02:22:34 +08:00
}
2022-09-12 22:10:50 +08:00
- ( void ) openAppPressedForRowAtIndexPath : ( NSIndexPath * ) indexPath
{
2022-09-13 01:23:09 +08:00
TSApplicationsManager * appsManager = [ TSApplicationsManager sharedInstance ] ;
2022-09-12 22:10:50 +08:00
2022-09-13 01:23:09 +08:00
NSString * appPath = _cachedAppPaths [ indexPath . row ] ;
NSString * appId = [ appsManager appIdForAppPath : appPath ] ;
BOOL didOpen = [ appsManager openApplicationWithBundleID : appId ] ;
2022-09-12 22:10:50 +08:00
2022-09-13 01:23:09 +08:00
// 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 ] ;
UIAlertAction * cancelAction = [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : nil ] ;
2022-09-12 22:10:50 +08:00
2022-09-13 01:23:09 +08:00
[ didFailController addAction : cancelAction ] ;
[ self presentViewController : didFailController animated : YES completion : nil ] ;
}
2022-09-12 22:10:50 +08:00
}
2022-09-10 02:22:34 +08:00
- ( void ) uninstallPressedForRowAtIndexPath : ( NSIndexPath * ) indexPath
{
2022-09-13 01:23:09 +08:00
TSApplicationsManager * appsManager = [ TSApplicationsManager sharedInstance ] ;
NSString * appPath = _cachedAppPaths [ indexPath . row ] ;
NSString * appId = [ appsManager appIdForAppPath : appPath ] ;
NSString * appName = [ appsManager displayNameForAppPath : appPath ] ;
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 ] ;
UIAlertAction * uninstallAction = [ UIAlertAction actionWithTitle : @ "Uninstall" style : UIAlertActionStyleDestructive handler : ^ ( UIAlertAction * action )
{
if ( appId )
{
[ appsManager uninstallApp : appId ] ;
}
else
{
[ appsManager uninstallAppByPath : appPath ] ;
}
} ] ;
[ confirmAlert addAction : uninstallAction ] ;
UIAlertAction * cancelAction = [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : nil ] ;
[ confirmAlert addAction : cancelAction ] ;
[ self presentViewController : confirmAlert animated : YES completion : nil ] ;
2022-09-10 02:22:34 +08:00
}
- ( void ) deselectRow
{
2022-09-13 01:23:09 +08:00
[ self . tableView deselectRowAtIndexPath : [ self . tableView indexPathForSelectedRow ] animated : YES ] ;
2022-09-10 02:22:34 +08:00
}
2022-09-02 23:19:48 +08:00
# pragma mark - Table view data source
- ( NSInteger ) numberOfSectionsInTableView : ( UITableView * ) tableView {
2022-09-13 01:23:09 +08:00
return 1 ;
2022-09-02 23:19:48 +08:00
}
- ( NSInteger ) tableView : ( UITableView * ) tableView numberOfRowsInSection : ( NSInteger ) section {
2022-09-13 01:23:09 +08:00
return _cachedAppPaths . count ;
2022-09-02 23:19:48 +08:00
}
2022-09-22 23:38:58 +08:00
- ( void ) traitCollectionDidChange : ( UITraitCollection * ) previousTraitCollection
{
[ self reloadTable ] ;
}
2022-09-02 23:19:48 +08:00
- ( UITableViewCell * ) tableView : ( UITableView * ) tableView cellForRowAtIndexPath : ( NSIndexPath * ) indexPath {
2022-09-13 01:23:09 +08:00
UITableViewCell * cell = [ tableView dequeueReusableCellWithIdentifier : @ "ApplicationCell" ] ;
if ( ! cell ) {
cell = [ [ UITableViewCell alloc ] initWithStyle : UITableViewCellStyleSubtitle reuseIdentifier : @ "ApplicationCell" ] ;
}
NSString * appPath = _cachedAppPaths [ indexPath . row ] ;
NSString * appId = [ [ TSApplicationsManager sharedInstance ] appIdForAppPath : appPath ] ;
NSString * appVersion = [ [ TSApplicationsManager sharedInstance ] versionStringForAppPath : appPath ] ;
// Configure the cell . . .
cell . textLabel . text = [ [ TSApplicationsManager sharedInstance ] displayNameForAppPath : appPath ] ;
cell . detailTextLabel . text = [ NSString stringWithFormat : @ "%@ • %@" , appVersion , appId ] ;
2022-09-22 23:38:58 +08:00
cell . imageView . layer . borderWidth = 1 ;
cell . imageView . layer . borderColor = [ UIColor . labelColor colorWithAlphaComponent : 0.1 ] . CGColor ;
cell . imageView . layer . cornerRadius = 13.5 ;
cell . imageView . layer . masksToBounds = YES ;
cell . imageView . layer . cornerCurve = kCACornerCurveContinuous ;
2022-09-13 01:23:09 +08:00
2022-09-13 06:07:34 +08:00
if ( appId )
2022-09-13 01:23:09 +08:00
{
2022-09-13 06:07:34 +08:00
UIImage * cachedIcon = _cachedIcons [ appId ] ;
if ( cachedIcon )
{
cell . imageView . image = cachedIcon ;
}
else
{
cell . imageView . image = _placeholderIcon ;
dispatch_async ( dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _DEFAULT , 0 ) , ^
{
// usleep ( 1000 * 5000 ) ; // ( test delay for debugging )
2022-09-22 23:38:58 +08:00
UIImage * iconImage = imageWithSize ( [ UIImage _applicationIconImageForBundleIdentifier : appId format : iconFormatToUse ( ) scale : [ UIScreen mainScreen ] . scale ] , _placeholderIcon . size ) ;
2022-09-13 06:07:34 +08:00
_cachedIcons [ appId ] = iconImage ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
if ( [ tableView . indexPathsForVisibleRows containsObject : indexPath ] )
{
cell . imageView . image = iconImage ;
2022-09-22 23:38:58 +08:00
[ cell setNeedsLayout ] ;
2022-09-13 06:07:34 +08:00
}
} ) ;
} ) ;
}
2022-09-13 02:52:04 +08:00
}
else
{
cell . imageView . image = _placeholderIcon ;
}
2022-09-13 01:23:09 +08:00
cell . preservesSuperviewLayoutMargins = NO ;
cell . separatorInset = UIEdgeInsetsZero ;
cell . layoutMargins = UIEdgeInsetsZero ;
return cell ;
2022-09-02 23:19:48 +08:00
}
2022-09-12 15:06:07 +08:00
- ( CGFloat ) tableView : ( UITableView * ) tableView heightForRowAtIndexPath : ( NSIndexPath * ) indexPath {
2022-09-13 01:23:09 +08:00
return 80.0 f ;
2022-09-12 15:06:07 +08:00
}
2022-09-02 23:19:48 +08:00
- ( void ) tableView : ( UITableView * ) tableView commitEditingStyle : ( UITableViewCellEditingStyle ) editingStyle forRowAtIndexPath : ( NSIndexPath * ) indexPath
{
2022-09-13 01:23:09 +08:00
if ( editingStyle = = UITableViewCellEditingStyleDelete )
{
[ self uninstallPressedForRowAtIndexPath : indexPath ] ;
}
2022-09-10 02:22:34 +08:00
}
- ( void ) tableView : ( UITableView * ) tableView didSelectRowAtIndexPath : ( NSIndexPath * ) indexPath
{
2022-09-13 01:23:09 +08:00
TSApplicationsManager * appsManager = [ TSApplicationsManager sharedInstance ] ;
NSString * appPath = _cachedAppPaths [ indexPath . row ] ;
NSString * appId = [ appsManager appIdForAppPath : appPath ] ;
NSString * appName = [ appsManager displayNameForAppPath : appPath ] ;
2022-09-13 06:07:34 +08:00
UIAlertController * appSelectAlert = [ UIAlertController alertControllerWithTitle : appName message : appId ? : @ "" preferredStyle : UIAlertControllerStyleActionSheet ] ;
2022-09-13 01:23:09 +08:00
/ * 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 ] ; * /
2022-09-22 23:38:58 +08:00
UIAlertAction * openAction = [ UIAlertAction actionWithTitle : @ "Open" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action )
2022-09-13 01:23:09 +08:00
{
[ self openAppPressedForRowAtIndexPath : indexPath ] ;
[ self deselectRow ] ;
} ] ;
[ appSelectAlert addAction : openAction ] ;
UIAlertAction * uninstallAction = [ UIAlertAction actionWithTitle : @ "Uninstall App" style : UIAlertActionStyleDestructive handler : ^ ( UIAlertAction * action )
{
[ self uninstallPressedForRowAtIndexPath : indexPath ] ;
[ self deselectRow ] ;
} ] ;
[ appSelectAlert addAction : uninstallAction ] ;
UIAlertAction * cancelAction = [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : ^ ( UIAlertAction * action )
{
[ self deselectRow ] ;
} ] ;
[ appSelectAlert addAction : cancelAction ] ;
2022-09-10 02:22:34 +08:00
appSelectAlert . popoverPresentationController . sourceView = tableView ;
appSelectAlert . popoverPresentationController . sourceRect = [ tableView rectForRowAtIndexPath : indexPath ] ;
2022-09-13 01:23:09 +08:00
[ self presentViewController : appSelectAlert animated : YES completion : nil ] ;
2022-09-02 23:19:48 +08:00
}
@ end