配置化json strings
This commit is contained in:
@@ -29,6 +29,7 @@
|
|||||||
04286A062ECC81B200CE730C /* KBSkinService.m in Sources */ = {isa = PBXBuildFile; fileRef = 04286A052ECC81B200CE730C /* KBSkinService.m */; };
|
04286A062ECC81B200CE730C /* KBSkinService.m in Sources */ = {isa = PBXBuildFile; fileRef = 04286A052ECC81B200CE730C /* KBSkinService.m */; };
|
||||||
04286A0B2ECD88B400CE730C /* KeyboardAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 04286A0A2ECD88B400CE730C /* KeyboardAssets.xcassets */; };
|
04286A0B2ECD88B400CE730C /* KeyboardAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 04286A0A2ECD88B400CE730C /* KeyboardAssets.xcassets */; };
|
||||||
04286A0F2ECDA71B00CE730C /* 001.zip in Resources */ = {isa = PBXBuildFile; fileRef = 04286A0E2ECDA71B00CE730C /* 001.zip */; };
|
04286A0F2ECDA71B00CE730C /* 001.zip in Resources */ = {isa = PBXBuildFile; fileRef = 04286A0E2ECDA71B00CE730C /* 001.zip */; };
|
||||||
|
04286A132ECDEBF900CE730C /* KBSkinIconMap.strings in Resources */ = {isa = PBXBuildFile; fileRef = 04286A122ECDEBF900CE730C /* KBSkinIconMap.strings */; };
|
||||||
043FBCD22EAF97630036AFE1 /* KBPermissionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04C6EAE12EAF940F0089C901 /* KBPermissionViewController.m */; };
|
043FBCD22EAF97630036AFE1 /* KBPermissionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04C6EAE12EAF940F0089C901 /* KBPermissionViewController.m */; };
|
||||||
0459D1B42EBA284C00F2D189 /* KBSkinCenterVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B32EBA284C00F2D189 /* KBSkinCenterVC.m */; };
|
0459D1B42EBA284C00F2D189 /* KBSkinCenterVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B32EBA284C00F2D189 /* KBSkinCenterVC.m */; };
|
||||||
0459D1B72EBA287900F2D189 /* KBSkinManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B62EBA287900F2D189 /* KBSkinManager.m */; };
|
0459D1B72EBA287900F2D189 /* KBSkinManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B62EBA287900F2D189 /* KBSkinManager.m */; };
|
||||||
@@ -216,6 +217,7 @@
|
|||||||
04286A052ECC81B200CE730C /* KBSkinService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinService.m; sourceTree = "<group>"; };
|
04286A052ECC81B200CE730C /* KBSkinService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinService.m; sourceTree = "<group>"; };
|
||||||
04286A0A2ECD88B400CE730C /* KeyboardAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = KeyboardAssets.xcassets; sourceTree = "<group>"; };
|
04286A0A2ECD88B400CE730C /* KeyboardAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = KeyboardAssets.xcassets; sourceTree = "<group>"; };
|
||||||
04286A0E2ECDA71B00CE730C /* 001.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = 001.zip; sourceTree = "<group>"; };
|
04286A0E2ECDA71B00CE730C /* 001.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = 001.zip; sourceTree = "<group>"; };
|
||||||
|
04286A122ECDEBF900CE730C /* KBSkinIconMap.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = KBSkinIconMap.strings; sourceTree = "<group>"; };
|
||||||
0459D1B22EBA284C00F2D189 /* KBSkinCenterVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinCenterVC.h; sourceTree = "<group>"; };
|
0459D1B22EBA284C00F2D189 /* KBSkinCenterVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinCenterVC.h; sourceTree = "<group>"; };
|
||||||
0459D1B32EBA284C00F2D189 /* KBSkinCenterVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinCenterVC.m; sourceTree = "<group>"; };
|
0459D1B32EBA284C00F2D189 /* KBSkinCenterVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinCenterVC.m; sourceTree = "<group>"; };
|
||||||
0459D1B52EBA287900F2D189 /* KBSkinManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinManager.h; sourceTree = "<group>"; };
|
0459D1B52EBA287900F2D189 /* KBSkinManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinManager.h; sourceTree = "<group>"; };
|
||||||
@@ -591,6 +593,7 @@
|
|||||||
047C652C2EBCAAAC0035E841 /* Resource */ = {
|
047C652C2EBCAAAC0035E841 /* Resource */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
04286A122ECDEBF900CE730C /* KBSkinIconMap.strings */,
|
||||||
047C652B2EBCAAAC0035E841 /* Images */,
|
047C652B2EBCAAAC0035E841 /* Images */,
|
||||||
04286A0E2ECDA71B00CE730C /* 001.zip */,
|
04286A0E2ECDA71B00CE730C /* 001.zip */,
|
||||||
);
|
);
|
||||||
@@ -1411,6 +1414,7 @@
|
|||||||
04C6EABA2EAF86530089C901 /* Assets.xcassets in Resources */,
|
04C6EABA2EAF86530089C901 /* Assets.xcassets in Resources */,
|
||||||
04A9FE212EB893F10020DB6D /* Localizable.strings in Resources */,
|
04A9FE212EB893F10020DB6D /* Localizable.strings in Resources */,
|
||||||
04C6EABC2EAF86530089C901 /* LaunchScreen.storyboard in Resources */,
|
04C6EABC2EAF86530089C901 /* LaunchScreen.storyboard in Resources */,
|
||||||
|
04286A132ECDEBF900CE730C /* KBSkinIconMap.strings in Resources */,
|
||||||
04C6EABD2EAF86530089C901 /* Main.storyboard in Resources */,
|
04C6EABD2EAF86530089C901 /* Main.storyboard in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -1448,10 +1452,14 @@
|
|||||||
inputFileListPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-keyBoard/Pods-keyBoard-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-keyBoard/Pods-keyBoard-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
);
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
name = "[CP] Embed Pods Frameworks";
|
name = "[CP] Embed Pods Frameworks";
|
||||||
outputFileListPaths = (
|
outputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-keyBoard/Pods-keyBoard-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-keyBoard/Pods-keyBoard-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
);
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-keyBoard/Pods-keyBoard-frameworks.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-keyBoard/Pods-keyBoard-frameworks.sh\"\n";
|
||||||
|
|||||||
@@ -188,7 +188,11 @@
|
|||||||
if (idx < 0 || idx >= self.skins.count) return;
|
if (idx < 0 || idx >= self.skins.count) return;
|
||||||
NSDictionary *skin = self.skins[idx];
|
NSDictionary *skin = self.skins[idx];
|
||||||
if (!skin) return;
|
if (!skin) return;
|
||||||
[[KBSkinService shared] applySkinWithJSON:skin fromViewController:self completion:nil];
|
// 默认按远程 Zip 模式测试;如需本地 bundle,请改为 KBSkinSourceModeLocalBundleZip。
|
||||||
|
[[KBSkinService shared] applySkinWithJSON:skin
|
||||||
|
fromViewController:self
|
||||||
|
mode:KBSkinSourceModeLocalBundleZip
|
||||||
|
completion:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
typedef void(^KBSkinApplyCompletion)(BOOL success);
|
typedef void(^KBSkinApplyCompletion)(BOOL success);
|
||||||
|
|
||||||
|
/// 皮肤资源来源
|
||||||
|
typedef NS_ENUM(NSUInteger, KBSkinSourceMode) {
|
||||||
|
/// 远程 Zip:通过网络下载 zip_url 并解压到 App Group
|
||||||
|
KBSkinSourceModeRemoteZip = 0,
|
||||||
|
/// 本地 bundle Zip:从主 App bundle 中读取 zip_url 指定的 zip 文件并解压到 App Group
|
||||||
|
KBSkinSourceModeLocalBundleZip = 1,
|
||||||
|
};
|
||||||
|
|
||||||
/// 皮肤下载与应用服务(仅主 App 使用)
|
/// 皮肤下载与应用服务(仅主 App 使用)
|
||||||
@interface KBSkinService : NSObject
|
@interface KBSkinService : NSObject
|
||||||
|
|
||||||
@@ -23,12 +31,13 @@ typedef void(^KBSkinApplyCompletion)(BOOL success);
|
|||||||
///
|
///
|
||||||
/// @param skinJSON 与后端约定的皮肤结构(包含 id/name/background_image/hidden_keys/key_icons 等)
|
/// @param skinJSON 与后端约定的皮肤结构(包含 id/name/background_image/hidden_keys/key_icons 等)
|
||||||
/// @param presenting 用于弹出“键盘权限引导页”的控制器,可为 nil
|
/// @param presenting 用于弹出“键盘权限引导页”的控制器,可为 nil
|
||||||
|
/// @param mode 资源来源模式:远程 / 本地 bundle
|
||||||
/// @param completion 应用完成回调(下载/写入全部结束后调用,success 表示是否成功)
|
/// @param completion 应用完成回调(下载/写入全部结束后调用,success 表示是否成功)
|
||||||
- (void)applySkinWithJSON:(NSDictionary *)skinJSON
|
- (void)applySkinWithJSON:(NSDictionary *)skinJSON
|
||||||
fromViewController:(nullable UIViewController *)presenting
|
fromViewController:(nullable UIViewController *)presenting
|
||||||
|
mode:(KBSkinSourceMode)mode
|
||||||
completion:(nullable KBSkinApplyCompletion)completion;
|
completion:(nullable KBSkinApplyCompletion)completion;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
||||||
|
|||||||
@@ -25,126 +25,18 @@
|
|||||||
static NSDictionary<NSString *, NSString *> *map;
|
static NSDictionary<NSString *, NSString *> *map;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
map = @{
|
// 从配置文件加载映射,避免在代码里维护大段字面量。
|
||||||
// 字母键(大小写共用)
|
// 使用 .strings 形式,便于为每个键添加注释。
|
||||||
@"letter_q_lower": @"key_q", // 字母 q(小写)
|
NSString *path = [[NSBundle mainBundle] pathForResource:@"KBSkinIconMap" ofType:@"strings"];
|
||||||
@"letter_q_upper": @"key_q", // 字母 Q(大写)
|
if (path.length > 0) {
|
||||||
@"letter_w_lower": @"key_w", // 字母 w(小写)
|
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
|
||||||
@"letter_w_upper": @"key_w", // 字母 W(大写)
|
if ([dict isKindOfClass:NSDictionary.class]) {
|
||||||
@"letter_e_lower": @"key_e", // 字母 e(小写)
|
map = dict;
|
||||||
@"letter_e_upper": @"key_e", // 字母 E(大写)
|
}
|
||||||
@"letter_r_lower": @"key_r", // 字母 r(小写)
|
}
|
||||||
@"letter_r_upper": @"key_r", // 字母 R(大写)
|
if (!map) {
|
||||||
@"letter_t_lower": @"key_t", // 字母 t(小写)
|
map = @{}; // 防御:配置缺失时返回空表,避免崩溃
|
||||||
@"letter_t_upper": @"key_t", // 字母 T(大写)
|
}
|
||||||
@"letter_y_lower": @"key_y", // 字母 y(小写)
|
|
||||||
@"letter_y_upper": @"key_y", // 字母 Y(大写)
|
|
||||||
@"letter_u_lower": @"key_u", // 字母 u(小写)
|
|
||||||
@"letter_u_upper": @"key_u", // 字母 U(大写)
|
|
||||||
@"letter_i_lower": @"key_i", // 字母 i(小写)
|
|
||||||
@"letter_i_upper": @"key_i", // 字母 I(大写)
|
|
||||||
@"letter_o_lower": @"key_o", // 字母 o(小写)
|
|
||||||
@"letter_o_upper": @"key_o", // 字母 O(大写)
|
|
||||||
@"letter_p_lower": @"key_p", // 字母 p(小写)
|
|
||||||
@"letter_p_upper": @"key_p", // 字母 P(大写)
|
|
||||||
|
|
||||||
@"letter_a_lower": @"key_a", // 字母 a(小写)
|
|
||||||
@"letter_a_upper": @"key_a", // 字母 A(大写)
|
|
||||||
@"letter_s_lower": @"key_s", // 字母 s(小写)
|
|
||||||
@"letter_s_upper": @"key_s", // 字母 S(大写)
|
|
||||||
@"letter_d_lower": @"key_d", // 字母 d(小写)
|
|
||||||
@"letter_d_upper": @"key_d", // 字母 D(大写)
|
|
||||||
@"letter_f_lower": @"key_f", // 字母 f(小写)
|
|
||||||
@"letter_f_upper": @"key_f", // 字母 F(大写)
|
|
||||||
@"letter_g_lower": @"key_g", // 字母 g(小写)
|
|
||||||
@"letter_g_upper": @"key_g", // 字母 G(大写)
|
|
||||||
@"letter_h_lower": @"key_h", // 字母 h(小写)
|
|
||||||
@"letter_h_upper": @"key_h", // 字母 H(大写)
|
|
||||||
@"letter_j_lower": @"key_j", // 字母 j(小写)
|
|
||||||
@"letter_j_upper": @"key_j", // 字母 J(大写)
|
|
||||||
@"letter_k_lower": @"key_k", // 字母 k(小写)
|
|
||||||
@"letter_k_upper": @"key_k", // 字母 K(大写)
|
|
||||||
@"letter_l_lower": @"key_l", // 字母 l(小写)
|
|
||||||
@"letter_l_upper": @"key_l", // 字母 L(大写)
|
|
||||||
|
|
||||||
@"letter_z_lower": @"key_z", // 字母 z(小写)
|
|
||||||
@"letter_z_upper": @"key_z", // 字母 Z(大写)
|
|
||||||
@"letter_x_lower": @"key_x", // 字母 x(小写)
|
|
||||||
@"letter_x_upper": @"key_x", // 字母 X(大写)
|
|
||||||
@"letter_c_lower": @"key_c", // 字母 c(小写)
|
|
||||||
@"letter_c_upper": @"key_c", // 字母 C(大写)
|
|
||||||
@"letter_v_lower": @"key_v", // 字母 v(小写)
|
|
||||||
@"letter_v_upper": @"key_v", // 字母 V(大写)
|
|
||||||
@"letter_b_lower": @"key_b", // 字母 b(小写)
|
|
||||||
@"letter_b_upper": @"key_b", // 字母 B(大写)
|
|
||||||
@"letter_n_lower": @"key_n", // 字母 n(小写)
|
|
||||||
@"letter_n_upper": @"key_n", // 字母 N(大写)
|
|
||||||
@"letter_m_lower": @"key_m", // 字母 m(小写)
|
|
||||||
@"letter_m_upper": @"key_m", // 字母 M(大写)
|
|
||||||
|
|
||||||
// 数字键(数字面板顶行 1~0)
|
|
||||||
@"digit_1": @"key_1", // 数字 1
|
|
||||||
@"digit_2": @"key_2", // 数字 2
|
|
||||||
@"digit_3": @"key_3", // 数字 3
|
|
||||||
@"digit_4": @"key_4", // 数字 4
|
|
||||||
@"digit_5": @"key_5", // 数字 5
|
|
||||||
@"digit_6": @"key_6", // 数字 6
|
|
||||||
@"digit_7": @"key_7", // 数字 7
|
|
||||||
@"digit_8": @"key_8", // 数字 8
|
|
||||||
@"digit_9": @"key_9", // 数字 9
|
|
||||||
@"digit_0": @"key_0", // 数字 0
|
|
||||||
|
|
||||||
// 常用符号(123 页第二行 + 第三行)
|
|
||||||
@"sym_minus": @"key_minus", // '-'
|
|
||||||
@"sym_slash": @"key_slash", // '/'
|
|
||||||
@"sym_colon": @"key_colon", // ':'
|
|
||||||
@"sym_semicolon": @"key_semicolon",// ';'
|
|
||||||
@"sym_paren_l": @"key_paren_l", // '('
|
|
||||||
@"sym_paren_r": @"key_paren_r", // ')'
|
|
||||||
@"sym_dollar": @"key_dollar", // '$'
|
|
||||||
@"sym_amp": @"key_amp", // '&'
|
|
||||||
@"sym_at": @"key_at", // '@'
|
|
||||||
@"sym_quote_double": @"key_quote_d", // 双引号 "
|
|
||||||
|
|
||||||
@"sym_comma": @"key_comma", // ','
|
|
||||||
@"sym_dot": @"key_dot", // '.'
|
|
||||||
@"sym_question": @"key_question", // '?'
|
|
||||||
@"sym_exclam": @"key_exclam", // '!'
|
|
||||||
@"sym_quote_single": @"key_quote", // 单引号 '
|
|
||||||
|
|
||||||
// #+= 页额外符号(包括中括号/大括号等)
|
|
||||||
@"sym_bracket_l": @"key_bracket_l", // '['
|
|
||||||
@"sym_bracket_r": @"key_bracket_r", // ']'
|
|
||||||
@"sym_brace_l": @"key_brace_l", // '{'
|
|
||||||
@"sym_brace_r": @"key_brace_r", // '}'
|
|
||||||
@"sym_hash": @"key_hash", // '#'
|
|
||||||
@"sym_percent": @"key_percent", // '%'
|
|
||||||
@"sym_caret": @"key_caret", // '^'
|
|
||||||
@"sym_asterisk": @"key_asterisk", // '*'
|
|
||||||
@"sym_plus": @"key_plus", // '+'
|
|
||||||
@"sym_equal": @"key_equal", // '='
|
|
||||||
|
|
||||||
@"sym_underscore": @"key_underscore", // '_'
|
|
||||||
@"sym_backslash": @"key_backslash", // '\'
|
|
||||||
@"sym_pipe": @"key_pipe", // '|'
|
|
||||||
@"sym_tilde": @"key_tilde", // '~'
|
|
||||||
@"sym_lt": @"key_lt", // '<'
|
|
||||||
@"sym_gt": @"key_gt", // '>'
|
|
||||||
@"sym_euro": @"key_euro", // '€'
|
|
||||||
@"sym_pound": @"key_pound", // '£'
|
|
||||||
@"sym_bullet": @"key_bullet", // '•'
|
|
||||||
|
|
||||||
// 功能键(非字符输出)
|
|
||||||
@"space": @"key_space", // 空格键
|
|
||||||
@"backspace": @"key_del", // 删除键(⌫)
|
|
||||||
@"shift": @"key_up", // Shift(⇧)
|
|
||||||
@"mode_123": @"key_123", // 字母面板左下角 "123"
|
|
||||||
@"mode_abc": @"key_abc", // 数字面板左下角 "abc"
|
|
||||||
@"symbols_toggle_more": @"key_symbols_more", // 数字面板内 "123 -> #+="
|
|
||||||
@"symbols_toggle_123": @"key_symbols_123", // 数字面板内 "#+= -> 123"
|
|
||||||
@"ai": @"key_ai", // 自定义 AI 功能键
|
|
||||||
@"return": @"key_send" // 发送/换行键
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
@@ -157,6 +49,7 @@
|
|||||||
|
|
||||||
- (void)applySkinWithJSON:(NSDictionary *)skinJSON
|
- (void)applySkinWithJSON:(NSDictionary *)skinJSON
|
||||||
fromViewController:(UIViewController *)presenting
|
fromViewController:(UIViewController *)presenting
|
||||||
|
mode:(KBSkinSourceMode)mode
|
||||||
completion:(KBSkinApplyCompletion)completion {
|
completion:(KBSkinApplyCompletion)completion {
|
||||||
if (skinJSON.count == 0) {
|
if (skinJSON.count == 0) {
|
||||||
if (completion) completion(NO);
|
if (completion) completion(NO);
|
||||||
@@ -177,10 +70,17 @@
|
|||||||
// [KBHUD showInfo:KBLocalized(@"皮肤已应用,键盘需开启“允许完全访问”后才能显示图片")];
|
// [KBHUD showInfo:KBLocalized(@"皮肤已应用,键盘需开启“允许完全访问”后才能显示图片")];
|
||||||
// }
|
// }
|
||||||
|
|
||||||
#if KB_SKIN_ICON_USE_REMOTE
|
switch (mode) {
|
||||||
[self kb_applySkinUsingRemoteIcons:skinJSON completion:completion];
|
case KBSkinSourceModeLocalBundleZip:
|
||||||
#else
|
// 本地 bundle 模式:zip_url 为 bundle 内的 zip 文件名
|
||||||
#endif
|
[self kb_applySkinUsingLocalBundle:skinJSON completion:completion];
|
||||||
|
break;
|
||||||
|
case KBSkinSourceModeRemoteZip:
|
||||||
|
default:
|
||||||
|
// 远程模式:zip_url 为 http/https 地址
|
||||||
|
[self kb_applySkinUsingRemoteIcons:skinJSON completion:completion];
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 远程 Zip 模式:skinJSON 提供 zip_url,一套皮肤一个压缩包。
|
/// 远程 Zip 模式:skinJSON 提供 zip_url,一套皮肤一个压缩包。
|
||||||
@@ -231,10 +131,7 @@
|
|||||||
__block BOOL zipOK = YES;
|
__block BOOL zipOK = YES;
|
||||||
|
|
||||||
#if __has_include(<SSZipArchive/SSZipArchive.h>)
|
#if __has_include(<SSZipArchive/SSZipArchive.h>)
|
||||||
// 若本地尚未缓存该皮肤资源且提供了 zip_url,则下载并解压 Zip 包。
|
// 若本地尚未缓存该皮肤资源且提供了 zip_url,则通过网络下载并解压 Zip 包。
|
||||||
// 支持两种来源:
|
|
||||||
// 1) 线上 URL(http/https):通过 KBNetworkManager 下载;
|
|
||||||
// 2) 本地测试:zip_url 以 "bundle://" 前缀开头,例如 "bundle://001.zip"。
|
|
||||||
if (!hasCachedAssets && zipURL.length > 0) {
|
if (!hasCachedAssets && zipURL.length > 0) {
|
||||||
dispatch_group_enter(group);
|
dispatch_group_enter(group);
|
||||||
|
|
||||||
@@ -314,32 +211,16 @@
|
|||||||
dispatch_group_leave(group);
|
dispatch_group_leave(group);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 本地 bundle 测试:zip_url 形如 "bundle://001.zip"
|
// 远程下载(http/https)
|
||||||
if ([zipURL hasPrefix:@"bundle://"]) {
|
[[KBNetworkManager shared] GET:zipURL parameters:nil headers:nil completion:^(id jsonOrData, NSURLResponse *response, NSError *error) {
|
||||||
NSString *name = [zipURL substringFromIndex:@"bundle://".length];
|
NSData *data = ([jsonOrData isKindOfClass:NSData.class] ? (NSData *)jsonOrData : nil);
|
||||||
NSString *fileName = name ?: @"";
|
if (error || data.length == 0) {
|
||||||
NSString *ext = fileName.pathExtension;
|
zipOK = NO;
|
||||||
NSString *base = fileName;
|
dispatch_group_leave(group);
|
||||||
if (ext.length == 0) {
|
return;
|
||||||
ext = @"zip";
|
|
||||||
} else {
|
|
||||||
base = [fileName stringByDeletingPathExtension];
|
|
||||||
}
|
}
|
||||||
NSString *path = [[NSBundle mainBundle] pathForResource:base ofType:ext];
|
|
||||||
NSData *data = (path.length > 0) ? [NSData dataWithContentsOfFile:path] : nil;
|
|
||||||
handleZipData(data);
|
handleZipData(data);
|
||||||
} else {
|
}];
|
||||||
// 正常远程下载
|
|
||||||
[[KBNetworkManager shared] GET:zipURL parameters:nil headers:nil completion:^(id jsonOrData, NSURLResponse *response, NSError *error) {
|
|
||||||
NSData *data = ([jsonOrData isKindOfClass:NSData.class] ? (NSData *)jsonOrData : nil);
|
|
||||||
if (error || data.length == 0) {
|
|
||||||
zipOK = NO;
|
|
||||||
dispatch_group_leave(group);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleZipData(data);
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
zipOK = NO;
|
zipOK = NO;
|
||||||
@@ -383,4 +264,184 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 本地 bundle 模式:不走网络,skin[@"zip_url"] 直接为 bundle 内 zip 文件名(可带/不带扩展名)。
|
||||||
|
/// - 仍然解压到 AppGroup/Skins/<skinId>/...,方便键盘扩展通过 App Group 读取。
|
||||||
|
- (void)kb_applySkinUsingLocalBundle:(NSDictionary *)skin completion:(KBSkinApplyCompletion)completion {
|
||||||
|
NSString *skinId = skin[@"id"] ?: @"local";
|
||||||
|
NSString *name = skin[@"name"] ?: skinId;
|
||||||
|
NSString *zipName = skin[@"zip_url"] ?: @""; // 本地 bundle 内的 zip 文件名
|
||||||
|
|
||||||
|
// key_icons 逻辑与远程模式保持一致
|
||||||
|
NSDictionary *iconShortNames = nil;
|
||||||
|
if ([skin[@"key_icons"] isKindOfClass:NSDictionary.class]) {
|
||||||
|
iconShortNames = skin[@"key_icons"];
|
||||||
|
} else {
|
||||||
|
iconShortNames = [self.class kb_defaultIconShortNames];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:AppGroup];
|
||||||
|
if (!containerURL) {
|
||||||
|
if (completion) completion(NO);
|
||||||
|
[KBHUD showInfo:KBLocalized(@"无法访问共享容器,应用皮肤失败")];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *skinsRoot = [containerURL.path stringByAppendingPathComponent:@"Skins"];
|
||||||
|
NSString *skinRoot = [skinsRoot stringByAppendingPathComponent:skinId];
|
||||||
|
NSString *iconsDir = [skinRoot stringByAppendingPathComponent:@"icons"];
|
||||||
|
[[NSFileManager defaultManager] createDirectoryAtPath:iconsDir
|
||||||
|
withIntermediateDirectories:YES
|
||||||
|
attributes:nil
|
||||||
|
error:NULL];
|
||||||
|
|
||||||
|
NSFileManager *fm = [NSFileManager defaultManager];
|
||||||
|
BOOL isDir = NO;
|
||||||
|
BOOL hasIconsDir = [fm fileExistsAtPath:iconsDir isDirectory:&isDir] && isDir;
|
||||||
|
NSArray *contents = hasIconsDir ? [fm contentsOfDirectoryAtPath:iconsDir error:NULL] : nil;
|
||||||
|
BOOL hasCachedAssets = (contents.count > 0);
|
||||||
|
|
||||||
|
NSString *bgPath = [skinRoot stringByAppendingPathComponent:@"background.png"];
|
||||||
|
|
||||||
|
dispatch_group_t group = dispatch_group_create();
|
||||||
|
__block BOOL zipOK = YES;
|
||||||
|
|
||||||
|
#if __has_include(<SSZipArchive/SSZipArchive.h>)
|
||||||
|
// 若本地尚未缓存该皮肤资源且提供了 zipName,则从 bundle 读取 zip 并解压。
|
||||||
|
if (!hasCachedAssets && zipName.length > 0) {
|
||||||
|
dispatch_group_enter(group);
|
||||||
|
|
||||||
|
// 兼容旧协议:zipName 可能形如 "bundle://001.zip"
|
||||||
|
NSString *fileName = zipName ?: @"";
|
||||||
|
if ([fileName hasPrefix:@"bundle://"]) {
|
||||||
|
fileName = [fileName substringFromIndex:@"bundle://".length];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持带子路径,例如 Images/001.zip
|
||||||
|
NSString *dir = [fileName stringByDeletingLastPathComponent];
|
||||||
|
NSString *last = fileName.lastPathComponent;
|
||||||
|
|
||||||
|
NSString *ext = last.pathExtension;
|
||||||
|
NSString *base = last;
|
||||||
|
if (ext.length == 0) {
|
||||||
|
ext = @"zip";
|
||||||
|
} else {
|
||||||
|
base = [last stringByDeletingPathExtension];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *path = nil;
|
||||||
|
if (dir.length > 0) {
|
||||||
|
path = [[NSBundle mainBundle] pathForResource:base ofType:ext inDirectory:dir];
|
||||||
|
} else {
|
||||||
|
path = [[NSBundle mainBundle] pathForResource:base ofType:ext];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSData *data = (path.length > 0) ? [NSData dataWithContentsOfFile:path] : nil;
|
||||||
|
|
||||||
|
if (data.length == 0) {
|
||||||
|
zipOK = NO;
|
||||||
|
dispatch_group_leave(group);
|
||||||
|
} else {
|
||||||
|
// 将 Zip 写入临时路径再解压
|
||||||
|
[[NSFileManager defaultManager] createDirectoryAtPath:skinRoot
|
||||||
|
withIntermediateDirectories:YES
|
||||||
|
attributes:nil
|
||||||
|
error:NULL];
|
||||||
|
NSString *zipPath = [skinRoot stringByAppendingPathComponent:@"skin.zip"];
|
||||||
|
if (![data writeToFile:zipPath atomically:YES]) {
|
||||||
|
zipOK = NO;
|
||||||
|
dispatch_group_leave(group);
|
||||||
|
} else {
|
||||||
|
NSError *unzipError = nil;
|
||||||
|
BOOL ok = [SSZipArchive unzipFileAtPath:zipPath
|
||||||
|
toDestination:skinRoot
|
||||||
|
overwrite:YES
|
||||||
|
password:nil
|
||||||
|
error:&unzipError];
|
||||||
|
[fm removeItemAtPath:zipPath error:nil];
|
||||||
|
if (!ok || unzipError) {
|
||||||
|
zipOK = NO;
|
||||||
|
dispatch_group_leave(group);
|
||||||
|
} else {
|
||||||
|
// 兼容“额外包一层目录”的压缩结构,与远程模式保持一致。
|
||||||
|
BOOL isDir2 = NO;
|
||||||
|
NSArray *iconsContent = [fm contentsOfDirectoryAtPath:iconsDir error:NULL];
|
||||||
|
BOOL iconsValid = ([fm fileExistsAtPath:iconsDir isDirectory:&isDir2] && isDir2 && iconsContent.count > 0);
|
||||||
|
if (!iconsValid) {
|
||||||
|
NSArray<NSString *> *subItems = [fm contentsOfDirectoryAtPath:skinRoot error:NULL];
|
||||||
|
for (NSString *name in subItems) {
|
||||||
|
if ([name isEqualToString:@"icons"] || [name isEqualToString:@"__MACOSX"]) continue;
|
||||||
|
NSString *nestedRoot = [skinRoot stringByAppendingPathComponent:name];
|
||||||
|
BOOL isDirNested = NO;
|
||||||
|
if (![fm fileExistsAtPath:nestedRoot isDirectory:&isDirNested] || !isDirNested) continue;
|
||||||
|
|
||||||
|
NSString *nestedIcons = [nestedRoot stringByAppendingPathComponent:@"icons"];
|
||||||
|
BOOL isDirNestedIcons = NO;
|
||||||
|
if ([fm fileExistsAtPath:nestedIcons isDirectory:&isDirNestedIcons] && isDirNestedIcons) {
|
||||||
|
NSArray *nestedFiles = [fm contentsOfDirectoryAtPath:nestedIcons error:NULL];
|
||||||
|
if (nestedFiles.count > 0) {
|
||||||
|
[fm createDirectoryAtPath:iconsDir
|
||||||
|
withIntermediateDirectories:YES
|
||||||
|
attributes:nil
|
||||||
|
error:NULL];
|
||||||
|
for (NSString *fn in nestedFiles) {
|
||||||
|
NSString *from = [nestedIcons stringByAppendingPathComponent:fn];
|
||||||
|
NSString *to = [iconsDir stringByAppendingPathComponent:fn];
|
||||||
|
[fm removeItemAtPath:to error:nil];
|
||||||
|
[fm moveItemAtPath:from toPath:to error:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *nestedBg = [nestedRoot stringByAppendingPathComponent:@"background.png"];
|
||||||
|
if ([fm fileExistsAtPath:nestedBg]) {
|
||||||
|
[fm removeItemAtPath:bgPath error:nil];
|
||||||
|
[fm moveItemAtPath:nestedBg toPath:bgPath error:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dispatch_group_leave(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
zipOK = NO;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
|
||||||
|
// 构造 key_icons -> App Group 相对路径 映射
|
||||||
|
NSMutableDictionary<NSString *, NSString *> *iconPathMap = [NSMutableDictionary dictionary];
|
||||||
|
[iconShortNames enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, NSString *shortName, BOOL *stop) {
|
||||||
|
if (![shortName isKindOfClass:NSString.class] || shortName.length == 0) return;
|
||||||
|
NSString *fileName = shortName;
|
||||||
|
if (fileName.pathExtension.length == 0) {
|
||||||
|
fileName = [fileName stringByAppendingPathExtension:@"png"];
|
||||||
|
}
|
||||||
|
NSString *relative = [NSString stringWithFormat:@"Skins/%@/icons/%@", skinId, fileName];
|
||||||
|
iconPathMap[identifier] = relative;
|
||||||
|
}];
|
||||||
|
|
||||||
|
NSMutableDictionary *themeJSON = [skin mutableCopy];
|
||||||
|
themeJSON[@"id"] = skinId;
|
||||||
|
if (iconPathMap.count > 0) {
|
||||||
|
themeJSON[@"key_icons"] = iconPathMap.copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL themeOK = [[KBSkinManager shared] applyThemeFromJSON:themeJSON];
|
||||||
|
|
||||||
|
NSData *bgData = [NSData dataWithContentsOfFile:bgPath];
|
||||||
|
BOOL ok = themeOK;
|
||||||
|
if (bgData.length > 0) {
|
||||||
|
ok = [[KBSkinManager shared] applyImageSkinWithData:bgData skinId:skinId name:name];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!zipOK && !hasCachedAssets) {
|
||||||
|
ok = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (completion) completion(ok);
|
||||||
|
[KBHUD showInfo:(ok ? KBLocalized(@"已应用,切到键盘查看") : KBLocalized(@"应用皮肤失败"))];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
242
keyBoard/Class/Resource/KBSkinIconMap.strings
Normal file
242
keyBoard/Class/Resource/KBSkinIconMap.strings
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
/* 字母 q(小写) */
|
||||||
|
"letter_q_lower" = "key_q";
|
||||||
|
/* 字母 Q(大写) */
|
||||||
|
"letter_q_upper" = "key_q";
|
||||||
|
|
||||||
|
/* 字母 w(小写) */
|
||||||
|
"letter_w_lower" = "key_w";
|
||||||
|
/* 字母 W(大写) */
|
||||||
|
"letter_w_upper" = "key_w";
|
||||||
|
|
||||||
|
/* 字母 e(小写) */
|
||||||
|
"letter_e_lower" = "key_e";
|
||||||
|
/* 字母 E(大写) */
|
||||||
|
"letter_e_upper" = "key_e";
|
||||||
|
|
||||||
|
/* 字母 r(小写) */
|
||||||
|
"letter_r_lower" = "key_r";
|
||||||
|
/* 字母 R(大写) */
|
||||||
|
"letter_r_upper" = "key_r";
|
||||||
|
|
||||||
|
/* 字母 t(小写) */
|
||||||
|
"letter_t_lower" = "key_t";
|
||||||
|
/* 字母 T(大写) */
|
||||||
|
"letter_t_upper" = "key_t";
|
||||||
|
|
||||||
|
/* 字母 y(小写) */
|
||||||
|
"letter_y_lower" = "key_y";
|
||||||
|
/* 字母 Y(大写) */
|
||||||
|
"letter_y_upper" = "key_y";
|
||||||
|
|
||||||
|
/* 字母 u(小写) */
|
||||||
|
"letter_u_lower" = "key_u";
|
||||||
|
/* 字母 U(大写) */
|
||||||
|
"letter_u_upper" = "key_u";
|
||||||
|
|
||||||
|
/* 字母 i(小写) */
|
||||||
|
"letter_i_lower" = "key_i";
|
||||||
|
/* 字母 I(大写) */
|
||||||
|
"letter_i_upper" = "key_i";
|
||||||
|
|
||||||
|
/* 字母 o(小写) */
|
||||||
|
"letter_o_lower" = "key_o";
|
||||||
|
/* 字母 O(大写) */
|
||||||
|
"letter_o_upper" = "key_o";
|
||||||
|
|
||||||
|
/* 字母 p(小写) */
|
||||||
|
"letter_p_lower" = "key_p";
|
||||||
|
/* 字母 P(大写) */
|
||||||
|
"letter_p_upper" = "key_p";
|
||||||
|
|
||||||
|
/* 字母 a(小写) */
|
||||||
|
"letter_a_lower" = "key_a";
|
||||||
|
/* 字母 A(大写) */
|
||||||
|
"letter_a_upper" = "key_a";
|
||||||
|
|
||||||
|
/* 字母 s(小写) */
|
||||||
|
"letter_s_lower" = "key_s";
|
||||||
|
/* 字母 S(大写) */
|
||||||
|
"letter_s_upper" = "key_s";
|
||||||
|
|
||||||
|
/* 字母 d(小写) */
|
||||||
|
"letter_d_lower" = "key_d";
|
||||||
|
/* 字母 D(大写) */
|
||||||
|
"letter_d_upper" = "key_d";
|
||||||
|
|
||||||
|
/* 字母 f(小写) */
|
||||||
|
"letter_f_lower" = "key_f";
|
||||||
|
/* 字母 F(大写) */
|
||||||
|
"letter_f_upper" = "key_f";
|
||||||
|
|
||||||
|
/* 字母 g(小写) */
|
||||||
|
"letter_g_lower" = "key_g";
|
||||||
|
/* 字母 G(大写) */
|
||||||
|
"letter_g_upper" = "key_g";
|
||||||
|
|
||||||
|
/* 字母 h(小写) */
|
||||||
|
"letter_h_lower" = "key_h";
|
||||||
|
/* 字母 H(大写) */
|
||||||
|
"letter_h_upper" = "key_h";
|
||||||
|
|
||||||
|
/* 字母 j(小写) */
|
||||||
|
"letter_j_lower" = "key_j";
|
||||||
|
/* 字母 J(大写) */
|
||||||
|
"letter_j_upper" = "key_j";
|
||||||
|
|
||||||
|
/* 字母 k(小写) */
|
||||||
|
"letter_k_lower" = "key_k";
|
||||||
|
/* 字母 K(大写) */
|
||||||
|
"letter_k_upper" = "key_k";
|
||||||
|
|
||||||
|
/* 字母 l(小写) */
|
||||||
|
"letter_l_lower" = "key_l";
|
||||||
|
/* 字母 L(大写) */
|
||||||
|
"letter_l_upper" = "key_l";
|
||||||
|
|
||||||
|
/* 字母 z(小写) */
|
||||||
|
"letter_z_lower" = "key_z";
|
||||||
|
/* 字母 Z(大写) */
|
||||||
|
"letter_z_upper" = "key_z";
|
||||||
|
|
||||||
|
/* 字母 x(小写) */
|
||||||
|
"letter_x_lower" = "key_x";
|
||||||
|
/* 字母 X(大写) */
|
||||||
|
"letter_x_upper" = "key_x";
|
||||||
|
|
||||||
|
/* 字母 c(小写) */
|
||||||
|
"letter_c_lower" = "key_c";
|
||||||
|
/* 字母 C(大写) */
|
||||||
|
"letter_c_upper" = "key_c";
|
||||||
|
|
||||||
|
/* 字母 v(小写) */
|
||||||
|
"letter_v_lower" = "key_v";
|
||||||
|
/* 字母 V(大写) */
|
||||||
|
"letter_v_upper" = "key_v";
|
||||||
|
|
||||||
|
/* 字母 b(小写) */
|
||||||
|
"letter_b_lower" = "key_b";
|
||||||
|
/* 字母 B(大写) */
|
||||||
|
"letter_b_upper" = "key_b";
|
||||||
|
|
||||||
|
/* 字母 n(小写) */
|
||||||
|
"letter_n_lower" = "key_n";
|
||||||
|
/* 字母 N(大写) */
|
||||||
|
"letter_n_upper" = "key_n";
|
||||||
|
|
||||||
|
/* 字母 m(小写) */
|
||||||
|
"letter_m_lower" = "key_m";
|
||||||
|
/* 字母 M(大写) */
|
||||||
|
"letter_m_upper" = "key_m";
|
||||||
|
|
||||||
|
/* 数字 1 */
|
||||||
|
"digit_1" = "key_1";
|
||||||
|
/* 数字 2 */
|
||||||
|
"digit_2" = "key_2";
|
||||||
|
/* 数字 3 */
|
||||||
|
"digit_3" = "key_3";
|
||||||
|
/* 数字 4 */
|
||||||
|
"digit_4" = "key_4";
|
||||||
|
/* 数字 5 */
|
||||||
|
"digit_5" = "key_5";
|
||||||
|
/* 数字 6 */
|
||||||
|
"digit_6" = "key_6";
|
||||||
|
/* 数字 7 */
|
||||||
|
"digit_7" = "key_7";
|
||||||
|
/* 数字 8 */
|
||||||
|
"digit_8" = "key_8";
|
||||||
|
/* 数字 9 */
|
||||||
|
"digit_9" = "key_9";
|
||||||
|
/* 数字 0 */
|
||||||
|
"digit_0" = "key_0";
|
||||||
|
|
||||||
|
/* '-' */
|
||||||
|
"sym_minus" = "key_minus";
|
||||||
|
/* '/' */
|
||||||
|
"sym_slash" = "key_slash";
|
||||||
|
/* ':' */
|
||||||
|
"sym_colon" = "key_colon";
|
||||||
|
/* ';' */
|
||||||
|
"sym_semicolon" = "key_semicolon";
|
||||||
|
/* '(' */
|
||||||
|
"sym_paren_l" = "key_paren_l";
|
||||||
|
/* ')' */
|
||||||
|
"sym_paren_r" = "key_paren_r";
|
||||||
|
/* '$' */
|
||||||
|
"sym_dollar" = "key_dollar";
|
||||||
|
/* '&' */
|
||||||
|
"sym_amp" = "key_amp";
|
||||||
|
/* '@' */
|
||||||
|
"sym_at" = "key_at";
|
||||||
|
/* 双引号 " */
|
||||||
|
"sym_quote_double" = "key_quote_d";
|
||||||
|
|
||||||
|
/* ',' */
|
||||||
|
"sym_comma" = "key_comma";
|
||||||
|
/* '.' */
|
||||||
|
"sym_dot" = "key_dot";
|
||||||
|
/* '?' */
|
||||||
|
"sym_question" = "key_question";
|
||||||
|
/* '!' */
|
||||||
|
"sym_exclam" = "key_exclam";
|
||||||
|
/* 单引号 ' */
|
||||||
|
"sym_quote_single" = "key_quote";
|
||||||
|
|
||||||
|
/* '[' */
|
||||||
|
"sym_bracket_l" = "key_bracket_l";
|
||||||
|
/* ']' */
|
||||||
|
"sym_bracket_r" = "key_bracket_r";
|
||||||
|
/* '{' */
|
||||||
|
"sym_brace_l" = "key_brace_l";
|
||||||
|
/* '}' */
|
||||||
|
"sym_brace_r" = "key_brace_r";
|
||||||
|
/* '#' */
|
||||||
|
"sym_hash" = "key_hash";
|
||||||
|
/* '%' */
|
||||||
|
"sym_percent" = "key_percent";
|
||||||
|
/* '^' */
|
||||||
|
"sym_caret" = "key_caret";
|
||||||
|
/* '*' */
|
||||||
|
"sym_asterisk" = "key_asterisk";
|
||||||
|
/* '+' */
|
||||||
|
"sym_plus" = "key_plus";
|
||||||
|
/* '=' */
|
||||||
|
"sym_equal" = "key_equal";
|
||||||
|
|
||||||
|
/* '_' */
|
||||||
|
"sym_underscore" = "key_underscore";
|
||||||
|
/* '\' */
|
||||||
|
"sym_backslash" = "key_backslash";
|
||||||
|
/* '|' */
|
||||||
|
"sym_pipe" = "key_pipe";
|
||||||
|
/* '~' */
|
||||||
|
"sym_tilde" = "key_tilde";
|
||||||
|
/* '<' */
|
||||||
|
"sym_lt" = "key_lt";
|
||||||
|
/* '>' */
|
||||||
|
"sym_gt" = "key_gt";
|
||||||
|
/* '€' */
|
||||||
|
"sym_euro" = "key_euro";
|
||||||
|
/* '£' */
|
||||||
|
"sym_pound" = "key_pound";
|
||||||
|
/* '•' */
|
||||||
|
"sym_bullet" = "key_bullet";
|
||||||
|
|
||||||
|
/* 空格键 */
|
||||||
|
"space" = "key_space";
|
||||||
|
/* 删除键(⌫) */
|
||||||
|
"backspace" = "key_del";
|
||||||
|
/* Shift(⇧) */
|
||||||
|
"shift" = "key_up";
|
||||||
|
/* 字母面板左下角 "123" */
|
||||||
|
"mode_123" = "key_123";
|
||||||
|
/* 数字面板左下角 "abc" */
|
||||||
|
"mode_abc" = "key_abc";
|
||||||
|
/* 数字面板内 "123 -> #+=" */
|
||||||
|
"symbols_toggle_more" = "key_symbols_more";
|
||||||
|
/* 数字面板内 "#+= -> 123" */
|
||||||
|
"symbols_toggle_123" = "key_symbols_123";
|
||||||
|
/* 自定义 AI 功能键 */
|
||||||
|
"ai" = "key_ai";
|
||||||
|
/* 发送/换行键 */
|
||||||
|
"return" = "key_send";
|
||||||
|
|
||||||
Reference in New Issue
Block a user