mirror of
https://github.com/zydeco/minivmac4ios.git
synced 2025-01-10 08:29:36 +00:00
load disk icons asynchronously
This commit is contained in:
parent
be06c815f4
commit
a811c30549
@ -531,6 +531,9 @@
|
||||
@end
|
||||
|
||||
@implementation FileTableViewCell
|
||||
{
|
||||
UIImage *defaultIcon;
|
||||
}
|
||||
|
||||
- (void)prepareForReuse {
|
||||
[super prepareForReuse];
|
||||
@ -540,25 +543,34 @@
|
||||
self.imageView.image = nil;
|
||||
self.imageView.alpha = 1.0;
|
||||
self.detailTextLabel.text = nil;
|
||||
if (_filePath) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:DidUpdateIconForDiskImageNotification object:_filePath];
|
||||
_filePath = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFilePath:(NSString *)filePath {
|
||||
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
||||
if (_filePath) {
|
||||
[notificationCenter removeObserver:self name:DidUpdateIconForDiskImageNotification object:_filePath];
|
||||
}
|
||||
_filePath = filePath;
|
||||
if (_filePath) {
|
||||
[notificationCenter addObserver:self selector:@selector(didUpdateDiskIcon:) name:DidUpdateIconForDiskImageNotification object:_filePath];
|
||||
}
|
||||
NSString *fileName = filePath.lastPathComponent;
|
||||
self.textLabel.text = self.showExtension ? fileName : fileName.stringByDeletingPathExtension;
|
||||
NSDictionary *attributes = [[NSURL fileURLWithPath:filePath] resourceValuesForKeys:@[NSURLTotalFileSizeKey, NSURLFileSizeKey] error:NULL];
|
||||
if (attributes && attributes[NSURLTotalFileSizeKey]) {
|
||||
BOOL isDiskImage = [[AppDelegate sharedInstance].diskImageExtensions containsObject:fileName.pathExtension.lowercaseString];
|
||||
if (isDiskImage) {
|
||||
UIImage *icon = [UIImage imageWithIconForDiskImage:filePath];
|
||||
if (icon == nil) {
|
||||
NSInteger fileSize = [attributes[NSURLTotalFileSizeKey] integerValue];
|
||||
NSInteger numBlocks = fileSize / 512;
|
||||
icon = [UIImage imageNamed:numBlocks == 800 || numBlocks == 1600 ? @"floppy" : @"floppyV"];
|
||||
}
|
||||
self.imageView.image = icon;
|
||||
NSInteger fileSize = [attributes[NSURLTotalFileSizeKey] integerValue];
|
||||
NSInteger numBlocks = fileSize / 512;
|
||||
defaultIcon = [UIImage imageNamed:numBlocks == 800 || numBlocks == 1600 ? @"floppy" : @"floppyV"];
|
||||
self.imageView.image = [UIImage imageWithIconForDiskImage:filePath] ?: defaultIcon;
|
||||
} else {
|
||||
self.imageView.image = [UIImage imageNamed:@"document"];
|
||||
defaultIcon = [UIImage imageNamed:@"document"];
|
||||
self.imageView.image = defaultIcon;
|
||||
}
|
||||
NSString *sizeString = [NSByteCountFormatter stringFromByteCount:[attributes[NSURLTotalFileSizeKey] longLongValue] countStyle:NSByteCountFormatterCountStyleBinary];
|
||||
self.detailTextLabel.text = sizeString;
|
||||
@ -568,6 +580,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didUpdateDiskIcon:(NSNotification*)notification {
|
||||
if ([_filePath isEqual:notification.object]) {
|
||||
UIImage *icon = notification.userInfo[@"icon"];
|
||||
self.imageView.image = icon ?: defaultIcon;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)share:(id)sender {
|
||||
UIActivityViewController *avc = [[UIActivityViewController alloc] initWithActivityItems:@[[NSURL fileURLWithPath:self.filePath]] applicationActivities:nil];
|
||||
if ([avc respondsToSelector:@selector(popoverPresentationController)]) {
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
extern NSString *DidUpdateIconForDiskImageNotification;
|
||||
|
||||
@interface UIImage (DiskImageIcon)
|
||||
|
||||
+ (UIImage *)imageWithIconForDiskImage:(NSString *)path;
|
||||
|
@ -11,6 +11,10 @@
|
||||
#import "res.h"
|
||||
#import "mfs.h"
|
||||
#import "AppDelegate.h"
|
||||
#import <sys/xattr.h>
|
||||
|
||||
NSString *DidUpdateIconForDiskImageNotification = @"didUpdateIconForDiskImage";
|
||||
static const char kDiskImageIconAttributeName[] = "net.namedfork.DiskImageIcon";
|
||||
|
||||
#define kDiskImageHasDC42Header 1 << 0
|
||||
#define RSHORT(base, offset) ntohs(*((short *)((base) + (offset))))
|
||||
@ -23,37 +27,58 @@
|
||||
|
||||
@end
|
||||
|
||||
static NSCache<NSString*,UIImage*> *diskImageIconCache = nil;
|
||||
|
||||
@implementation UIImage (DiskImageIcon)
|
||||
|
||||
+ (NSCache<NSString*,UIImage*> *)diskImageIconCache {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
diskImageIconCache = [NSCache new];
|
||||
diskImageIconCache.name = @"net.namedfork.minivmac.icon-cache";
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didEjectDisk:) name:[AppDelegate sharedEmulator].ejectDiskNotification object:nil];
|
||||
});
|
||||
return diskImageIconCache;
|
||||
}
|
||||
|
||||
+ (void)_didEjectDisk:(NSNotification*)notification {
|
||||
NSString *path = [notification.userInfo[@"path"] stringByStandardizingPath];
|
||||
// reload icon
|
||||
[diskImageIconCache removeObjectForKey:path];
|
||||
[self imageWithIconForDiskImage:path];
|
||||
[self loadIconForDiskImageAndNotify:path];
|
||||
}
|
||||
|
||||
+ (UIImage *)imageWithIconForDiskImage:(NSString *)path {
|
||||
path = path.stringByStandardizingPath;
|
||||
UIImage *icon = [[self diskImageIconCache] objectForKey:path];
|
||||
if (icon == nil) {
|
||||
icon = [[DiskImageIconReader new] iconForDiskImage:path];
|
||||
if (icon != nil) {
|
||||
[diskImageIconCache setObject:icon forKey:path];
|
||||
}
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didEjectDisk:) name:[AppDelegate sharedEmulator].ejectDiskNotification object:nil];
|
||||
});
|
||||
|
||||
// check attribute
|
||||
ssize_t attrLen = getxattr(path.fileSystemRepresentation, kDiskImageIconAttributeName, NULL, 0, 0, 0);
|
||||
if (attrLen == -1) {
|
||||
[self loadIconForDiskImageAndNotify:path];
|
||||
return nil;
|
||||
}
|
||||
|
||||
// read data
|
||||
void *attrData = malloc(attrLen);
|
||||
getxattr(path.fileSystemRepresentation, kDiskImageIconAttributeName, attrData, attrLen, 0, 0);
|
||||
return [UIImage imageWithData:[NSData dataWithBytesNoCopy:attrData length:attrLen freeWhenDone:YES]];
|
||||
}
|
||||
|
||||
+ (void)loadIconForDiskImageAndNotify:(NSString *)path {
|
||||
if ([NSThread isMainThread]) {
|
||||
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
||||
[self loadIconForDiskImageAndNotify:path];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// get current value
|
||||
ssize_t attrLen = getxattr(path.fileSystemRepresentation, kDiskImageIconAttributeName, NULL, 0, 0, 0);
|
||||
NSData *previousData = nil;
|
||||
if (attrLen != -1) {
|
||||
void *attrData = malloc(attrLen);
|
||||
getxattr(path.fileSystemRepresentation, kDiskImageIconAttributeName, attrData, attrLen, 0, 0);
|
||||
previousData = [NSData dataWithBytesNoCopy:attrData length:attrLen freeWhenDone:YES];
|
||||
}
|
||||
|
||||
// load new icon
|
||||
UIImage *icon = [[DiskImageIconReader new] iconForDiskImage:path];
|
||||
NSData *newData = UIImagePNGRepresentation(icon);
|
||||
if (![newData isEqualToData:previousData]) {
|
||||
// save new icon and notify
|
||||
setxattr(path.fileSystemRepresentation, kDiskImageIconAttributeName, newData.bytes, newData.length, 0, 0);
|
||||
NSNotification *newIconNotification = [[NSNotification alloc] initWithName:DidUpdateIconForDiskImageNotification object:path userInfo:icon ? @{@"icon": icon} : nil];
|
||||
[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:newIconNotification waitUntilDone:NO];
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
@end
|
||||
|
Loading…
x
Reference in New Issue
Block a user