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
|
@end
|
||||||
|
|
||||||
@implementation FileTableViewCell
|
@implementation FileTableViewCell
|
||||||
|
{
|
||||||
|
UIImage *defaultIcon;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)prepareForReuse {
|
- (void)prepareForReuse {
|
||||||
[super prepareForReuse];
|
[super prepareForReuse];
|
||||||
@ -540,25 +543,34 @@
|
|||||||
self.imageView.image = nil;
|
self.imageView.image = nil;
|
||||||
self.imageView.alpha = 1.0;
|
self.imageView.alpha = 1.0;
|
||||||
self.detailTextLabel.text = nil;
|
self.detailTextLabel.text = nil;
|
||||||
|
if (_filePath) {
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:DidUpdateIconForDiskImageNotification object:_filePath];
|
||||||
|
_filePath = nil;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFilePath:(NSString *)filePath {
|
- (void)setFilePath:(NSString *)filePath {
|
||||||
|
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
||||||
|
if (_filePath) {
|
||||||
|
[notificationCenter removeObserver:self name:DidUpdateIconForDiskImageNotification object:_filePath];
|
||||||
|
}
|
||||||
_filePath = filePath;
|
_filePath = filePath;
|
||||||
|
if (_filePath) {
|
||||||
|
[notificationCenter addObserver:self selector:@selector(didUpdateDiskIcon:) name:DidUpdateIconForDiskImageNotification object:_filePath];
|
||||||
|
}
|
||||||
NSString *fileName = filePath.lastPathComponent;
|
NSString *fileName = filePath.lastPathComponent;
|
||||||
self.textLabel.text = self.showExtension ? fileName : fileName.stringByDeletingPathExtension;
|
self.textLabel.text = self.showExtension ? fileName : fileName.stringByDeletingPathExtension;
|
||||||
NSDictionary *attributes = [[NSURL fileURLWithPath:filePath] resourceValuesForKeys:@[NSURLTotalFileSizeKey, NSURLFileSizeKey] error:NULL];
|
NSDictionary *attributes = [[NSURL fileURLWithPath:filePath] resourceValuesForKeys:@[NSURLTotalFileSizeKey, NSURLFileSizeKey] error:NULL];
|
||||||
if (attributes && attributes[NSURLTotalFileSizeKey]) {
|
if (attributes && attributes[NSURLTotalFileSizeKey]) {
|
||||||
BOOL isDiskImage = [[AppDelegate sharedInstance].diskImageExtensions containsObject:fileName.pathExtension.lowercaseString];
|
BOOL isDiskImage = [[AppDelegate sharedInstance].diskImageExtensions containsObject:fileName.pathExtension.lowercaseString];
|
||||||
if (isDiskImage) {
|
if (isDiskImage) {
|
||||||
UIImage *icon = [UIImage imageWithIconForDiskImage:filePath];
|
NSInteger fileSize = [attributes[NSURLTotalFileSizeKey] integerValue];
|
||||||
if (icon == nil) {
|
NSInteger numBlocks = fileSize / 512;
|
||||||
NSInteger fileSize = [attributes[NSURLTotalFileSizeKey] integerValue];
|
defaultIcon = [UIImage imageNamed:numBlocks == 800 || numBlocks == 1600 ? @"floppy" : @"floppyV"];
|
||||||
NSInteger numBlocks = fileSize / 512;
|
self.imageView.image = [UIImage imageWithIconForDiskImage:filePath] ?: defaultIcon;
|
||||||
icon = [UIImage imageNamed:numBlocks == 800 || numBlocks == 1600 ? @"floppy" : @"floppyV"];
|
|
||||||
}
|
|
||||||
self.imageView.image = icon;
|
|
||||||
} else {
|
} else {
|
||||||
self.imageView.image = [UIImage imageNamed:@"document"];
|
defaultIcon = [UIImage imageNamed:@"document"];
|
||||||
|
self.imageView.image = defaultIcon;
|
||||||
}
|
}
|
||||||
NSString *sizeString = [NSByteCountFormatter stringFromByteCount:[attributes[NSURLTotalFileSizeKey] longLongValue] countStyle:NSByteCountFormatterCountStyleBinary];
|
NSString *sizeString = [NSByteCountFormatter stringFromByteCount:[attributes[NSURLTotalFileSizeKey] longLongValue] countStyle:NSByteCountFormatterCountStyleBinary];
|
||||||
self.detailTextLabel.text = sizeString;
|
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 {
|
- (void)share:(id)sender {
|
||||||
UIActivityViewController *avc = [[UIActivityViewController alloc] initWithActivityItems:@[[NSURL fileURLWithPath:self.filePath]] applicationActivities:nil];
|
UIActivityViewController *avc = [[UIActivityViewController alloc] initWithActivityItems:@[[NSURL fileURLWithPath:self.filePath]] applicationActivities:nil];
|
||||||
if ([avc respondsToSelector:@selector(popoverPresentationController)]) {
|
if ([avc respondsToSelector:@selector(popoverPresentationController)]) {
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
extern NSString *DidUpdateIconForDiskImageNotification;
|
||||||
|
|
||||||
@interface UIImage (DiskImageIcon)
|
@interface UIImage (DiskImageIcon)
|
||||||
|
|
||||||
+ (UIImage *)imageWithIconForDiskImage:(NSString *)path;
|
+ (UIImage *)imageWithIconForDiskImage:(NSString *)path;
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
#import "res.h"
|
#import "res.h"
|
||||||
#import "mfs.h"
|
#import "mfs.h"
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
#import <sys/xattr.h>
|
||||||
|
|
||||||
|
NSString *DidUpdateIconForDiskImageNotification = @"didUpdateIconForDiskImage";
|
||||||
|
static const char kDiskImageIconAttributeName[] = "net.namedfork.DiskImageIcon";
|
||||||
|
|
||||||
#define kDiskImageHasDC42Header 1 << 0
|
#define kDiskImageHasDC42Header 1 << 0
|
||||||
#define RSHORT(base, offset) ntohs(*((short *)((base) + (offset))))
|
#define RSHORT(base, offset) ntohs(*((short *)((base) + (offset))))
|
||||||
@ -23,37 +27,58 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static NSCache<NSString*,UIImage*> *diskImageIconCache = nil;
|
|
||||||
|
|
||||||
@implementation UIImage (DiskImageIcon)
|
@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 {
|
+ (void)_didEjectDisk:(NSNotification*)notification {
|
||||||
NSString *path = [notification.userInfo[@"path"] stringByStandardizingPath];
|
NSString *path = [notification.userInfo[@"path"] stringByStandardizingPath];
|
||||||
// reload icon
|
[self loadIconForDiskImageAndNotify:path];
|
||||||
[diskImageIconCache removeObjectForKey:path];
|
|
||||||
[self imageWithIconForDiskImage:path];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (UIImage *)imageWithIconForDiskImage:(NSString *)path {
|
+ (UIImage *)imageWithIconForDiskImage:(NSString *)path {
|
||||||
path = path.stringByStandardizingPath;
|
static dispatch_once_t onceToken;
|
||||||
UIImage *icon = [[self diskImageIconCache] objectForKey:path];
|
dispatch_once(&onceToken, ^{
|
||||||
if (icon == nil) {
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didEjectDisk:) name:[AppDelegate sharedEmulator].ejectDiskNotification object:nil];
|
||||||
icon = [[DiskImageIconReader new] iconForDiskImage:path];
|
});
|
||||||
if (icon != nil) {
|
|
||||||
[diskImageIconCache setObject:icon forKey:path];
|
// 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
|
@end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user