1
This commit is contained in:
@@ -226,6 +226,12 @@ static NSString * const kKBSkinMetadataThemeKey = @"theme_json";
|
||||
if ([forceValue respondsToSelector:@selector(boolValue)]) {
|
||||
forceDownload = [forceValue boolValue];
|
||||
}
|
||||
id serverIcons = skinJSON[@"key_icons"];
|
||||
NSUInteger serverIconCount = [serverIcons isKindOfClass:NSDictionary.class] ? ((NSDictionary *)serverIcons).count : 0;
|
||||
NSLog(@"[SkinBridge] request id=%@ zip=%@ force=%d key_icons_class=%@ count=%tu",
|
||||
skinId, zipURL, forceDownload,
|
||||
serverIcons ? NSStringFromClass([serverIcons class]) : @"nil",
|
||||
serverIconCount);
|
||||
|
||||
// key_icons 可选:
|
||||
// - 若后端提供 key_icons,则优先使用服务端映射;
|
||||
@@ -236,6 +242,9 @@ static NSString * const kKBSkinMetadataThemeKey = @"theme_json";
|
||||
} else {
|
||||
iconShortNames = [self defaultIconShortNames];
|
||||
}
|
||||
NSLog(@"[SkinBridge] iconShortNames source=%@ count=%tu",
|
||||
[skinJSON[@"key_icons"] isKindOfClass:NSDictionary.class] ? @"server" : @"default",
|
||||
iconShortNames.count);
|
||||
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSURL *containerURL = [fm containerURLForSecurityApplicationGroupIdentifier:AppGroup];
|
||||
@@ -262,6 +271,7 @@ static NSString * const kKBSkinMetadataThemeKey = @"theme_json";
|
||||
NSArray *contents = hasIconsDir ? [fm contentsOfDirectoryAtPath:iconsDir error:NULL] : nil;
|
||||
// 标记在本次请求发起前是否已经有缓存资源(用于“有缓存但本次下载失败”时仍允许切换皮肤)。
|
||||
BOOL hasCachedAssets = (contents.count > 0);
|
||||
NSLog(@"[SkinBridge] assets cache id=%@ cached=%d iconsDir=%@", skinId, hasCachedAssets, iconsDir);
|
||||
|
||||
NSString *bgPath = [skinRoot stringByAppendingPathComponent:@"background.png"];
|
||||
BOOL useTempRoot = forceDownload;
|
||||
@@ -482,6 +492,8 @@ static NSString * const kKBSkinMetadataThemeKey = @"theme_json";
|
||||
// 若既没有预先存在的缓存资源,也没有在本次流程中成功解压出资源,
|
||||
// 说明当前皮肤 B 的资源完全不可用,此时不应覆盖现有皮肤主题。
|
||||
BOOL hasAssets = (didUnzip || (!forceDownload && hasCachedAssets));
|
||||
NSLog(@"[SkinBridge] apply check id=%@ hasAssets=%d didUnzip=%d cached=%d",
|
||||
skinId, hasAssets, didUnzip, hasCachedAssets);
|
||||
if (!hasAssets) {
|
||||
NSError *finalError = innerError ?: [NSError errorWithDomain:KBSkinBridgeErrorDomain
|
||||
code:KBSkinBridgeErrorZipMissing
|
||||
@@ -491,7 +503,9 @@ static NSString * const kKBSkinMetadataThemeKey = @"theme_json";
|
||||
return;
|
||||
}
|
||||
|
||||
// 构造 key_icons -> App Group 相对路径 映射
|
||||
// 构造 key_icons -> App Group 相对路径 映射(仅保留实际存在的图标)
|
||||
NSString *iconsDirFinal = [skinRoot stringByAppendingPathComponent:@"icons"];
|
||||
__block NSUInteger missingCount = 0;
|
||||
NSMutableDictionary<NSString *, NSString *> *iconPathMap = [NSMutableDictionary dictionary];
|
||||
[iconShortNames enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, NSString *shortName, BOOL *stop) {
|
||||
if (![shortName isKindOfClass:NSString.class] || shortName.length == 0) return;
|
||||
@@ -500,9 +514,27 @@ static NSString * const kKBSkinMetadataThemeKey = @"theme_json";
|
||||
if (fileName.pathExtension.length == 0) {
|
||||
fileName = [fileName stringByAppendingPathExtension:@"png"];
|
||||
}
|
||||
NSString *fullPath = [iconsDirFinal stringByAppendingPathComponent:fileName];
|
||||
if (![fm fileExistsAtPath:fullPath]) {
|
||||
missingCount += 1;
|
||||
if (missingCount <= 5) {
|
||||
NSLog(@"[SkinBridge] icon missing id=%@ short=%@", identifier, fileName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
NSString *relative = [NSString stringWithFormat:@"Skins/%@/icons/%@", skinId, fileName];
|
||||
iconPathMap[identifier] = relative;
|
||||
}];
|
||||
if (missingCount > 0) {
|
||||
NSLog(@"[SkinBridge] icon missing count=%tu total=%tu", missingCount, iconShortNames.count);
|
||||
}
|
||||
NSLog(@"[SkinBridge] iconPathMap count=%tu shift=%@ shift_upper=%@ backspace=%@ mode_123=%@ return=%@",
|
||||
iconPathMap.count,
|
||||
iconPathMap[@"shift"],
|
||||
iconPathMap[@"shift_upper"],
|
||||
iconPathMap[@"backspace"],
|
||||
iconPathMap[@"mode_123"],
|
||||
iconPathMap[@"return"]);
|
||||
|
||||
NSMutableDictionary *themeJSON = [skinJSON mutableCopy];
|
||||
themeJSON[@"id"] = skinId;
|
||||
@@ -515,6 +547,8 @@ static NSString * const kKBSkinMetadataThemeKey = @"theme_json";
|
||||
// 背景图优先从 Zip 解压出的 background.png 读取
|
||||
NSData *bgData = [NSData dataWithContentsOfFile:bgPath];
|
||||
BOOL ok = themeOK;
|
||||
NSLog(@"[SkinBridge] theme apply id=%@ themeOK=%d bg=%d",
|
||||
skinId, themeOK, (bgData.length > 0));
|
||||
if (bgData.length > 0) {
|
||||
ok = [[KBSkinManager shared] applyImageSkinWithData:bgData skinId:skinId name:name];
|
||||
}
|
||||
@@ -748,6 +782,8 @@ static NSString * const kKBSkinMetadataThemeKey = @"theme_json";
|
||||
shortNames = [self defaultIconShortNames];
|
||||
}
|
||||
|
||||
NSString *iconsDirFinal = iconsDir;
|
||||
__block NSUInteger missingCount = 0;
|
||||
NSMutableDictionary<NSString *, NSString *> *iconPathMap = [NSMutableDictionary dictionary];
|
||||
[shortNames enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, NSString *shortName, BOOL *stop) {
|
||||
if (identifier.length == 0 || ![shortName isKindOfClass:NSString.class] || shortName.length == 0) return;
|
||||
@@ -755,9 +791,20 @@ static NSString * const kKBSkinMetadataThemeKey = @"theme_json";
|
||||
if (fileName.pathExtension.length == 0) {
|
||||
fileName = [fileName stringByAppendingPathExtension:@"png"];
|
||||
}
|
||||
NSString *fullPath = [iconsDirFinal stringByAppendingPathComponent:fileName];
|
||||
if (![fm fileExistsAtPath:fullPath]) {
|
||||
missingCount += 1;
|
||||
if (missingCount <= 5) {
|
||||
NSLog(@"[SkinBridge] icon missing(bundle) id=%@ short=%@", identifier, fileName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
NSString *relative = [NSString stringWithFormat:@"Skins/%@/icons/%@", skinId, fileName];
|
||||
iconPathMap[identifier] = relative;
|
||||
}];
|
||||
if (missingCount > 0) {
|
||||
NSLog(@"[SkinBridge] icon missing(bundle) count=%tu total=%tu", missingCount, shortNames.count);
|
||||
}
|
||||
|
||||
NSMutableDictionary *themeJSON = [NSMutableDictionary dictionary];
|
||||
themeJSON[@"id"] = skinId;
|
||||
|
||||
@@ -152,6 +152,18 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
|
||||
if ([icons isKindOfClass:NSDictionary.class]) {
|
||||
t.keyIconMap = icons;
|
||||
}
|
||||
NSUInteger iconCount = [t.keyIconMap isKindOfClass:NSDictionary.class] ? t.keyIconMap.count : 0;
|
||||
NSUInteger hiddenCount = t.hiddenKeyTextIdentifiers.count;
|
||||
NSLog(@"[SkinManager] applyThemeFromJSON id=%@ name=%@ iconMap=%tu hiddenKeys=%tu",
|
||||
t.skinId, t.name, iconCount, hiddenCount);
|
||||
if (iconCount > 0) {
|
||||
NSLog(@"[SkinManager] iconMap sample shift=%@ shift_upper=%@ backspace=%@ mode_123=%@ return=%@",
|
||||
t.keyIconMap[@"shift"],
|
||||
t.keyIconMap[@"shift_upper"],
|
||||
t.keyIconMap[@"backspace"],
|
||||
t.keyIconMap[@"mode_123"],
|
||||
t.keyIconMap[@"return"]);
|
||||
}
|
||||
return [self applyTheme:t];
|
||||
}
|
||||
|
||||
@@ -249,6 +261,19 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
|
||||
}
|
||||
|
||||
- (UIImage *)iconImageForKeyIdentifier:(NSString *)identifier caseVariant:(NSInteger)caseVariant {
|
||||
#if DEBUG
|
||||
static NSSet<NSString *> *kb_debugIconIds;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
kb_debugIconIds = [NSSet setWithObjects:
|
||||
@"shift", @"backspace", @"mode_123", @"mode_abc",
|
||||
@"symbols_toggle_more", @"symbols_toggle_123",
|
||||
@"return", @"space", @"emoji_panel", @"letter_q",
|
||||
nil];
|
||||
});
|
||||
BOOL shouldLog = [kb_debugIconIds containsObject:identifier];
|
||||
#endif
|
||||
|
||||
NSDictionary<NSString *, NSString *> *map = self.current.keyIconMap;
|
||||
NSString *value = nil;
|
||||
|
||||
@@ -292,10 +317,23 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
|
||||
UIImage *img = [UIImage imageWithContentsOfFile:fullPath];
|
||||
if (img) return img;
|
||||
}
|
||||
#if DEBUG
|
||||
if (shouldLog) {
|
||||
NSLog(@"[SkinManager] icon file missing id=%@ value=%@ skin=%@",
|
||||
identifier, value, self.current.skinId ?: @"");
|
||||
}
|
||||
#endif
|
||||
return nil;
|
||||
}
|
||||
// 否则按本地 Assets 名称加载(兼容旧实现)
|
||||
return [UIImage imageNamed:value];
|
||||
UIImage *img = [UIImage imageNamed:value];
|
||||
#if DEBUG
|
||||
if (!img && shouldLog) {
|
||||
NSLog(@"[SkinManager] icon asset missing id=%@ value=%@ skin=%@",
|
||||
identifier, value, self.current.skinId ?: @"");
|
||||
}
|
||||
#endif
|
||||
return img;
|
||||
}
|
||||
|
||||
// 兜底:若 keyIconMap 中没有该键,则按照约定的命名规则直接从 App Group 读取:
|
||||
@@ -329,6 +367,12 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
|
||||
if (img) return img;
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
if (shouldLog) {
|
||||
NSLog(@"[SkinManager] icon fallback missing id=%@ variant=%ld skin=%@",
|
||||
identifier, (long)caseVariant, self.current.skinId ?: @"");
|
||||
}
|
||||
#endif
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user