2
This commit is contained in:
@@ -23,9 +23,142 @@ static NSString * const kKBSkinPendingZipKey = @"zipName";
|
||||
static NSString * const kKBSkinPendingKindKey = @"kind";
|
||||
static NSString * const kKBSkinPendingTimestampKey = @"timestamp";
|
||||
static NSString * const kKBSkinPendingIconShortKey = @"iconShortNames";
|
||||
static NSString * const kKBSkinMetadataFileName = @"metadata.plist";
|
||||
static NSString * const kKBSkinMetadataNameKey = @"name";
|
||||
static NSString * const kKBSkinMetadataPreviewKey = @"preview";
|
||||
static NSString * const kKBSkinMetadataZipKey = @"zip_url";
|
||||
static NSString * const kKBSkinMetadataInstalledKey = @"installed_at";
|
||||
|
||||
@interface KBSkinDownloadRecord ()
|
||||
- (instancetype)initWithSkinId:(NSString *)skinId metadata:(NSDictionary *)metadata;
|
||||
@end
|
||||
|
||||
@implementation KBSkinDownloadRecord
|
||||
|
||||
- (instancetype)initWithSkinId:(NSString *)skinId metadata:(NSDictionary *)metadata {
|
||||
if (self = [super init]) {
|
||||
_skinId = skinId.length ? skinId : @"";
|
||||
NSString *name = [metadata[kKBSkinMetadataNameKey] isKindOfClass:NSString.class] ? metadata[kKBSkinMetadataNameKey] : nil;
|
||||
_name = name.length > 0 ? name : (_skinId.length ? _skinId : @"");
|
||||
NSString *preview = [metadata[kKBSkinMetadataPreviewKey] isKindOfClass:NSString.class] ? metadata[kKBSkinMetadataPreviewKey] : nil;
|
||||
_previewImage = preview.length > 0 ? preview : nil;
|
||||
NSString *zip = [metadata[kKBSkinMetadataZipKey] isKindOfClass:NSString.class] ? metadata[kKBSkinMetadataZipKey] : nil;
|
||||
_zipURL = zip.length > 0 ? zip : nil;
|
||||
NSTimeInterval installed = 0;
|
||||
id installObj = metadata[kKBSkinMetadataInstalledKey];
|
||||
if ([installObj respondsToSelector:@selector(doubleValue)]) {
|
||||
installed = [installObj doubleValue];
|
||||
}
|
||||
if (installed <= 0) {
|
||||
installed = [[NSDate date] timeIntervalSince1970];
|
||||
}
|
||||
_installedAt = installed;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation KBSkinInstallBridge
|
||||
|
||||
+ (NSString *)kb_skinsRootPath {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSURL *containerURL = [fm containerURLForSecurityApplicationGroupIdentifier:AppGroup];
|
||||
NSString *root = containerURL.path;
|
||||
if (root.length == 0) {
|
||||
NSArray<NSString *> *dirs = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||
root = dirs.firstObject ?: NSTemporaryDirectory();
|
||||
}
|
||||
return [root stringByAppendingPathComponent:@"Skins"];
|
||||
}
|
||||
|
||||
+ (NSString *)kb_metadataPathForSkinId:(NSString *)skinId {
|
||||
if (skinId.length == 0) { return nil; }
|
||||
NSString *skinRoot = [[self kb_skinsRootPath] stringByAppendingPathComponent:skinId];
|
||||
return [skinRoot stringByAppendingPathComponent:kKBSkinMetadataFileName];
|
||||
}
|
||||
|
||||
+ (void)kb_storeMetadataForSkinId:(NSString *)skinId
|
||||
name:(NSString *)name
|
||||
preview:(NSString *)preview
|
||||
zipURL:(NSString *)zipURL {
|
||||
if (skinId.length == 0) { return; }
|
||||
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
|
||||
NSString *metaPath = [self kb_metadataPathForSkinId:skinId];
|
||||
if (metaPath.length == 0) { return; }
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSString *dir = [metaPath stringByDeletingLastPathComponent];
|
||||
[fm createDirectoryAtPath:dir withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
|
||||
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
|
||||
dict[@"id"] = skinId;
|
||||
if (name.length > 0) {
|
||||
dict[kKBSkinMetadataNameKey] = name;
|
||||
}
|
||||
if (preview.length > 0) {
|
||||
dict[kKBSkinMetadataPreviewKey] = preview;
|
||||
}
|
||||
if (zipURL.length > 0) {
|
||||
dict[kKBSkinMetadataZipKey] = zipURL;
|
||||
}
|
||||
dict[kKBSkinMetadataInstalledKey] = @(now);
|
||||
[dict writeToFile:metaPath atomically:YES];
|
||||
});
|
||||
}
|
||||
|
||||
+ (NSArray<KBSkinDownloadRecord *> *)installedSkinRecords {
|
||||
NSString *root = [self kb_skinsRootPath];
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
BOOL isDir = NO;
|
||||
if (![fm fileExistsAtPath:root isDirectory:&isDir] || !isDir) {
|
||||
return @[];
|
||||
}
|
||||
NSArray<NSString *> *entries = [fm contentsOfDirectoryAtPath:root error:NULL] ?: @[];
|
||||
NSMutableArray<KBSkinDownloadRecord *> *records = [NSMutableArray array];
|
||||
for (NSString *entry in entries) {
|
||||
if (entry.length == 0 || [entry hasPrefix:@"."]) { continue; }
|
||||
NSString *path = [root stringByAppendingPathComponent:entry];
|
||||
BOOL isSubDir = NO;
|
||||
if (![fm fileExistsAtPath:path isDirectory:&isSubDir] || !isSubDir) { continue; }
|
||||
NSString *metaPath = [path stringByAppendingPathComponent:kKBSkinMetadataFileName];
|
||||
NSDictionary *meta = [NSDictionary dictionaryWithContentsOfFile:metaPath] ?: @{};
|
||||
KBSkinDownloadRecord *record = [[KBSkinDownloadRecord alloc] initWithSkinId:entry metadata:meta];
|
||||
[records addObject:record];
|
||||
}
|
||||
[records sortUsingComparator:^NSComparisonResult(KBSkinDownloadRecord *obj1, KBSkinDownloadRecord *obj2) {
|
||||
if (obj1.installedAt == obj2.installedAt) { return NSOrderedSame; }
|
||||
return (obj1.installedAt > obj2.installedAt) ? NSOrderedAscending : NSOrderedDescending;
|
||||
}];
|
||||
return records.copy;
|
||||
}
|
||||
|
||||
+ (BOOL)removeInstalledSkinWithId:(NSString *)skinId
|
||||
error:(NSError * __autoreleasing *)error {
|
||||
if (skinId.length == 0) { return YES; }
|
||||
NSString *root = [self kb_skinsRootPath];
|
||||
NSString *skinPath = [root stringByAppendingPathComponent:skinId];
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
BOOL isDir = NO;
|
||||
if (![fm fileExistsAtPath:skinPath isDirectory:&isDir] || !isDir) {
|
||||
return YES;
|
||||
}
|
||||
BOOL ok = [fm removeItemAtPath:skinPath error:error];
|
||||
if (ok) {
|
||||
NSString *currentId = [KBSkinManager shared].current.skinId;
|
||||
if ([currentId isKindOfClass:NSString.class] && [currentId isEqualToString:skinId]) {
|
||||
[[KBSkinManager shared] resetToDefault];
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
+ (void)recordInstalledSkinWithId:(NSString *)skinId
|
||||
name:(NSString *)name
|
||||
preview:(NSString *)preview
|
||||
zipURL:(NSString *)zipURL {
|
||||
[self kb_storeMetadataForSkinId:skinId name:name preview:preview zipURL:zipURL];
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *,NSString *> *)defaultIconShortNames {
|
||||
static NSDictionary<NSString *, NSString *> *map;
|
||||
static dispatch_once_t onceToken;
|
||||
@@ -302,6 +435,13 @@ static NSString * const kKBSkinPendingIconShortKey = @"iconShortNames";
|
||||
userInfo:nil];
|
||||
}
|
||||
if (completion) completion(ok, finalError);
|
||||
if (ok) {
|
||||
NSString *preview = [skinJSON[@"preview"] isKindOfClass:NSString.class] ? skinJSON[@"preview"] : nil;
|
||||
[self recordInstalledSkinWithId:skinId
|
||||
name:name ?: skinId
|
||||
preview:preview
|
||||
zipURL:zipURL];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -538,6 +678,12 @@ static NSString * const kKBSkinPendingIconShortKey = @"iconShortNames";
|
||||
code:KBSkinBridgeErrorApplyFailed
|
||||
userInfo:nil];
|
||||
}
|
||||
if (ok) {
|
||||
[self recordInstalledSkinWithId:skinId
|
||||
name:name ?: skinId
|
||||
preview:nil
|
||||
zipURL:zipName];
|
||||
}
|
||||
return ok;
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user