Files
keyboard/Shared/KBInputProfileManager.m

187 lines
7.3 KiB
Mathematica
Raw Permalink Normal View History

2026-03-02 09:19:06 +08:00
//
// KBInputProfileManager.m
// KeyBoard
//
#import "KBInputProfileManager.h"
@implementation KBInputProfileLayout
@end
@implementation KBInputProfile
@end
@interface KBInputProfileManager ()
@property (nonatomic, strong) NSArray<KBInputProfile *> *profiles;
@end
@implementation KBInputProfileManager
+ (instancetype)sharedManager {
static KBInputProfileManager *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- (instancetype)init {
if (self = [super init]) {
[self loadProfiles];
}
return self;
}
- (void)loadProfiles {
NSString *path = [[NSBundle mainBundle] pathForResource:@"kb_input_profiles" ofType:@"json"];
if (path.length == 0) {
path = [[NSBundle mainBundle] pathForResource:@"kb_input_profiles" ofType:@"json" inDirectory:@"Resource"];
}
if (path.length == 0) {
path = [[NSBundle mainBundle] pathForResource:@"kb_input_profiles" ofType:@"json" inDirectory:@"Shared/Resource"];
}
if (!path) {
NSLog(@"[KBInputProfileManager] kb_input_profiles.json not found");
self.profiles = [self defaultProfiles];
return;
}
NSData *data = [NSData dataWithContentsOfFile:path];
if (!data) {
NSLog(@"[KBInputProfileManager] Failed to read kb_input_profiles.json");
self.profiles = [self defaultProfiles];
return;
}
NSError *error = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (error || ![json isKindOfClass:NSDictionary.class]) {
NSLog(@"[KBInputProfileManager] Failed to parse JSON: %@", error);
self.profiles = [self defaultProfiles];
return;
}
NSArray *profilesArray = json[@"profiles"];
if (![profilesArray isKindOfClass:NSArray.class]) {
NSLog(@"[KBInputProfileManager] Invalid profiles array");
self.profiles = [self defaultProfiles];
return;
}
self.profiles = [self parseProfilesFromJSONArray:profilesArray];
if (self.profiles.count == 0) {
NSLog(@"[KBInputProfileManager] Parsed profiles is empty, fallback to default");
self.profiles = [self defaultProfiles];
}
NSLog(@"[KBInputProfileManager] Loaded %lu profiles", (unsigned long)self.profiles.count);
}
- (NSArray<KBInputProfile *> *)parseProfilesFromJSONArray:(NSArray *)profilesArray {
NSMutableArray<KBInputProfile *> *result = [NSMutableArray array];
for (NSDictionary *profileDict in profilesArray) {
if (![profileDict isKindOfClass:NSDictionary.class]) { continue; }
KBInputProfile *profile = [[KBInputProfile alloc] init];
profile.code = [profileDict[@"code"] isKindOfClass:NSString.class] ? profileDict[@"code"] : @"";
profile.name = [profileDict[@"name"] isKindOfClass:NSString.class] ? profileDict[@"name"] : @"";
profile.defaultSkinZip = [profileDict[@"defaultSkinZip"] isKindOfClass:NSString.class] ? profileDict[@"defaultSkinZip"] : @"";
NSArray *layoutsArray = profileDict[@"layouts"];
NSMutableArray<KBInputProfileLayout *> *layouts = [NSMutableArray array];
if ([layoutsArray isKindOfClass:NSArray.class]) {
for (NSDictionary *layoutDict in layoutsArray) {
if (![layoutDict isKindOfClass:NSDictionary.class]) { continue; }
KBInputProfileLayout *layout = [[KBInputProfileLayout alloc] init];
layout.variant = [layoutDict[@"variant"] isKindOfClass:NSString.class] ? layoutDict[@"variant"] : @"";
layout.title = [layoutDict[@"title"] isKindOfClass:NSString.class] ? layoutDict[@"title"] : @"";
layout.profileId = [layoutDict[@"profileId"] isKindOfClass:NSString.class] ? layoutDict[@"profileId"] : @"";
layout.layoutJsonId = [layoutDict[@"layoutJsonId"] isKindOfClass:NSString.class] ? layoutDict[@"layoutJsonId"] : @"";
layout.suggestionEngine = [layoutDict[@"suggestionEngine"] isKindOfClass:NSString.class] ? layoutDict[@"suggestionEngine"] : @"";
[layouts addObject:layout];
}
}
profile.layouts = [layouts copy];
[result addObject:profile];
}
return [result copy];
}
- (NSArray<KBInputProfile *> *)defaultProfiles {
NSArray *fallback = @[
@{
@"code": @"en",
@"name": @"English",
@"defaultSkinZip": @"normal_them.zip",
2026-03-04 14:15:45 +08:00
@"layouts": @[@{@"variant": @"qwerty", @"title": @"QWERTY", @"profileId": @"en_US_qwerty", @"layoutJsonId": @"letters", @"suggestionEngine": @"english"}]
2026-03-02 09:19:06 +08:00
},
@{
@"code": @"es",
2026-03-02 21:13:54 +08:00
@"name": @"Español (Latinoamérica)",
2026-03-02 20:20:28 +08:00
@"defaultSkinZip": @"西班牙初始皮肤.zip",
2026-03-02 21:13:54 +08:00
@"layouts": @[@{@"variant": @"qwerty", @"title": @"QWERTY", @"profileId": @"es_419_qwerty", @"layoutJsonId": @"letters_es", @"suggestionEngine": @"spanish"}]
2026-03-02 09:19:06 +08:00
},
@{
@"code": @"pt",
@"name": @"Português",
2026-03-03 13:14:47 +08:00
@"defaultSkinZip": @"葡萄牙初始皮肤.zip",
2026-03-04 13:44:56 +08:00
@"layouts": @[@{@"variant": @"qwerty", @"title": @"QWERTY", @"profileId": @"pt_PT_qwerty", @"layoutJsonId": @"letters_pt", @"suggestionEngine": @"portuguese"}]
2026-03-02 09:19:06 +08:00
},
@{
2026-03-03 19:02:28 +08:00
@"code": @"zh-Hant-Pinyin",
@"name": @"繁體中文-拼音",
@"defaultSkinZip": @"繁体中文拼音皮肤.zip",
@"layouts": @[@{@"variant": @"pinyin", @"title": @"拼音(繁體)", @"profileId": @"zh_Hant_TW_pinyin", @"layoutJsonId": @"letters_zh_hant_pinyin", @"suggestionEngine": @"pinyin_traditional"}]
},
@{
@"code": @"zh-Hant-Bopomofo",
@"name": @"繁體中文-注音",
@"defaultSkinZip": @"繁体中文注音皮肤.zip",
2026-03-03 19:12:24 +08:00
@"layouts": @[@{@"variant": @"bopomofo", @"title": @"注音", @"profileId": @"zh_Hant_TW_bopomofo", @"layoutJsonId": @"letters_bopomofo_full", @"suggestionEngine": @"bopomofo"}]
2026-03-02 09:19:06 +08:00
},
@{
@"code": @"id",
@"name": @"Bahasa Indonesia",
2026-03-03 19:02:28 +08:00
@"defaultSkinZip": @"印度尼西亚初始皮肤.zip",
2026-03-04 13:44:56 +08:00
@"layouts": @[@{@"variant": @"qwerty", @"title": @"QWERTY", @"profileId": @"id_ID_qwerty", @"layoutJsonId": @"letters_id", @"suggestionEngine": @"indonesian"}]
2026-03-02 09:19:06 +08:00
}
];
return [self parseProfilesFromJSONArray:fallback];
}
- (NSArray<KBInputProfile *> *)allProfiles {
return self.profiles;
}
- (nullable KBInputProfile *)profileForLanguageCode:(NSString *)languageCode {
for (KBInputProfile *profile in self.profiles) {
if ([profile.code isEqualToString:languageCode]) {
return profile;
}
}
return nil;
}
- (nullable KBInputProfileLayout *)layoutForProfileId:(NSString *)profileId {
for (KBInputProfile *profile in self.profiles) {
for (KBInputProfileLayout *layout in profile.layouts) {
if ([layout.profileId isEqualToString:profileId]) {
return layout;
}
}
}
return nil;
}
- (nullable NSString *)layoutJsonIdForProfileId:(NSString *)profileId {
KBInputProfileLayout *layout = [self layoutForProfileId:profileId];
return layout.layoutJsonId;
}
- (nullable NSString *)suggestionEngineForProfileId:(NSString *)profileId {
KBInputProfileLayout *layout = [self layoutForProfileId:profileId];
return layout.suggestionEngine;
}
@end