Compare commits
49 Commits
dev_语音_tab
...
635ad932c7
| Author | SHA1 | Date | |
|---|---|---|---|
| 635ad932c7 | |||
| cbe0a53cac | |||
| 5c273c3963 | |||
| c9743cb363 | |||
| f0cb69948e | |||
| 0144f9cc6d | |||
| ae4070ae88 | |||
| a83fd918a8 | |||
| 4168da618e | |||
| d2ffada83f | |||
| 76d387e08b | |||
| ea0df4fb19 | |||
| 02323fb5f1 | |||
| 3c71797b7b | |||
| 4c57f16058 | |||
| cb2e8467a7 | |||
| 4dfd6f5cbb | |||
| e4223b3a4c | |||
| 3d19403539 | |||
| 3cb02d5b76 | |||
| 750b391100 | |||
| faccf6f10f | |||
| 35b1fc0f1e | |||
| b73f225d15 | |||
| dd59094a16 | |||
| bacaf537f3 | |||
| 619d356d31 | |||
| db9f07d199 | |||
| 3ed120106e | |||
| ff4edab820 | |||
| 3e30f619b9 | |||
| 533e23ebfe | |||
| 85fb407717 | |||
| c1b50b407d | |||
| 7c7e2477cb | |||
| faae0297cb | |||
| e50eaecbd9 | |||
| 879dbb860c | |||
| b4e4b7b606 | |||
| 68a610e0a8 | |||
| 305326aa9a | |||
| 61095a379f | |||
| 822a814f85 | |||
| 0bd0392191 | |||
| b9663037f5 | |||
| a0923c8572 | |||
| d482cfcb7d | |||
| 9e6d2906f8 | |||
| 6f7bb4f960 |
8
.claude/settings.local.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"WebSearch",
|
||||
"Bash(git checkout:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
22
CustomKeyboard/KeyboardAssets.xcassets/ai_limit_close.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_limit_close@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_limit_close@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
CustomKeyboard/KeyboardAssets.xcassets/ai_limit_close.imageset/ai_limit_close@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
CustomKeyboard/KeyboardAssets.xcassets/ai_limit_close.imageset/ai_limit_close@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
22
CustomKeyboard/KeyboardAssets.xcassets/ai_limit_goto.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_limit_goto@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_limit_goto@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
CustomKeyboard/KeyboardAssets.xcassets/ai_limit_goto.imageset/ai_limit_goto@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
CustomKeyboard/KeyboardAssets.xcassets/ai_limit_goto.imageset/ai_limit_goto@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
22
CustomKeyboard/KeyboardAssets.xcassets/ai_limit_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_limit_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_limit_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
CustomKeyboard/KeyboardAssets.xcassets/ai_limit_icon.imageset/ai_limit_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 358 KiB |
BIN
CustomKeyboard/KeyboardAssets.xcassets/ai_limit_icon.imageset/ai_limit_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 733 KiB |
@@ -20,6 +20,7 @@
|
||||
#import "KBSettingView.h"
|
||||
#import "KBChatMessage.h"
|
||||
#import "KBChatPanelView.h"
|
||||
#import "KBChatLimitPopView.h"
|
||||
#import "KBSkinInstallBridge.h"
|
||||
#import "KBSkinManager.h"
|
||||
#import "KBSuggestionEngine.h"
|
||||
@@ -29,6 +30,9 @@
|
||||
#import "UIImage+KBColor.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <SDWebImage/SDWebImage.h>
|
||||
#if DEBUG
|
||||
#import <mach/mach.h>
|
||||
#endif
|
||||
|
||||
// #import "KBLog.h"
|
||||
|
||||
@@ -66,7 +70,8 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
@interface KeyboardViewController () <KBKeyBoardMainViewDelegate,
|
||||
KBFunctionViewDelegate,
|
||||
KBKeyboardSubscriptionViewDelegate,
|
||||
KBChatPanelViewDelegate>
|
||||
KBChatPanelViewDelegate,
|
||||
KBChatLimitPopViewDelegate>
|
||||
@property(nonatomic, strong)
|
||||
UIButton *nextKeyboardButton; // 系统“下一个键盘”按钮(可选)
|
||||
@property(nonatomic, strong) UIView *contentView;
|
||||
@@ -76,14 +81,12 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
KBFunctionView *functionView; // 功能面板视图(点击工具栏第0个时显示)
|
||||
@property(nonatomic, strong) KBSettingView *settingView; // 设置页
|
||||
@property(nonatomic, strong) UIImageView *bgImageView; // 背景图(在底层)
|
||||
@property(nonatomic, strong) UIImageView *personaAvatarImageView; // 语音模式下显示的 persona 小头像
|
||||
@property(nonatomic, strong) UIImageView *personaGrayImageView; // 语音模式下显示的 persona 小头像
|
||||
|
||||
@property(nonatomic, strong) KBChatPanelView *chatPanelView;
|
||||
@property(nonatomic, strong) KBKeyboardSubscriptionView *subscriptionView;
|
||||
@property(nonatomic, strong) KBSuggestionEngine *suggestionEngine;
|
||||
@property(nonatomic, copy) NSString *currentWord;
|
||||
@property(nonatomic, assign) BOOL suppressSuggestions;
|
||||
@property(nonatomic, strong) UIControl *chatLimitMaskView;
|
||||
@property(nonatomic, strong) MASConstraint *contentWidthConstraint;
|
||||
@property(nonatomic, strong) MASConstraint *contentHeightConstraint;
|
||||
@property(nonatomic, strong) MASConstraint *keyBoardMainHeightConstraint;
|
||||
@@ -92,19 +95,57 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
@property(nonatomic, strong) NSLayoutConstraint *kb_widthConstraint;
|
||||
@property(nonatomic, assign) CGFloat kb_lastPortraitWidth;
|
||||
@property(nonatomic, assign) CGFloat kb_lastKeyboardHeight;
|
||||
@property(nonatomic, strong) UIImage *kb_cachedGradientImage;
|
||||
@property(nonatomic, assign) CGSize kb_cachedGradientSize;
|
||||
@property(nonatomic, strong, nullable) CAGradientLayer *kb_defaultGradientLayer;
|
||||
@property(nonatomic, copy, nullable) NSString *kb_lastAppliedThemeKey;
|
||||
@property(nonatomic, strong) NSMutableArray<KBChatMessage *> *chatMessages;
|
||||
@property(nonatomic, strong) AVAudioPlayer *chatAudioPlayer;
|
||||
@property(nonatomic, assign) BOOL chatPanelVisible;
|
||||
@property(nonatomic, copy) NSString *chatPanelBaselineText; // 打开聊天面板时宿主输入框已有的文本
|
||||
@property(nonatomic, strong, nullable) id kb_fullAccessObserverToken;
|
||||
@property(nonatomic, strong, nullable) id kb_skinObserverToken;
|
||||
@end
|
||||
|
||||
#if DEBUG
|
||||
static NSInteger sKBKeyboardVCAliveCount = 0;
|
||||
|
||||
static uint64_t KBPhysFootprintBytes(void) {
|
||||
task_vm_info_data_t vmInfo;
|
||||
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
|
||||
kern_return_t kr = task_info(mach_task_self(), TASK_VM_INFO,
|
||||
(task_info_t)&vmInfo, &count);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
return (uint64_t)vmInfo.phys_footprint;
|
||||
}
|
||||
|
||||
static NSString *KBFormatMB(uint64_t bytes) {
|
||||
double mb = (double)bytes / 1024.0 / 1024.0;
|
||||
return [NSString stringWithFormat:@"%.1fMB", mb];
|
||||
}
|
||||
#endif
|
||||
|
||||
@implementation KeyboardViewController
|
||||
|
||||
{
|
||||
BOOL _kb_didTriggerLoginDeepLinkOnce;
|
||||
#if DEBUG
|
||||
BOOL _kb_debugDidCountAlive;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
#if DEBUG
|
||||
if (!_kb_debugDidCountAlive) {
|
||||
_kb_debugDidCountAlive = YES;
|
||||
sKBKeyboardVCAliveCount += 1;
|
||||
}
|
||||
NSLog(@"[Keyboard] KeyboardViewController viewDidLoad alive=%ld self=%p mem=%@",
|
||||
(long)sKBKeyboardVCAliveCount, self, KBFormatMB(KBPhysFootprintBytes()));
|
||||
#endif
|
||||
// 撤销删除是“上一段删除操作”的临时状态;键盘被系统回收/重建或跨页面回来时应当清空,避免误显示。
|
||||
[[KBBackspaceUndoManager shared] registerNonClearAction];
|
||||
[self setupUI];
|
||||
@@ -114,7 +155,7 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
[KBHUD setContainerView:self.view];
|
||||
// 绑定完全访问管理器,便于统一感知和联动网络开关
|
||||
[[KBFullAccessManager shared] bindInputController:self];
|
||||
__unused id token = [[NSNotificationCenter defaultCenter]
|
||||
self.kb_fullAccessObserverToken = [[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:KBFullAccessChangedNotification
|
||||
object:nil
|
||||
queue:[NSOperationQueue mainQueue]
|
||||
@@ -123,11 +164,16 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
}];
|
||||
|
||||
// 皮肤变化时,立即应用
|
||||
__unused id token2 = [[NSNotificationCenter defaultCenter]
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.kb_skinObserverToken = [[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:KBSkinDidChangeNotification
|
||||
object:nil
|
||||
queue:[NSOperationQueue mainQueue]
|
||||
usingBlock:^(__unused NSNotification *_Nonnull note) {
|
||||
__strong typeof(weakSelf) self = weakSelf;
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
[self kb_applyTheme];
|
||||
}];
|
||||
[self kb_applyTheme];
|
||||
@@ -140,12 +186,38 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
[self kb_applyDefaultSkinIfNeeded];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
// 扩展进程内存上限较小:在系统发出内存警告时主动清理可重建的缓存,降低被系统杀死概率。
|
||||
self.kb_cachedGradientImage = nil;
|
||||
[self.kb_defaultGradientLayer removeFromSuperlayer];
|
||||
self.kb_defaultGradientLayer = nil;
|
||||
[[KBSkinManager shared] clearRuntimeImageCaches];
|
||||
[[SDImageCache sharedImageCache] clearMemory];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
// FIX: iOS 26 键盘闪烁问题 —— 恢复键盘正确高度
|
||||
// setupUI 中高度初始为 0(防止系统预渲染快照闪烁),此处恢复为实际键盘高度。
|
||||
// 此时系统已准备好键盘滑入动画,恢复高度后键盘将正常从底部滑入。
|
||||
CGFloat portraitWidth = [self kb_portraitWidth];
|
||||
CGFloat keyboardHeight = [self kb_keyboardHeightForWidth:portraitWidth];
|
||||
if (self.kb_heightConstraint) {
|
||||
self.kb_heightConstraint.constant = keyboardHeight;
|
||||
}
|
||||
// 进入/重新进入输入界面时,清理上一次会话残留的撤销状态与缓存,避免显示“撤销删除”但实际上已不可撤销。
|
||||
[[KBBackspaceUndoManager shared] registerNonClearAction];
|
||||
[[KBInputBufferManager shared] resetWithText:@""];
|
||||
[[KBLocalizationManager shared] reloadFromSharedStorageIfNeeded];
|
||||
// 键盘再次出现时,恢复 HUD 容器与主题(viewDidDisappear 里可能已清理图片/缓存)。
|
||||
[KBHUD setContainerView:self.view];
|
||||
[self kb_ensureKeyBoardMainViewIfNeeded];
|
||||
[self kb_applyTheme];
|
||||
#if DEBUG
|
||||
NSLog(@"[Keyboard] viewWillAppear self=%p mem=%@",
|
||||
self, KBFormatMB(KBPhysFootprintBytes()));
|
||||
#endif
|
||||
// 注意:微信/QQ 等宿主的 documentContext 可能是“截断窗口”,这里只更新
|
||||
// liveText,不要把它当作全文 manualSnapshot。
|
||||
[[KBInputBufferManager shared]
|
||||
@@ -158,8 +230,17 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
[[KBBackspaceUndoManager shared] registerNonClearAction];
|
||||
// 清理 persona 头像内存
|
||||
[self kb_hidePersonaAvatar];
|
||||
[self kb_releaseMemoryWhenKeyboardHidden];
|
||||
#if DEBUG
|
||||
NSLog(@"[Keyboard] viewWillDisappear self=%p mem=%@",
|
||||
self, KBFormatMB(KBPhysFootprintBytes()));
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated {
|
||||
[super viewDidDisappear:animated];
|
||||
// 再兜底一次,防止某些宿主只触发 willDisappear 而未触发 didDisappear。
|
||||
[self kb_releaseMemoryWhenKeyboardHidden];
|
||||
}
|
||||
|
||||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
|
||||
@@ -167,6 +248,7 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
if (@available(iOS 13.0, *)) {
|
||||
if (previousTraitCollection.userInterfaceStyle !=
|
||||
self.traitCollection.userInterfaceStyle) {
|
||||
self.kb_cachedGradientImage = nil;
|
||||
[self kb_applyDefaultSkinIfNeeded];
|
||||
}
|
||||
}
|
||||
@@ -188,12 +270,17 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
CGFloat portraitWidth = [self kb_portraitWidth];
|
||||
CGFloat keyboardHeight = [self kb_keyboardHeightForWidth:portraitWidth];
|
||||
CGFloat keyboardBaseHeight = [self kb_keyboardBaseHeightForWidth:portraitWidth];
|
||||
CGFloat chatPanelHeight = [self kb_chatPanelHeightForWidth:portraitWidth];
|
||||
CGFloat screenWidth = CGRectGetWidth([UIScreen mainScreen].bounds);
|
||||
CGFloat outerVerticalInset = KBFit(4.0f);
|
||||
|
||||
// FIX: iOS 26 键盘闪烁问题
|
||||
// iOS 26 在键盘滑入动画开始前,会对 self.view 做一次离屏预渲染快照(非实时 view),
|
||||
// 该快照会短暂显示在屏幕中间。如果此时 view 已有完整高度和内容,用户就会看到
|
||||
// 键盘 UI 在屏幕中间闪现一帧,然后键盘才从底部正常滑入。
|
||||
// 解决方案:初始高度设为 0,让系统快照时无内容可渲染;
|
||||
// 在 viewWillAppear: 中恢复正确高度,此时系统已准备好滑入动画。
|
||||
// (iOS 18 及更早版本无此预渲染机制,不受影响)
|
||||
NSLayoutConstraint *h =
|
||||
[self.view.heightAnchor constraintEqualToConstant:keyboardHeight];
|
||||
[self.view.heightAnchor constraintEqualToConstant:0];
|
||||
NSLayoutConstraint *w =
|
||||
[self.view.widthAnchor constraintEqualToConstant:screenWidth];
|
||||
self.kb_heightConstraint = h;
|
||||
@@ -223,12 +310,6 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
[self.bgImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.contentView);
|
||||
}];
|
||||
// 预置功能面板(默认隐藏),与键盘区域共享相同布局
|
||||
self.functionView.hidden = YES;
|
||||
[self.contentView addSubview:self.functionView];
|
||||
[self.functionView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.contentView);
|
||||
}];
|
||||
|
||||
[self.contentView addSubview:self.keyBoardMainView];
|
||||
[self.keyBoardMainView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
@@ -238,14 +319,8 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
make.height.mas_equalTo(keyboardBaseHeight);
|
||||
}];
|
||||
|
||||
[self.contentView addSubview:self.chatPanelView];
|
||||
[self.chatPanelView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self.contentView);
|
||||
make.bottom.equalTo(self.keyBoardMainView.mas_top);
|
||||
self.chatPanelHeightConstraint =
|
||||
make.height.mas_equalTo(chatPanelHeight);
|
||||
}];
|
||||
self.chatPanelView.hidden = YES;
|
||||
// 初始隐藏,避免布局完成前闪烁
|
||||
self.contentView.hidden = YES;
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
@@ -385,8 +460,14 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
// 简单显隐切换,复用相同的布局区域
|
||||
if (show) {
|
||||
[self showChatPanel:NO];
|
||||
[self kb_ensureFunctionViewIfNeeded];
|
||||
}
|
||||
if (_functionView) {
|
||||
_functionView.hidden = !show;
|
||||
} else if (show) {
|
||||
// ensure 后按理已存在;这里兜底一次,避免异常情况下状态不一致
|
||||
self.functionView.hidden = NO;
|
||||
}
|
||||
self.functionView.hidden = !show;
|
||||
self.keyBoardMainView.hidden = show;
|
||||
|
||||
if (show) {
|
||||
@@ -406,7 +487,9 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
|
||||
// 可选:把当前显示的视图置顶,避免层级遮挡
|
||||
if (show) {
|
||||
[self.contentView bringSubviewToFront:self.functionView];
|
||||
if (_functionView) {
|
||||
[self.contentView bringSubviewToFront:_functionView];
|
||||
}
|
||||
} else {
|
||||
[self.contentView bringSubviewToFront:self.keyBoardMainView];
|
||||
}
|
||||
@@ -421,19 +504,18 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
pageId:@"keyboard_settings"
|
||||
extra:nil
|
||||
completion:nil];
|
||||
// if (!self.settingView) {
|
||||
self.settingView = [[KBSettingView alloc] init];
|
||||
self.settingView.hidden = YES;
|
||||
[self.contentView addSubview:self.settingView];
|
||||
[self.settingView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
// 与键盘主视图完全等同的区域,保证高度、宽度一致
|
||||
KBSettingView *settingView = self.settingView;
|
||||
if (!settingView.superview) {
|
||||
settingView.hidden = YES;
|
||||
[self.contentView addSubview:settingView];
|
||||
[settingView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.contentView);
|
||||
}];
|
||||
[self.settingView.backButton addTarget:self
|
||||
[settingView.backButton addTarget:self
|
||||
action:@selector(onTapSettingsBack)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
// }
|
||||
[self.contentView bringSubviewToFront:self.settingView];
|
||||
}
|
||||
[self.contentView bringSubviewToFront:settingView];
|
||||
// 以 keyBoardMainView 的实际宽度为准,避免首次添加时 self.view 宽度尚未计算
|
||||
[self.contentView layoutIfNeeded];
|
||||
CGFloat w = CGRectGetWidth(self.keyBoardMainView.bounds);
|
||||
@@ -443,17 +525,18 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
if (w <= 0) {
|
||||
w = [self kb_portraitWidth];
|
||||
}
|
||||
self.settingView.transform = CGAffineTransformMakeTranslation(w, 0);
|
||||
self.settingView.hidden = NO;
|
||||
settingView.transform = CGAffineTransformMakeTranslation(w, 0);
|
||||
settingView.hidden = NO;
|
||||
[UIView animateWithDuration:0.25
|
||||
delay:0
|
||||
options:UIViewAnimationOptionCurveEaseOut
|
||||
animations:^{
|
||||
self.settingView.transform = CGAffineTransformIdentity;
|
||||
settingView.transform = CGAffineTransformIdentity;
|
||||
}
|
||||
completion:nil];
|
||||
} else {
|
||||
if (!self.settingView || self.settingView.hidden)
|
||||
KBSettingView *settingView = self.settingView;
|
||||
if (!settingView.superview || settingView.hidden)
|
||||
return;
|
||||
CGFloat w = CGRectGetWidth(self.keyBoardMainView.bounds);
|
||||
if (w <= 0) {
|
||||
@@ -466,10 +549,10 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
delay:0
|
||||
options:UIViewAnimationOptionCurveEaseIn
|
||||
animations:^{
|
||||
self.settingView.transform = CGAffineTransformMakeTranslation(w, 0);
|
||||
settingView.transform = CGAffineTransformMakeTranslation(w, 0);
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
self.settingView.hidden = YES;
|
||||
settingView.hidden = YES;
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -481,10 +564,16 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
}
|
||||
self.chatPanelVisible = show;
|
||||
if (show) {
|
||||
// 记录打开聊天面板时宿主输入框已有的文本,发送时只取新增部分
|
||||
[[KBInputBufferManager shared] refreshFromProxyIfPossible:self.textDocumentProxy];
|
||||
self.chatPanelBaselineText = [KBInputBufferManager shared].liveText ?: @"";
|
||||
[self kb_ensureChatPanelViewIfNeeded];
|
||||
self.chatPanelView.hidden = NO;
|
||||
self.chatPanelView.alpha = 0.0;
|
||||
[self.contentView bringSubviewToFront:self.chatPanelView];
|
||||
self.functionView.hidden = YES;
|
||||
if (_functionView) {
|
||||
_functionView.hidden = YES;
|
||||
}
|
||||
[self hideSubscriptionPanel];
|
||||
[self showSettingView:NO];
|
||||
[UIView animateWithDuration:0.2
|
||||
@@ -495,6 +584,11 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
}
|
||||
completion:nil];
|
||||
} else {
|
||||
// 从未创建过聊天面板时,直接返回,避免 show/hide 触发额外内存分配
|
||||
if (!_chatPanelView) {
|
||||
[self kb_updateKeyboardLayoutIfNeeded];
|
||||
return;
|
||||
}
|
||||
[UIView animateWithDuration:0.18
|
||||
delay:0
|
||||
options:UIViewAnimationOptionCurveEaseIn
|
||||
@@ -508,6 +602,114 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
[self kb_updateKeyboardLayoutIfNeeded];
|
||||
}
|
||||
|
||||
// 延迟创建:仅在用户真正打开功能面板时才创建/布局,降低默认内存占用。
|
||||
- (void)kb_ensureFunctionViewIfNeeded {
|
||||
if (_functionView && _functionView.superview) {
|
||||
return;
|
||||
}
|
||||
KBFunctionView *v = self.functionView;
|
||||
if (!v.superview) {
|
||||
v.hidden = YES;
|
||||
[self.contentView addSubview:v];
|
||||
[v mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.contentView);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// 延迟创建:仅在用户打开聊天面板时才创建/布局。
|
||||
- (void)kb_ensureChatPanelViewIfNeeded {
|
||||
if (_chatPanelView && _chatPanelView.superview) {
|
||||
return;
|
||||
}
|
||||
CGFloat portraitWidth = [self kb_portraitWidth];
|
||||
CGFloat chatPanelHeight = [self kb_chatPanelHeightForWidth:portraitWidth];
|
||||
KBChatPanelView *v = self.chatPanelView;
|
||||
if (!v.superview) {
|
||||
[self.contentView addSubview:v];
|
||||
[v mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self.contentView);
|
||||
make.bottom.equalTo(self.keyBoardMainView.mas_top);
|
||||
self.chatPanelHeightConstraint =
|
||||
make.height.mas_equalTo(chatPanelHeight);
|
||||
}];
|
||||
v.hidden = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// 延迟创建:键盘主面板(按键区)在隐藏时会被释放;再次显示时需要重建。
|
||||
- (void)kb_ensureKeyBoardMainViewIfNeeded {
|
||||
if (_keyBoardMainView && _keyBoardMainView.superview) {
|
||||
return;
|
||||
}
|
||||
CGFloat portraitWidth = [self kb_portraitWidth];
|
||||
CGFloat keyboardBaseHeight =
|
||||
[self kb_keyboardBaseHeightForWidth:portraitWidth];
|
||||
KBKeyBoardMainView *v = self.keyBoardMainView;
|
||||
if (!v.superview) {
|
||||
[self.contentView addSubview:v];
|
||||
[v mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self.contentView);
|
||||
make.bottom.equalTo(self.contentView);
|
||||
self.keyBoardMainHeightConstraint =
|
||||
make.height.mas_equalTo(keyboardBaseHeight);
|
||||
}];
|
||||
}
|
||||
[self.contentView bringSubviewToFront:v];
|
||||
}
|
||||
|
||||
// 键盘隐藏时释放可重建资源(背景图/缓存/非必需面板),降低扩展内存峰值。
|
||||
- (void)kb_releaseMemoryWhenKeyboardHidden {
|
||||
[KBHUD setContainerView:nil];
|
||||
self.bgImageView.image = nil;
|
||||
self.kb_cachedGradientImage = nil;
|
||||
[self.kb_defaultGradientLayer removeFromSuperlayer];
|
||||
self.kb_defaultGradientLayer = nil;
|
||||
[[SDImageCache sharedImageCache] clearMemory];
|
||||
|
||||
// 聊天相关可能持有音频数据/临时文件,键盘隐藏时直接清空,避免累计占用。
|
||||
if (self.chatAudioPlayer) {
|
||||
[self.chatAudioPlayer stop];
|
||||
self.chatAudioPlayer = nil;
|
||||
}
|
||||
if (_chatMessages.count > 0) {
|
||||
NSString *tmpRoot = NSTemporaryDirectory();
|
||||
for (KBChatMessage *msg in _chatMessages.copy) {
|
||||
if (tmpRoot.length > 0 && msg.audioFilePath.length > 0 &&
|
||||
[msg.audioFilePath hasPrefix:tmpRoot]) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:msg.audioFilePath
|
||||
error:nil];
|
||||
}
|
||||
}
|
||||
[_chatMessages removeAllObjects];
|
||||
}
|
||||
|
||||
if (_keyBoardMainView) {
|
||||
[_keyBoardMainView removeFromSuperview];
|
||||
_keyBoardMainView = nil;
|
||||
}
|
||||
self.keyBoardMainHeightConstraint = nil;
|
||||
|
||||
if (_functionView) {
|
||||
[_functionView removeFromSuperview];
|
||||
_functionView = nil;
|
||||
}
|
||||
if (_chatPanelView) {
|
||||
[_chatPanelView removeFromSuperview];
|
||||
_chatPanelView = nil;
|
||||
}
|
||||
self.chatPanelVisible = NO;
|
||||
|
||||
if (_subscriptionView) {
|
||||
[_subscriptionView removeFromSuperview];
|
||||
_subscriptionView = nil;
|
||||
}
|
||||
if (_settingView) {
|
||||
[_settingView removeFromSuperview];
|
||||
_settingView = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showSubscriptionPanel {
|
||||
// 1) 先判断权限:未开启“完全访问”则走引导逻辑
|
||||
if (![[KBFullAccessManager shared] hasFullAccess]) {
|
||||
@@ -659,8 +861,6 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
if (index == 1) {
|
||||
[self showFunctionPanel:NO];
|
||||
[self showChatPanel:YES];
|
||||
// 显示 persona 头像
|
||||
[self kb_showPersonaAvatarOnBgImageView];
|
||||
return;
|
||||
}
|
||||
[self showFunctionPanel:NO];
|
||||
@@ -822,14 +1022,12 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
|
||||
- (void)chatPanelViewDidTapClose:(KBChatPanelView *)view {
|
||||
// 清空 chatPanelView 内部的消息
|
||||
[self.chatPanelView kb_reloadWithMessages:@[]];
|
||||
[view kb_reloadWithMessages:@[]];
|
||||
if (self.chatAudioPlayer.isPlaying) {
|
||||
[self.chatAudioPlayer stop];
|
||||
}
|
||||
self.chatAudioPlayer = nil;
|
||||
[self showChatPanel:NO];
|
||||
// 隐藏 persona 头像
|
||||
[self kb_hidePersonaAvatar];
|
||||
}
|
||||
|
||||
#pragma mark - Chat Helpers
|
||||
@@ -839,7 +1037,15 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
return;
|
||||
}
|
||||
[[KBInputBufferManager shared] refreshFromProxyIfPossible:self.textDocumentProxy];
|
||||
NSString *rawText = [KBInputBufferManager shared].liveText ?: @"";
|
||||
NSString *fullText = [KBInputBufferManager shared].liveText ?: @"";
|
||||
|
||||
// 去掉打开聊天面板前宿主输入框里已有的基线文本,只取新增部分
|
||||
NSString *baseline = self.chatPanelBaselineText ?: @"";
|
||||
NSString *rawText = fullText;
|
||||
if (baseline.length > 0 && [fullText hasPrefix:baseline]) {
|
||||
rawText = [fullText substringFromIndex:baseline.length];
|
||||
}
|
||||
|
||||
NSString *trim =
|
||||
[rawText stringByTrimmingCharactersInSet:
|
||||
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
@@ -848,6 +1054,7 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
return;
|
||||
}
|
||||
[self kb_sendChatText:trim];
|
||||
// 只清除新增的文本,保留基线文本
|
||||
[self kb_clearHostInputForText:rawText];
|
||||
}
|
||||
|
||||
@@ -874,6 +1081,58 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
[self kb_requestChatMessageWithContent:text];
|
||||
}
|
||||
|
||||
#pragma mark - Chat Limit Pop
|
||||
|
||||
- (void)kb_showChatLimitPopWithMessage:(NSString *)message {
|
||||
[self kb_dismissChatLimitPop];
|
||||
|
||||
UIControl *mask = [[UIControl alloc] init];
|
||||
mask.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
|
||||
mask.alpha = 0.0;
|
||||
[mask addTarget:self
|
||||
action:@selector(kb_dismissChatLimitPop)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.contentView addSubview:mask];
|
||||
[mask mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.contentView);
|
||||
}];
|
||||
|
||||
CGFloat width = 252.0;
|
||||
CGFloat height = 252.0 + 18.0 + 53.0 + 18.0 + 28.0;
|
||||
KBChatLimitPopView *content =
|
||||
[[KBChatLimitPopView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
|
||||
content.message = message ?: @"";
|
||||
content.delegate = self;
|
||||
[mask addSubview:content];
|
||||
[content mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.center.equalTo(mask);
|
||||
make.width.mas_equalTo(width);
|
||||
make.height.mas_equalTo(height);
|
||||
}];
|
||||
|
||||
self.chatLimitMaskView = mask;
|
||||
[self.contentView bringSubviewToFront:mask];
|
||||
[UIView animateWithDuration:0.18
|
||||
animations:^{
|
||||
mask.alpha = 1.0;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)kb_dismissChatLimitPop {
|
||||
if (!self.chatLimitMaskView) {
|
||||
return;
|
||||
}
|
||||
UIControl *mask = self.chatLimitMaskView;
|
||||
self.chatLimitMaskView = nil;
|
||||
[UIView animateWithDuration:0.15
|
||||
animations:^{
|
||||
mask.alpha = 0.0;
|
||||
}
|
||||
completion:^(__unused BOOL finished) {
|
||||
[mask removeFromSuperview];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)kb_clearHostInputForText:(NSString *)text {
|
||||
if (text.length == 0) {
|
||||
return;
|
||||
@@ -1017,16 +1276,22 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
__strong typeof(weakSelf) self = weakSelf;
|
||||
if (!self) return;
|
||||
|
||||
if (!response.success) {
|
||||
NSLog(@"[KB] ❌ 请求失败: %@", response.errorMessage);
|
||||
if (response.code != 0) {
|
||||
if (response.code == 50030) {
|
||||
NSLog(@"[KB] ⚠️ 次数用尽: %@", response.message);
|
||||
[self.chatPanelView kb_removeLoadingAssistantMessage];
|
||||
[KBHUD showInfo:response.errorMessage ?: KBLocalized(@"请求失败")];
|
||||
[self kb_showChatLimitPopWithMessage:response.message];
|
||||
return;
|
||||
}
|
||||
NSLog(@"[KB] ❌ 请求失败: %@", response.message);
|
||||
[self.chatPanelView kb_removeLoadingAssistantMessage];
|
||||
[KBHUD showInfo:response.message ?: KBLocalized(@"请求失败")];
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"[KB] ✅ 收到回复: %@", response.text);
|
||||
NSLog(@"[KB] ✅ 收到回复: %@", response.data.aiResponse);
|
||||
|
||||
if (response.text.length == 0) {
|
||||
if (response.data.aiResponse.length == 0) {
|
||||
[self.chatPanelView kb_removeLoadingAssistantMessage];
|
||||
[KBHUD showInfo:KBLocalized(@"未获取到回复内容")];
|
||||
return;
|
||||
@@ -1034,12 +1299,12 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
|
||||
// 添加 AI 消息(带打字机效果)
|
||||
NSLog(@"[KB] 准备添加 AI 消息");
|
||||
[self.chatPanelView kb_addAssistantMessage:response.text audioId:response.audioId];
|
||||
[self.chatPanelView kb_addAssistantMessage:response.data.aiResponse audioId:response.data.audioId];
|
||||
NSLog(@"[KB] AI 消息添加完成");
|
||||
|
||||
// 如果有 audioId,开始预加载音频
|
||||
if (response.audioId.length > 0) {
|
||||
[self kb_preloadAudioWithAudioId:response.audioId];
|
||||
if (response.data.audioId.length > 0) {
|
||||
[self kb_preloadAudioWithAudioId:response.data.audioId];
|
||||
}
|
||||
}];
|
||||
}
|
||||
@@ -1351,6 +1616,25 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
[self kb_openRechargeForProduct:product];
|
||||
}
|
||||
|
||||
#pragma mark - KBChatLimitPopViewDelegate
|
||||
|
||||
- (void)chatLimitPopViewDidTapCancel:(KBChatLimitPopView *)view {
|
||||
[self kb_dismissChatLimitPop];
|
||||
}
|
||||
|
||||
- (void)chatLimitPopViewDidTapRecharge:(KBChatLimitPopView *)view {
|
||||
[self kb_dismissChatLimitPop];
|
||||
NSString *urlString =
|
||||
[NSString stringWithFormat:@"%@://recharge?src=keyboard&vipType=svip",
|
||||
KB_APP_SCHEME];
|
||||
NSURL *scheme = [NSURL URLWithString:urlString];
|
||||
BOOL success = [KBHostAppLauncher openHostAppURL:scheme
|
||||
fromResponder:self.view];
|
||||
if (!success) {
|
||||
[KBHUD showInfo:KBLocalized(@"Please open the App to finish purchase")];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - lazy
|
||||
- (KBKeyBoardMainView *)keyBoardMainView {
|
||||
if (!_keyBoardMainView) {
|
||||
@@ -1401,92 +1685,6 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
return _subscriptionView;
|
||||
}
|
||||
|
||||
- (UIImageView *)personaAvatarImageView {
|
||||
if (!_personaAvatarImageView) {
|
||||
_personaAvatarImageView = [[UIImageView alloc] init];
|
||||
_personaAvatarImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
_personaAvatarImageView.clipsToBounds = YES;
|
||||
_personaAvatarImageView.hidden = YES;
|
||||
}
|
||||
return _personaAvatarImageView;
|
||||
}
|
||||
- (UIImageView *)personaGrayImageView{
|
||||
if (!_personaGrayImageView) {
|
||||
_personaGrayImageView = [[UIImageView alloc] init];
|
||||
_personaAvatarImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
|
||||
}
|
||||
return _personaGrayImageView;
|
||||
}
|
||||
|
||||
#pragma mark - Persona Avatar
|
||||
|
||||
/// 从 AppGroup 读取选中的 persona 信息
|
||||
- (NSDictionary *)kb_selectedPersonaFromAppGroup {
|
||||
NSUserDefaults *ud = [[NSUserDefaults alloc] initWithSuiteName:AppGroup];
|
||||
NSDictionary *personaDict = [ud objectForKey:@"AppGroup_SelectedPersona"];
|
||||
if ([personaDict isKindOfClass:[NSDictionary class]]) {
|
||||
return personaDict;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
/// 在 bgImageView 上显示 persona 头像
|
||||
- (void)kb_showPersonaAvatarOnBgImageView {
|
||||
// 检查是否有完全访问权限
|
||||
if (![[KBFullAccessManager shared] hasFullAccess]) {
|
||||
NSLog(@"[Keyboard] 未开启完全访问,无法显示 persona 头像");
|
||||
return;
|
||||
}
|
||||
|
||||
// 从 AppGroup 共享目录读取预处理好的小图片
|
||||
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:AppGroup];
|
||||
if (!containerURL) {
|
||||
NSLog(@"[Keyboard] 无法获取 AppGroup 容器目录");
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *imagePath = [[containerURL path] stringByAppendingPathComponent:@"persona_cover.jpg"];
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:imagePath]) {
|
||||
NSLog(@"[Keyboard] persona 封面图文件不存在: %@", imagePath);
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"[Keyboard] 准备从本地加载 persona 封面图: %@", imagePath);
|
||||
|
||||
// 添加视图到 contentView,与 bgImageView 尺寸一致
|
||||
if (!self.personaAvatarImageView.superview) {
|
||||
[self.contentView insertSubview:self.personaAvatarImageView aboveSubview:self.bgImageView];
|
||||
[self.personaAvatarImageView addSubview:self.personaGrayImageView];
|
||||
[self.personaAvatarImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.bgImageView);
|
||||
}];
|
||||
[self.personaGrayImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.bottom.equalTo(self.personaAvatarImageView);
|
||||
make.height.mas_equalTo(self.keyBoardMainView);
|
||||
}];
|
||||
}
|
||||
|
||||
// 先清理旧图片
|
||||
self.personaAvatarImageView.image = nil;
|
||||
|
||||
// 从本地文件加载图片(已经是缩小后的小图片,内存占用很小)
|
||||
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
|
||||
if (image) {
|
||||
self.personaAvatarImageView.image = image;
|
||||
self.personaAvatarImageView.hidden = NO;
|
||||
NSLog(@"[Keyboard] persona 封面图加载成功");
|
||||
} else {
|
||||
NSLog(@"[Keyboard] persona 封面图加载失败");
|
||||
}
|
||||
}
|
||||
|
||||
/// 隐藏 persona 头像
|
||||
- (void)kb_hidePersonaAvatar {
|
||||
self.personaAvatarImageView.hidden = YES;
|
||||
self.personaAvatarImageView.image = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)kb_openRechargeForProduct:(KBKeyboardSubscriptionProduct *)product {
|
||||
@@ -1541,10 +1739,26 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (self.kb_fullAccessObserverToken) {
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
removeObserver:self.kb_fullAccessObserverToken];
|
||||
self.kb_fullAccessObserverToken = nil;
|
||||
}
|
||||
if (self.kb_skinObserverToken) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self.kb_skinObserverToken];
|
||||
self.kb_skinObserverToken = nil;
|
||||
}
|
||||
CFNotificationCenterRemoveObserver(
|
||||
CFNotificationCenterGetDarwinNotifyCenter(),
|
||||
(__bridge const void *)(self),
|
||||
(__bridge CFStringRef)KBDarwinSkinInstallRequestNotification, NULL);
|
||||
#if DEBUG
|
||||
if (_kb_debugDidCountAlive) {
|
||||
sKBKeyboardVCAliveCount -= 1;
|
||||
}
|
||||
NSLog(@"[Keyboard] KeyboardViewController dealloc alive=%ld self=%p mem=%@",
|
||||
(long)sKBKeyboardVCAliveCount, self, KBFormatMB(KBPhysFootprintBytes()));
|
||||
#endif
|
||||
}
|
||||
|
||||
// 当键盘第一次显示时,尝试唤起主 App 以提示登录(由主 App
|
||||
@@ -1562,7 +1776,15 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
[self kb_updateKeyboardLayoutIfNeeded];
|
||||
// [self kb_updateKeyboardLayoutIfNeeded];
|
||||
|
||||
// 首次布局完成后显示,避免闪烁
|
||||
if (self.contentView.hidden) {
|
||||
self.contentView.hidden = NO;
|
||||
}
|
||||
if (self.kb_defaultGradientLayer) {
|
||||
self.kb_defaultGradientLayer.frame = self.bgImageView.bounds;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillTransitionToSize:(CGSize)size
|
||||
@@ -1596,10 +1818,23 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
#pragma mark - Theme
|
||||
|
||||
- (void)kb_applyTheme {
|
||||
@autoreleasepool {
|
||||
KBSkinTheme *t = [KBSkinManager shared].current;
|
||||
UIImage *img = [[KBSkinManager shared] currentBackgroundImage];
|
||||
UIImage *img = nil;
|
||||
BOOL isDefaultTheme = [self kb_isDefaultKeyboardTheme:t];
|
||||
BOOL isDarkMode = [self kb_isDarkModeActive];
|
||||
|
||||
NSString *skinId = t.skinId ?: @"";
|
||||
NSString *themeKey =
|
||||
[NSString stringWithFormat:@"%@|default=%d|dark=%d",
|
||||
skinId, isDefaultTheme, isDarkMode];
|
||||
BOOL themeChanged =
|
||||
(self.kb_lastAppliedThemeKey.length == 0 ||
|
||||
![self.kb_lastAppliedThemeKey isEqualToString:themeKey]);
|
||||
if (themeChanged) {
|
||||
self.kb_lastAppliedThemeKey = themeKey;
|
||||
}
|
||||
|
||||
CGSize size = self.bgImageView.bounds.size;
|
||||
if (isDefaultTheme) {
|
||||
if (isDarkMode) {
|
||||
@@ -1607,6 +1842,8 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
// 这样可以避免图片渲染时的色彩空间转换导致颜色不一致
|
||||
img = nil;
|
||||
self.bgImageView.image = nil;
|
||||
[self.kb_defaultGradientLayer removeFromSuperlayer];
|
||||
self.kb_defaultGradientLayer = nil;
|
||||
// 使用与系统键盘底部完全相同的颜色
|
||||
if (@available(iOS 13.0, *)) {
|
||||
// iOS 系统键盘使用的实际颜色 (RGB: 44, 44, 46 in sRGB, 或 #2C2C2E)
|
||||
@@ -1639,7 +1876,7 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
self.bgImageView.backgroundColor = darkColor;
|
||||
}
|
||||
} else {
|
||||
// 浅色模式:使用渐变图片
|
||||
// 浅色模式:使用渐变层(避免生成大位图导致内存上涨)
|
||||
if (size.width <= 0 || size.height <= 0) {
|
||||
[self.view layoutIfNeeded];
|
||||
size = self.bgImageView.bounds.size;
|
||||
@@ -1652,9 +1889,29 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
}
|
||||
UIColor *topColor = [UIColor colorWithHex:0xDEDFE4];
|
||||
UIColor *bottomColor = [UIColor colorWithHex:0xD1D3DB];
|
||||
img = [self kb_defaultGradientImageWithSize:size
|
||||
topColor:topColor
|
||||
bottomColor:bottomColor];
|
||||
UIColor *resolvedTopColor = topColor;
|
||||
UIColor *resolvedBottomColor = bottomColor;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
resolvedTopColor =
|
||||
[topColor resolvedColorWithTraitCollection:self.traitCollection];
|
||||
resolvedBottomColor = [bottomColor
|
||||
resolvedColorWithTraitCollection:self.traitCollection];
|
||||
}
|
||||
CAGradientLayer *layer = self.kb_defaultGradientLayer;
|
||||
if (!layer) {
|
||||
layer = [CAGradientLayer layer];
|
||||
layer.startPoint = CGPointMake(0.5, 0.0);
|
||||
layer.endPoint = CGPointMake(0.5, 1.0);
|
||||
[self.bgImageView.layer insertSublayer:layer atIndex:0];
|
||||
self.kb_defaultGradientLayer = layer;
|
||||
}
|
||||
layer.colors = @[
|
||||
(id)resolvedTopColor.CGColor,
|
||||
(id)resolvedBottomColor.CGColor
|
||||
];
|
||||
layer.frame = (CGRect){CGPointZero, size};
|
||||
img = nil;
|
||||
self.bgImageView.image = nil;
|
||||
self.contentView.backgroundColor = [UIColor clearColor];
|
||||
self.bgImageView.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
@@ -1663,28 +1920,32 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
// 自定义皮肤:清除背景色,使用皮肤图片
|
||||
self.contentView.backgroundColor = [UIColor clearColor];
|
||||
self.bgImageView.backgroundColor = [UIColor clearColor];
|
||||
[self.kb_defaultGradientLayer removeFromSuperlayer];
|
||||
self.kb_defaultGradientLayer = nil;
|
||||
img = [[KBSkinManager shared] currentBackgroundImage];
|
||||
}
|
||||
NSLog(@"⌨️[Keyboard] apply theme id=%@ hasBg=%d", t.skinId, (img != nil));
|
||||
[self kb_logSkinDiagnosticsWithTheme:t backgroundImage:img];
|
||||
self.bgImageView.image = img;
|
||||
self.personaGrayImageView.image = img;
|
||||
|
||||
// [self.chatPanelView kb_setBackgroundImage:img];
|
||||
BOOL hasImg = (img != nil);
|
||||
// 触发键区按主题重绘
|
||||
if ([self.keyBoardMainView respondsToSelector:@selector(kb_applyTheme)]) {
|
||||
if (themeChanged &&
|
||||
[self.keyBoardMainView respondsToSelector:@selector(kb_applyTheme)]) {
|
||||
// method declared in KBKeyBoardMainView.h
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
[self.keyBoardMainView performSelector:@selector(kb_applyTheme)];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
if ([self.functionView respondsToSelector:@selector(kb_applyTheme)]) {
|
||||
// 注意:这里不能直接访问 self.functionView,否则会导致功能面板提前创建,占用内存。
|
||||
if (themeChanged && _functionView &&
|
||||
[_functionView respondsToSelector:@selector(kb_applyTheme)]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
[self.functionView performSelector:@selector(kb_applyTheme)];
|
||||
[_functionView performSelector:@selector(kb_applyTheme)];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)kb_isDefaultKeyboardTheme:(KBSkinTheme *)theme {
|
||||
@@ -1722,8 +1983,12 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 将动态颜色解析为当前 trait collection 下的具体颜色值
|
||||
// 否则在 UIGraphicsBeginImageContextWithOptions 中渲染时会使用默认的浅色模式
|
||||
// 尺寸未变则复用缓存,避免反复创建图片撑爆键盘扩展内存
|
||||
if (self.kb_cachedGradientImage &&
|
||||
CGSizeEqualToSize(self.kb_cachedGradientSize, size)) {
|
||||
return self.kb_cachedGradientImage;
|
||||
}
|
||||
|
||||
UIColor *resolvedTopColor = topColor;
|
||||
UIColor *resolvedBottomColor = bottomColor;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
@@ -1744,6 +2009,9 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
|
||||
[layer renderInContext:UIGraphicsGetCurrentContext()];
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
self.kb_cachedGradientImage = image;
|
||||
self.kb_cachedGradientSize = size;
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,15 +7,25 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class KBChatDataModel;
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// 聊天响应模型
|
||||
@interface KBChatResponse : NSObject
|
||||
@property (nonatomic, copy, nullable) NSString *text;
|
||||
@property (nonatomic, copy, nullable) NSString *audioId;
|
||||
@property (nonatomic, copy, nullable) NSString *errorMessage;
|
||||
@property (nonatomic, strong, nullable) KBChatDataModel *data;
|
||||
//@property (nonatomic, copy, nullable) NSString *audioId;
|
||||
@property (nonatomic, copy, nullable) NSString *message;
|
||||
@property (nonatomic, assign) BOOL success;
|
||||
@property (nonatomic, assign) NSInteger code;
|
||||
|
||||
@end
|
||||
|
||||
@interface KBChatDataModel : NSObject
|
||||
@property (nonatomic, copy, nullable) NSString *aiResponse;
|
||||
@property (nonatomic, copy, nullable) NSString *audioId;
|
||||
@property (nonatomic, copy, nullable) NSString *llmDuration;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
/// 音频响应模型
|
||||
|
||||
@@ -7,10 +7,14 @@
|
||||
#import "KBNetworkManager.h"
|
||||
#import "KBConfig.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <MJExtension/MJExtension.h>
|
||||
|
||||
@implementation KBChatResponse
|
||||
@end
|
||||
|
||||
@implementation KBChatDataModel
|
||||
@end
|
||||
|
||||
@implementation KBAudioResponse
|
||||
@end
|
||||
|
||||
@@ -46,7 +50,7 @@
|
||||
if (completion) {
|
||||
KBChatResponse *response = [[KBChatResponse alloc] init];
|
||||
response.success = NO;
|
||||
response.errorMessage = @"内容为空";
|
||||
response.message = @"内容为空";
|
||||
completion(response);
|
||||
}
|
||||
return;
|
||||
@@ -66,24 +70,23 @@
|
||||
headers:nil
|
||||
completion:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
KBChatResponse *chatResponse = [[KBChatResponse alloc] init];
|
||||
|
||||
if (error) {
|
||||
KBChatResponse *chatResponse = [KBChatResponse mj_objectWithKeyValues:json];
|
||||
if (chatResponse.code != 0) {
|
||||
chatResponse.success = NO;
|
||||
chatResponse.errorMessage = error.localizedDescription ?: @"请求失败";
|
||||
// chatResponse.errorMessage = error.localizedDescription ?: @"请求失败";
|
||||
if (completion) completion(chatResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析文本
|
||||
chatResponse.text = [self p_parseTextFromJSON:json];
|
||||
// 解析 audioId
|
||||
chatResponse.audioId = [self p_parseAudioIdFromJSON:json];
|
||||
// // 解析文本
|
||||
// chatResponse.text = [self p_parseTextFromJSON:json];
|
||||
// // 解析 audioId
|
||||
// chatResponse.audioId = [self p_parseAudioIdFromJSON:json];
|
||||
|
||||
chatResponse.success = (chatResponse.text.length > 0);
|
||||
if (!chatResponse.success) {
|
||||
chatResponse.errorMessage = @"未获取到回复内容";
|
||||
}
|
||||
// chatResponse.success = (chatResponse.text.length > 0);
|
||||
// if (!chatResponse.success) {
|
||||
// chatResponse.errorMessage = @"未获取到回复内容";
|
||||
// }
|
||||
|
||||
if (completion) completion(chatResponse);
|
||||
});
|
||||
|
||||
@@ -318,6 +318,7 @@ static const NSUInteger kKBChatMessageLimit = 10;
|
||||
- (UILabel *)titleLabel {
|
||||
if (!_titleLabel) {
|
||||
_titleLabel = [[UILabel alloc] init];
|
||||
_titleLabel.hidden = true;
|
||||
_titleLabel.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium];
|
||||
_titleLabel.textColor =
|
||||
[UIColor kb_dynamicColorWithLightColor:[UIColor colorWithHex:0x1B1F1A]
|
||||
|
||||
@@ -49,7 +49,6 @@ static const CGFloat kKBLettersRow2EdgeSpacerMultiplier = 0.5;
|
||||
self.layoutConfig = [KBKeyboardLayoutConfig sharedConfig];
|
||||
self.backspaceHandler = [[KBBackspaceLongPressHandler alloc] initWithContainerView:self];
|
||||
[self buildBase];
|
||||
[self reloadKeys];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -594,7 +593,7 @@ edgeSpacerMultiplier:(CGFloat)edgeSpacerMultiplier {
|
||||
[btn setTitle:key.title forState:UIControlStateNormal];
|
||||
// 在设置完标题后,按当前皮肤应用图标与文字显隐
|
||||
[btn applyThemeForCurrentKey];
|
||||
[btn addTarget:self action:@selector(onKeyTapped:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[btn addTarget:self action:@selector(onKeyTapped:) forControlEvents:UIControlEventTouchDown];
|
||||
[row addSubview:btn];
|
||||
|
||||
if (key.type == KBKeyTypeBackspace) {
|
||||
@@ -920,7 +919,7 @@ edgeSpacerMultiplier:(CGFloat)edgeSpacerMultiplier {
|
||||
}
|
||||
|
||||
[btn applyThemeForCurrentKey];
|
||||
[btn addTarget:self action:@selector(onKeyTapped:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[btn addTarget:self action:@selector(onKeyTapped:) forControlEvents:UIControlEventTouchDown];
|
||||
|
||||
if (key.type == KBKeyTypeBackspace) {
|
||||
[self.backspaceHandler bindDeleteButton:btn showClearLabel:YES];
|
||||
|
||||
@@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property (nonatomic, weak, nullable) id<KBToolBarDelegate> delegate;
|
||||
|
||||
/// 左侧按钮的标题(数量由数组决定)。默认值:@[@"AI", @"语音"]。
|
||||
/// 左侧按钮的标题(数量由数组决定)。默认值:@[@"AI"]。
|
||||
@property (nonatomic, copy) NSArray<NSString *> *leftButtonTitles;
|
||||
|
||||
/// 暴露按钮以便外部定制(只读;首次访问时懒加载创建)
|
||||
|
||||
@@ -9,29 +9,34 @@
|
||||
#import "KBResponderUtils.h" // 查找 UIInputViewController,用于系统切换输入法
|
||||
#import "KBBackspaceUndoManager.h"
|
||||
#import "KBSkinManager.h"
|
||||
#import <ImageIO/ImageIO.h>
|
||||
|
||||
@interface KBToolBar ()
|
||||
@property (nonatomic, strong) UIView *leftContainer;
|
||||
@property (nonatomic, strong) NSArray<UIButton *> *leftButtonsInternal;
|
||||
//@property (nonatomic, strong) UIButton *settingsButtonInternal;
|
||||
@property (nonatomic, strong) UIButton *globeButtonInternal; // 可选:系统“切换输入法”键
|
||||
@property (nonatomic, strong) UIImageView *avatarImageView; // 右侧头像(AppGroup persona_cover.jpg)
|
||||
@property (nonatomic, strong) UIButton *undoButtonInternal; // 右侧撤销删除
|
||||
@property (nonatomic, assign) BOOL kbNeedsInputModeSwitchKey;
|
||||
@property (nonatomic, assign) BOOL kbUndoVisible;
|
||||
@property (nonatomic, assign) BOOL kbAvatarVisible;
|
||||
@property (nonatomic, copy, nullable) NSString *kb_cachedPersonaCoverPath;
|
||||
@property (nonatomic, strong, nullable) UIImage *kb_cachedPersonaCoverImage;
|
||||
@end
|
||||
|
||||
@implementation KBToolBar
|
||||
|
||||
static NSString * const kKBAIKeyIdentifier = @"ai";
|
||||
static NSString * const kKBUndoKeyIdentifier = @"key_revoke";
|
||||
static const CGFloat kKBAIButtonWidth = 40;
|
||||
static const CGFloat kKBAIButtonHeight = 40;
|
||||
static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
static NSString * const kKBAIKeyIdentifier = @"ai";
|
||||
static NSString * const kKBUndoKeyIdentifier = @"key_revoke";
|
||||
static const CGFloat kKBAIButtonWidth = 40;
|
||||
static const CGFloat kKBAIButtonHeight = 40;
|
||||
static const CGFloat kKBAvatarSize = 40;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
_leftButtonTitles = @[@"AI", KBLocalized(@"语音")]; // 默认标题
|
||||
_leftButtonTitles = @[@"AI"]; // 默认标题
|
||||
[self setupUI];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(kb_undoStateChanged)
|
||||
@@ -69,7 +74,6 @@ static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
}
|
||||
}];
|
||||
[self kb_updateAIButtonAppearance];
|
||||
[self kb_updateVoiceButtonAppearance];
|
||||
}
|
||||
|
||||
#pragma mark - 视图搭建
|
||||
@@ -79,6 +83,7 @@ static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
// [self addSubview:self.settingsButtonInternal];
|
||||
[self addSubview:self.globeButtonInternal];
|
||||
[self addSubview:self.undoButtonInternal];
|
||||
[self addSubview:self.avatarImageView];
|
||||
|
||||
// 右侧设置按钮
|
||||
// [self.settingsButtonInternal mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
@@ -94,13 +99,7 @@ static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
make.width.height.mas_equalTo(32);
|
||||
}];
|
||||
|
||||
// 右侧撤销按钮
|
||||
[self.undoButtonInternal mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self.mas_right).offset(-12);
|
||||
make.centerY.equalTo(self.mas_centerY);
|
||||
make.height.mas_equalTo(32);
|
||||
make.width.mas_equalTo(84);
|
||||
}];
|
||||
[self kb_updateRightControlsConstraints];
|
||||
|
||||
[self kb_updateLeftContainerConstraints];
|
||||
|
||||
@@ -173,8 +172,8 @@ static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
|
||||
- (void)kb_applyTheme {
|
||||
[self kb_updateAIButtonAppearance];
|
||||
[self kb_updateVoiceButtonAppearance];
|
||||
[self kb_updateUndoButtonAppearance];
|
||||
[self kb_updateAvatarAppearance];
|
||||
}
|
||||
|
||||
- (void)kb_updateAIButtonAppearance {
|
||||
@@ -211,16 +210,6 @@ static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)kb_updateVoiceButtonAppearance {
|
||||
UIButton *voiceButton = [self kb_voiceButton];
|
||||
if (!voiceButton) { return; }
|
||||
|
||||
voiceButton.backgroundColor = [UIColor colorWithHex:0xE53935];
|
||||
[voiceButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
voiceButton.layer.cornerRadius = 16;
|
||||
voiceButton.layer.masksToBounds = YES;
|
||||
}
|
||||
|
||||
- (void)kb_updateUndoButtonAppearance {
|
||||
if (!self.undoButtonInternal) { return; }
|
||||
|
||||
@@ -241,6 +230,72 @@ static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Avatar
|
||||
|
||||
- (void)kb_updateAvatarAppearance {
|
||||
UIImage *img = [self kb_personaCoverImageFromAppGroup];
|
||||
BOOL shouldShow = (img != nil);
|
||||
self.avatarImageView.image = img;
|
||||
if (self.kbAvatarVisible == shouldShow) {
|
||||
self.avatarImageView.hidden = !shouldShow;
|
||||
return;
|
||||
}
|
||||
self.kbAvatarVisible = shouldShow;
|
||||
self.avatarImageView.hidden = !shouldShow;
|
||||
[self kb_updateRightControlsConstraints];
|
||||
[self kb_updateLeftContainerConstraints];
|
||||
[self setNeedsLayout];
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (nullable UIImage *)kb_personaCoverImageFromAppGroup {
|
||||
NSURL *containerURL = [[NSFileManager defaultManager]
|
||||
containerURLForSecurityApplicationGroupIdentifier:AppGroup];
|
||||
if (!containerURL) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *imagePath =
|
||||
[[containerURL path] stringByAppendingPathComponent:@"persona_cover.jpg"];
|
||||
if (imagePath.length == 0 ||
|
||||
![[NSFileManager defaultManager] fileExistsAtPath:imagePath]) {
|
||||
self.kb_cachedPersonaCoverPath = nil;
|
||||
self.kb_cachedPersonaCoverImage = nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (self.kb_cachedPersonaCoverImage &&
|
||||
[self.kb_cachedPersonaCoverPath isEqualToString:imagePath]) {
|
||||
return self.kb_cachedPersonaCoverImage;
|
||||
}
|
||||
|
||||
// 头像仅 40pt,直接按像素上限缩略解码,避免每次显示键盘都 full decode 一张大 JPG 顶爆扩展内存。
|
||||
NSUInteger maxPixel = 256;
|
||||
NSURL *url = [NSURL fileURLWithPath:imagePath];
|
||||
CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
|
||||
if (!source) {
|
||||
return nil;
|
||||
}
|
||||
NSDictionary *opts = @{
|
||||
(__bridge id)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
|
||||
(__bridge id)kCGImageSourceCreateThumbnailWithTransform : @YES,
|
||||
(__bridge id)kCGImageSourceThumbnailMaxPixelSize : @(maxPixel),
|
||||
};
|
||||
CGImageRef cg = CGImageSourceCreateThumbnailAtIndex(source, 0, (__bridge CFDictionaryRef)opts);
|
||||
CFRelease(source);
|
||||
if (!cg) {
|
||||
return nil;
|
||||
}
|
||||
UIImage *img = [UIImage imageWithCGImage:cg
|
||||
scale:[UIScreen mainScreen].scale
|
||||
orientation:UIImageOrientationUp];
|
||||
CGImageRelease(cg);
|
||||
|
||||
self.kb_cachedPersonaCoverPath = imagePath;
|
||||
self.kb_cachedPersonaCoverImage = img;
|
||||
return img;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)onLeftAction:(UIButton *)sender {
|
||||
@@ -261,6 +316,16 @@ static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onAvatarTap {
|
||||
if (!self.kbAvatarVisible || self.avatarImageView.hidden) {
|
||||
return;
|
||||
}
|
||||
// 复用原“语音”入口的 index=1 逻辑(外部会按 index 做面板切换)
|
||||
if ([self.delegate respondsToSelector:@selector(toolBar:didTapActionAtIndex:)]) {
|
||||
[self.delegate toolBar:self didTapActionAtIndex:1];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Lazy
|
||||
|
||||
- (UIView *)leftContainer {
|
||||
@@ -271,6 +336,23 @@ static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
return _leftContainer;
|
||||
}
|
||||
|
||||
- (UIImageView *)avatarImageView {
|
||||
if (!_avatarImageView) {
|
||||
_avatarImageView = [[UIImageView alloc] init];
|
||||
_avatarImageView.hidden = YES;
|
||||
_avatarImageView.backgroundColor = [UIColor colorWithWhite:1 alpha:0.9];
|
||||
_avatarImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
_avatarImageView.layer.cornerRadius = kKBAvatarSize * 0.5;
|
||||
_avatarImageView.layer.masksToBounds = YES;
|
||||
_avatarImageView.userInteractionEnabled = YES;
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
|
||||
initWithTarget:self
|
||||
action:@selector(onAvatarTap)];
|
||||
[_avatarImageView addGestureRecognizer:tap];
|
||||
}
|
||||
return _avatarImageView;
|
||||
}
|
||||
|
||||
//- (UIButton *)settingsButtonInternal {
|
||||
// if (!_settingsButtonInternal) {
|
||||
// _settingsButtonInternal = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
@@ -319,11 +401,6 @@ static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
return self.leftButtonsInternal[0];
|
||||
}
|
||||
|
||||
- (UIButton *)kb_voiceButton {
|
||||
if (self.leftButtonsInternal.count <= kKBVoiceButtonIndex) { return nil; }
|
||||
return self.leftButtonsInternal[kKBVoiceButtonIndex];
|
||||
}
|
||||
|
||||
#pragma mark - Globe (Input Mode Switch)
|
||||
|
||||
// 根据宿主是否已提供系统切换键,决定是否显示地球按钮;并绑定系统事件。
|
||||
@@ -362,6 +439,8 @@ static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
}
|
||||
if (self.kbUndoVisible) {
|
||||
make.right.equalTo(self.undoButtonInternal.mas_left).offset(-8);
|
||||
} else if (self.kbAvatarVisible) {
|
||||
make.right.equalTo(self.avatarImageView.mas_left).offset(-8);
|
||||
} else {
|
||||
make.right.equalTo(self).offset(-12);
|
||||
}
|
||||
@@ -371,6 +450,24 @@ static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)kb_updateRightControlsConstraints {
|
||||
[self.avatarImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self).offset(-12);
|
||||
make.centerY.equalTo(self).offset(0);
|
||||
make.width.height.mas_equalTo(kKBAvatarSize);
|
||||
}];
|
||||
[self.undoButtonInternal mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
if (self.kbAvatarVisible) {
|
||||
make.right.equalTo(self.avatarImageView.mas_left).offset(-8);
|
||||
} else {
|
||||
make.right.equalTo(self).offset(-12);
|
||||
}
|
||||
make.centerY.equalTo(self.mas_centerY);
|
||||
make.height.mas_equalTo(32);
|
||||
make.width.mas_equalTo(84);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)kb_undoStateChanged {
|
||||
[self kb_updateUndoVisibilityAnimated:YES];
|
||||
}
|
||||
@@ -405,6 +502,7 @@ static const NSInteger kKBVoiceButtonIndex = 1;
|
||||
- (void)didMoveToWindow {
|
||||
[super didMoveToWindow];
|
||||
[self kb_refreshGlobeVisibility];
|
||||
[self kb_updateAvatarAppearance];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
Feb 5 20:30:09 macbookpro com.apple.dt.xcodebuild[56551] <Error>: Unable to deliver request ({
|
||||
"developer_dir" = "/Applications/Xcode.app/Contents/Developer";
|
||||
request = "set_developer_dir";
|
||||
}) because we are not connected to CoreSimulatorService.
|
||||
Feb 5 20:30:22 macbookpro com.apple.dt.xcodebuild[56567] <Error>: Unable to deliver request ({
|
||||
"developer_dir" = "/Applications/Xcode.app/Contents/Developer";
|
||||
request = "set_developer_dir";
|
||||
}) because we are not connected to CoreSimulatorService.
|
||||
Feb 5 20:31:06 macbookpro com.apple.dt.xcodebuild[56684] <Error>: Unable to deliver request ({
|
||||
"developer_dir" = "/Applications/Xcode.app/Contents/Developer";
|
||||
request = "set_developer_dir";
|
||||
}) because we are not connected to CoreSimulatorService.
|
||||
Feb 5 20:31:06 macbookpro com.apple.dt.xcodebuild[56684] <Warning>: Unable to discover any Simulator runtimes. Developer Directory is /Applications/Xcode.app/Contents/Developer.
|
||||
Feb 5 20:31:06 macbookpro com.apple.dt.xcodebuild[56684] <Error>: Could not kickstart simdiskimaged; SimDiskImageManager services will not be available: Error Domain=NSPOSIXErrorDomain Code=53 "Software caused connection abort" UserInfo={NSLocalizedDescription=Error returned in reply from CoreSimulatorService: Connection invalid}
|
||||
Feb 5 20:31:06 macbookpro com.apple.dt.xcodebuild[56684] <Error>: simdiskimaged returned error (invalid), marking disconnected.
|
||||
Feb 5 20:31:06 macbookpro com.apple.dt.xcodebuild[56684] <Error>: Could not get list of trusted mount directories: Error Domain=com.apple.CoreSimulator.SimError Code=410 "The service used to manage runtime disk images (simdiskimaged) crashed or is not responding" UserInfo={NSLocalizedDescription=The service used to manage runtime disk images (simdiskimaged) crashed or is not responding}
|
||||
Feb 5 20:31:06 macbookpro com.apple.dt.xcodebuild[56684] <Error>: simdiskimaged returned error (invalid), marking disconnected.
|
||||
Feb 5 20:31:06 macbookpro com.apple.dt.xcodebuild[56684] <Error>: Unable to deliver request ({
|
||||
request = "notification_subscription";
|
||||
"set_path" = "/Users/mac/Library/Developer/CoreSimulator/Devices";
|
||||
}) because we are not connected to CoreSimulatorService.
|
||||
Feb 5 20:31:06 macbookpro com.apple.dt.xcodebuild[56684] <Error>: Unable to deliver request ({
|
||||
request = "notification_subscription";
|
||||
"set_path" = "/Users/mac/Library/Developer/CoreSimulator/Devices";
|
||||
}) because we are not connected to CoreSimulatorService.
|
||||
Feb 5 20:31:55 macbookpro com.apple.dt.xcodebuild[56806] <Error>: Unable to deliver request ({
|
||||
"developer_dir" = "/Applications/Xcode.app/Contents/Developer";
|
||||
request = "set_developer_dir";
|
||||
}) because we are not connected to CoreSimulatorService.
|
||||
Feb 5 20:31:55 macbookpro com.apple.dt.xcodebuild[56806] <Warning>: Unable to discover any Simulator runtimes. Developer Directory is /Applications/Xcode.app/Contents/Developer.
|
||||
Feb 5 20:31:55 macbookpro com.apple.dt.xcodebuild[56806] <Error>: Could not kickstart simdiskimaged; SimDiskImageManager services will not be available: Error Domain=NSPOSIXErrorDomain Code=53 "Software caused connection abort" UserInfo={NSLocalizedDescription=Error returned in reply from CoreSimulatorService: Connection invalid}
|
||||
Feb 5 20:31:55 macbookpro com.apple.dt.xcodebuild[56806] <Error>: simdiskimaged returned error (invalid), marking disconnected.
|
||||
Feb 5 20:31:55 macbookpro com.apple.dt.xcodebuild[56806] <Error>: Could not get list of trusted mount directories: Error Domain=com.apple.CoreSimulator.SimError Code=410 "The service used to manage runtime disk images (simdiskimaged) crashed or is not responding" UserInfo={NSLocalizedDescription=The service used to manage runtime disk images (simdiskimaged) crashed or is not responding}
|
||||
Feb 5 20:31:55 macbookpro com.apple.dt.xcodebuild[56806] <Error>: simdiskimaged returned error (invalid), marking disconnected.
|
||||
Feb 5 20:31:55 macbookpro com.apple.dt.xcodebuild[56806] <Error>: Unable to deliver request ({
|
||||
request = "notification_subscription";
|
||||
"set_path" = "/Users/mac/Library/Developer/CoreSimulator/Devices";
|
||||
}) because we are not connected to CoreSimulatorService.
|
||||
Feb 5 20:31:55 macbookpro com.apple.dt.xcodebuild[56806] <Error>: Unable to deliver request ({
|
||||
request = "notification_subscription";
|
||||
"set_path" = "/Users/mac/Library/Developer/CoreSimulator/Devices";
|
||||
}) because we are not connected to CoreSimulatorService.
|
||||
@@ -20,6 +20,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
+ (NSString *)signWithParams:(NSDictionary<NSString *, NSString *> *)params
|
||||
secret:(NSString *)secret;
|
||||
|
||||
/// 获取签名原始拼接字符串(HMAC 前的明文)
|
||||
+ (NSString *)signSourceStringWithParams:(NSDictionary<NSString *, NSString *> *)params
|
||||
secret:(NSString *)secret;
|
||||
|
||||
/// 秒级时间戳(字符串)
|
||||
+ (NSString *)currentTimestamp;
|
||||
|
||||
@@ -29,4 +33,3 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
|
||||
@@ -12,10 +12,16 @@
|
||||
|
||||
+ (NSString *)urlEncode:(NSString *)value {
|
||||
if (!value) return @"";
|
||||
// 和 Swift 里的 .urlQueryAllowed 类似
|
||||
NSCharacterSet *allowed = [NSCharacterSet URLQueryAllowedCharacterSet];
|
||||
// 按 application/x-www-form-urlencoded 规则编码(更贴近后端常见实现)
|
||||
NSMutableCharacterSet *allowed = [NSMutableCharacterSet alphanumericCharacterSet];
|
||||
[allowed addCharactersInString:@"-._*"];
|
||||
NSString *encoded = [value stringByAddingPercentEncodingWithAllowedCharacters:allowed];
|
||||
return encoded ?: value;
|
||||
if (!encoded) {
|
||||
return value;
|
||||
}
|
||||
// 空格改为 +(URLEncoder 风格)
|
||||
encoded = [encoded stringByReplacingOccurrencesOfString:@"%20" withString:@"+"];
|
||||
return encoded;
|
||||
}
|
||||
|
||||
+ (NSString *)hmacSHA256:(NSString *)data secret:(NSString *)secret {
|
||||
@@ -41,6 +47,12 @@
|
||||
|
||||
+ (NSString *)signWithParams:(NSDictionary<NSString *, NSString *> *)params
|
||||
secret:(NSString *)secret {
|
||||
NSString *dataString = [self signSourceStringWithParams:params secret:secret];
|
||||
return [self hmacSHA256:dataString secret:secret];
|
||||
}
|
||||
|
||||
+ (NSString *)signSourceStringWithParams:(NSDictionary<NSString *, NSString *> *)params
|
||||
secret:(NSString *)secret {
|
||||
|
||||
// 1. 过滤空值 & sign 自身
|
||||
NSMutableDictionary<NSString *, NSString *> *filtered = [NSMutableDictionary dictionary];
|
||||
@@ -62,15 +74,11 @@
|
||||
[components addObject:part];
|
||||
}
|
||||
|
||||
NSString *encodedSecret = [self urlEncode:secret];
|
||||
NSString *encodedSecret = [self urlEncode:secret ?: @""];
|
||||
NSString *secretPart = [NSString stringWithFormat:@"secret=%@", encodedSecret];
|
||||
[components addObject:secretPart];
|
||||
|
||||
NSString *dataString = [components componentsJoinedByString:@"&"];
|
||||
|
||||
// 4. HMAC-SHA256
|
||||
NSString *sign = [self hmacSHA256:dataString secret:secret];
|
||||
return sign;
|
||||
return [components componentsJoinedByString:@"&"];
|
||||
}
|
||||
|
||||
+ (NSString *)currentTimestamp {
|
||||
@@ -89,4 +97,3 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -54,6 +54,9 @@ extern NSString * const KBDarwinSkinChanged; // cross-process
|
||||
/// 当前背景图片(若存在)
|
||||
- (nullable UIImage *)currentBackgroundImage;
|
||||
|
||||
/// 清理运行时图片缓存(内存缓存)。键盘扩展接近内存上限时可主动调用。
|
||||
- (void)clearRuntimeImageCaches;
|
||||
|
||||
/// 当前主题下,指定按键标识的文字是否应被隐藏(例如图标里已包含字母)
|
||||
- (BOOL)shouldHideKeyTextForIdentifier:(nullable NSString *)identifier;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#import "KBSkinManager.h"
|
||||
#import "KBConfig.h"
|
||||
#import <ImageIO/ImageIO.h>
|
||||
|
||||
NSString * const KBSkinDidChangeNotification = @"KBSkinDidChangeNotification";
|
||||
NSString * const KBDarwinSkinChanged = @"com.loveKey.nyx.skin.changed";
|
||||
@@ -59,10 +60,45 @@ static NSString * const kKBSkinThemeStoreKey = @"KBSkinThemeCurrent";
|
||||
|
||||
@interface KBSkinManager ()
|
||||
@property (atomic, strong, readwrite) KBSkinTheme *current;
|
||||
@property (nonatomic, strong) NSCache<NSString *, UIImage *> *kb_fileImageCache;
|
||||
@property (nonatomic, copy, nullable) NSString *kb_cachedBgSkinId;
|
||||
@property (nonatomic, assign) BOOL kb_cachedBgResolved;
|
||||
@property (nonatomic, strong, nullable) UIImage *kb_cachedBgImage;
|
||||
@end
|
||||
|
||||
@implementation KBSkinManager
|
||||
|
||||
/// 从文件路径解码图片,并按 maxPixel 限制最长边像素(避免加载超大背景图导致键盘扩展内存飙升)。
|
||||
+ (nullable UIImage *)kb_imageAtPath:(NSString *)path maxPixel:(NSUInteger)maxPixel {
|
||||
if (path.length == 0) return nil;
|
||||
NSURL *url = [NSURL fileURLWithPath:path];
|
||||
CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
|
||||
if (!source) return nil;
|
||||
NSDictionary *opts = @{
|
||||
(__bridge id)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
|
||||
(__bridge id)kCGImageSourceCreateThumbnailWithTransform : @YES,
|
||||
(__bridge id)kCGImageSourceThumbnailMaxPixelSize : @(MAX(1, (NSInteger)maxPixel)),
|
||||
};
|
||||
CGImageRef cg = CGImageSourceCreateThumbnailAtIndex(source, 0, (__bridge CFDictionaryRef)opts);
|
||||
CFRelease(source);
|
||||
if (!cg) return nil;
|
||||
UIImage *img = [UIImage imageWithCGImage:cg scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp];
|
||||
CGImageRelease(cg);
|
||||
return img;
|
||||
}
|
||||
|
||||
static inline NSUInteger KBApproxImageCostBytes(UIImage *img) {
|
||||
if (!img) return 0;
|
||||
CGFloat scale = img.scale > 0 ? img.scale : [UIScreen mainScreen].scale;
|
||||
CGSize s = img.size;
|
||||
double px = (double)s.width * scale * (double)s.height * scale;
|
||||
if (px <= 0) return 0;
|
||||
// RGBA 4 bytes/pixel
|
||||
double cost = px * 4.0;
|
||||
if (cost > (double)NSUIntegerMax) return NSUIntegerMax;
|
||||
return (NSUInteger)cost;
|
||||
}
|
||||
|
||||
/// 返回所有可能的皮肤根目录(优先 App Group,其次当前进程的 Caches)。
|
||||
+ (NSArray<NSString *> *)kb_candidateBaseRoots {
|
||||
NSMutableArray<NSString *> *roots = [NSMutableArray array];
|
||||
@@ -104,6 +140,14 @@ static NSString * const kKBSkinThemeStoreKey = @"KBSkinThemeCurrent";
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_kb_fileImageCache = [NSCache new];
|
||||
// 键盘扩展内存上限较小,缓存要保守一些;主 App 也共用该实现但不会出问题。
|
||||
// iPad 的键盘背景可能更大,适当放宽。
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
||||
_kb_fileImageCache.totalCostLimit = 24 * 1024 * 1024;
|
||||
} else {
|
||||
_kb_fileImageCache.totalCostLimit = 12 * 1024 * 1024;
|
||||
}
|
||||
KBSkinTheme *t = [self p_loadFromStore];
|
||||
// 若存储中的皮肤在 App Group 中找不到对应资源目录(如首次安装 / 已被清理),则回退到默认皮肤。
|
||||
if (!t || ![self.class kb_hasAssetsForSkinId:t.skinId]) {
|
||||
@@ -170,6 +214,7 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
|
||||
- (BOOL)applyTheme:(KBSkinTheme *)theme {
|
||||
if (!theme) return NO;
|
||||
NSLog(@"🎨[SkinManager] apply theme id=%@ name=%@", theme.skinId, theme.name);
|
||||
[self clearRuntimeImageCaches];
|
||||
// 将主题写入 App Group 存储(失败也不影响本次进程内的使用)
|
||||
[self p_saveToStore:theme];
|
||||
// 始终更新当前主题并广播通知,确保当前进程和扩展之间保持同步。
|
||||
@@ -187,6 +232,15 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
|
||||
[self applyTheme:[self.class defaultTheme]];
|
||||
}
|
||||
|
||||
- (void)clearRuntimeImageCaches {
|
||||
@synchronized (self) {
|
||||
[self.kb_fileImageCache removeAllObjects];
|
||||
self.kb_cachedBgSkinId = nil;
|
||||
self.kb_cachedBgResolved = NO;
|
||||
self.kb_cachedBgImage = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)applyImageSkinWithData:(NSData *)imageData skinId:(NSString *)skinId name:(NSString *)name {
|
||||
// 仅作为“存在背景图”的标记使用:图像文件本身存放在 App Group 容器
|
||||
// Skins/<skinId>/background.png 中,这里不再把二进制图片写入 Keychain,
|
||||
@@ -216,20 +270,52 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
|
||||
NSString *skinId = self.current.skinId;
|
||||
if (skinId.length == 0) return nil;
|
||||
|
||||
// 同一个 skinId 在键盘的生命周期内会被频繁读取;缓存一份避免反复解码导致内存上涨。
|
||||
@synchronized (self) {
|
||||
if (self.kb_cachedBgResolved && [self.kb_cachedBgSkinId isEqualToString:skinId]) {
|
||||
return self.kb_cachedBgImage;
|
||||
}
|
||||
}
|
||||
|
||||
NSArray<NSString *> *roots = [self.class kb_candidateBaseRoots];
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSString *relative = [NSString stringWithFormat:@"Skins/%@/background.png", skinId];
|
||||
|
||||
// 背景图通常远大于键盘实际显示区域,按像素上限做缩略解码,显著降低扩展内存占用。
|
||||
NSUInteger maxPixel = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ? 2048 : 1024;
|
||||
for (NSString *base in roots) {
|
||||
NSString *bgPath = [[base stringByAppendingPathComponent:relative] stringByStandardizingPath];
|
||||
BOOL isDir = NO;
|
||||
if (![fm fileExistsAtPath:bgPath isDirectory:&isDir] || isDir) {
|
||||
continue;
|
||||
}
|
||||
NSData *data = [NSData dataWithContentsOfFile:bgPath];
|
||||
if (data.length == 0) continue;
|
||||
UIImage *img = [UIImage imageWithData:data scale:[UIScreen mainScreen].scale];
|
||||
if (img) return img;
|
||||
NSString *cacheKey = [NSString stringWithFormat:@"bg|%@", bgPath];
|
||||
UIImage *cached = [self.kb_fileImageCache objectForKey:cacheKey];
|
||||
if (cached) {
|
||||
@synchronized (self) {
|
||||
self.kb_cachedBgSkinId = skinId;
|
||||
self.kb_cachedBgResolved = YES;
|
||||
self.kb_cachedBgImage = cached;
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
UIImage *img = [self.class kb_imageAtPath:bgPath maxPixel:maxPixel];
|
||||
if (img) {
|
||||
NSUInteger cost = KBApproxImageCostBytes(img);
|
||||
[self.kb_fileImageCache setObject:img forKey:cacheKey cost:cost];
|
||||
@synchronized (self) {
|
||||
self.kb_cachedBgSkinId = skinId;
|
||||
self.kb_cachedBgResolved = YES;
|
||||
self.kb_cachedBgImage = img;
|
||||
}
|
||||
return img;
|
||||
}
|
||||
}
|
||||
@synchronized (self) {
|
||||
self.kb_cachedBgSkinId = skinId;
|
||||
self.kb_cachedBgResolved = YES;
|
||||
self.kb_cachedBgImage = nil;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@@ -314,7 +400,13 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
|
||||
if (![fm fileExistsAtPath:fullPath isDirectory:&isDir] || isDir) {
|
||||
continue;
|
||||
}
|
||||
UIImage *img = [UIImage imageWithContentsOfFile:fullPath];
|
||||
NSString *cacheKey = [NSString stringWithFormat:@"icon|%@", fullPath];
|
||||
UIImage *img = [self.kb_fileImageCache objectForKey:cacheKey];
|
||||
if (img) return img;
|
||||
img = [UIImage imageWithContentsOfFile:fullPath];
|
||||
if (img) {
|
||||
[self.kb_fileImageCache setObject:img forKey:cacheKey cost:KBApproxImageCostBytes(img)];
|
||||
}
|
||||
if (img) return img;
|
||||
}
|
||||
#if DEBUG
|
||||
@@ -351,7 +443,13 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
|
||||
NSString *fullPath = [[base stringByAppendingPathComponent:relative] stringByStandardizingPath];
|
||||
BOOL isDir = NO;
|
||||
if ([fm fileExistsAtPath:fullPath isDirectory:&isDir] && !isDir) {
|
||||
UIImage *img = [UIImage imageWithContentsOfFile:fullPath];
|
||||
NSString *cacheKey = [NSString stringWithFormat:@"icon|%@", fullPath];
|
||||
UIImage *img = [self.kb_fileImageCache objectForKey:cacheKey];
|
||||
if (img) return img;
|
||||
img = [UIImage imageWithContentsOfFile:fullPath];
|
||||
if (img) {
|
||||
[self.kb_fileImageCache setObject:img forKey:cacheKey cost:KBApproxImageCostBytes(img)];
|
||||
}
|
||||
if (img) return img;
|
||||
}
|
||||
}
|
||||
@@ -363,7 +461,13 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
|
||||
NSString *fullPath = [[base stringByAppendingPathComponent:relative] stringByStandardizingPath];
|
||||
BOOL isDir = NO;
|
||||
if ([fm fileExistsAtPath:fullPath isDirectory:&isDir] && !isDir) {
|
||||
UIImage *img = [UIImage imageWithContentsOfFile:fullPath];
|
||||
NSString *cacheKey = [NSString stringWithFormat:@"icon|%@", fullPath];
|
||||
UIImage *img = [self.kb_fileImageCache objectForKey:cacheKey];
|
||||
if (img) return img;
|
||||
img = [UIImage imageWithContentsOfFile:fullPath];
|
||||
if (img) {
|
||||
[self.kb_fileImageCache setObject:img forKey:cacheKey cost:KBApproxImageCostBytes(img)];
|
||||
}
|
||||
if (img) return img;
|
||||
}
|
||||
}
|
||||
@@ -449,6 +553,7 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
|
||||
if (!t || ![self.class kb_hasAssetsForSkinId:t.skinId]) {
|
||||
t = [self.class defaultTheme];
|
||||
}
|
||||
[self clearRuntimeImageCaches];
|
||||
self.current = t;
|
||||
if (broadcast) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:KBSkinDidChangeNotification object:nil];
|
||||
|
||||
@@ -140,6 +140,10 @@
|
||||
"Nickname" = "Nickname";
|
||||
"Gender" = "Gender";
|
||||
"User ID" = "User ID";
|
||||
"Modify Gender" = "Modify Gender";
|
||||
"Male" = "Male";
|
||||
"Female" = "Female";
|
||||
"The Third Gender" = "The Third Gender";
|
||||
|
||||
" Paste Ta's Words" = " Paste Ta's Words";
|
||||
|
||||
@@ -170,6 +174,10 @@
|
||||
"Ranking List" = "Ranking List";
|
||||
"Persona circle" = "Persona circle";
|
||||
"Clear" = "Clear";
|
||||
"Copy" = "Copy";
|
||||
"Report" = "Report报";
|
||||
"Thumbs Up" = "Thumbs Up";
|
||||
"Chatting" = "Chatting";
|
||||
|
||||
// Payment & IAP
|
||||
"Payment successful" = "Payment successful";
|
||||
@@ -188,10 +196,6 @@
|
||||
"测试" = "Test";
|
||||
"这里是设置内容占位" = "Settings content placeholder";
|
||||
"设置" = "Settings";
|
||||
"使用引导" = "Usage Guide";
|
||||
"螃蟹啊斯柯达积分卡" = "Crab points card";
|
||||
"❎不是自己的键盘" = "❎ Not our keyboard";
|
||||
"是自己的键盘" = "Our keyboard";
|
||||
|
||||
// English-only keys with Chinese equivalents
|
||||
"Change The Nickname" = "Change Nickname";
|
||||
|
||||
@@ -103,7 +103,6 @@
|
||||
"Please check this app's wireless-data permission or network connection in Settings." = "请在“设置”中检查本应用的无线数据权限或网络连接。";
|
||||
|
||||
// 权限与引导(英文 key)
|
||||
"Usage Guide" = "使用引导";
|
||||
"Turn on Allow Full Access to experience all features" = "开启【允许完全访问】,体验完整功能";
|
||||
"Allow Full Access" = "允许完全访问";
|
||||
"Follow: Settings → General → Keyboard → Keyboards → %@ → Allow Full Access" = "请按路径:设置→通用→键盘→键盘→%@→允许完全访问";
|
||||
@@ -142,6 +141,10 @@
|
||||
"Nickname" = "用户名";
|
||||
"Gender" = "性别";
|
||||
"User ID" = "用户ID";
|
||||
"Modify Gender" = "修改性别";
|
||||
"Male" = "男";
|
||||
"Female" = "女";
|
||||
"The Third Gender" = "第三性别";
|
||||
|
||||
" Paste Ta's Words" = " 粘贴TA的话";
|
||||
|
||||
@@ -171,6 +174,10 @@
|
||||
"Ranking List" = "排行榜";
|
||||
"Persona circle" = "圈子";
|
||||
"Clear" = "立刻清空";
|
||||
"Copy" = "复制";
|
||||
"Report" = "举报";
|
||||
"Thumbs Up" = "赞过";
|
||||
"Chatting" = "聊过";
|
||||
|
||||
// 皮肤示例名称
|
||||
|
||||
@@ -191,10 +198,6 @@
|
||||
"Test" = "测试";
|
||||
"暂无数据" = "暂无数据"; // 已有英文 key "No data"
|
||||
"Settings" = "设置";
|
||||
"Usage Guide" = "使用引导";
|
||||
"螃蟹啊斯柯达积分卡" = "螃蟹啊斯柯达积分卡";
|
||||
"❎不是自己的键盘" = "❎不是自己的键盘";
|
||||
"是自己的键盘" = "是自己的键盘";
|
||||
|
||||
|
||||
"Change The Nickname" = "修改名称";
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<integer>11</integer>
|
||||
<key>logs</key>
|
||||
<dict>
|
||||
<key>800731DD-5595-43EC-B207-003BAB7870CE</key>
|
||||
<key>8E255990-30E2-4820-98AE-0559FF9CB504</key>
|
||||
<dict>
|
||||
<key>className</key>
|
||||
<string>IDECommandLineBuildLog</string>
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>domainType</key>
|
||||
<string>Xcode.IDEActivityLogDomainType.BuildLog</string>
|
||||
<key>fileName</key>
|
||||
<string>800731DD-5595-43EC-B207-003BAB7870CE.xcactivitylog</string>
|
||||
<string>8E255990-30E2-4820-98AE-0559FF9CB504.xcactivitylog</string>
|
||||
<key>hasPrimaryLog</key>
|
||||
<true/>
|
||||
<key>primaryObservable</key>
|
||||
@@ -34,13 +34,49 @@
|
||||
<key>signature</key>
|
||||
<string>Resolve Packages</string>
|
||||
<key>timeStartedRecording</key>
|
||||
<real>788359220.39837599</real>
|
||||
<real>791987515.57282996</real>
|
||||
<key>timeStoppedRecording</key>
|
||||
<real>788359220.51885402</real>
|
||||
<real>791987515.86043</real>
|
||||
<key>title</key>
|
||||
<string>Resolve Packages</string>
|
||||
<key>uniqueIdentifier</key>
|
||||
<string>800731DD-5595-43EC-B207-003BAB7870CE</string>
|
||||
<string>8E255990-30E2-4820-98AE-0559FF9CB504</string>
|
||||
</dict>
|
||||
<key>C9B2536F-0143-4FD7-90C7-C8F37E9F59D6</key>
|
||||
<dict>
|
||||
<key>className</key>
|
||||
<string>IDECommandLineBuildLog</string>
|
||||
<key>documentTypeString</key>
|
||||
<string><nil></string>
|
||||
<key>domainType</key>
|
||||
<string>Xcode.IDEActivityLogDomainType.BuildLog</string>
|
||||
<key>fileName</key>
|
||||
<string>C9B2536F-0143-4FD7-90C7-C8F37E9F59D6.xcactivitylog</string>
|
||||
<key>hasPrimaryLog</key>
|
||||
<true/>
|
||||
<key>primaryObservable</key>
|
||||
<dict>
|
||||
<key>highLevelStatus</key>
|
||||
<string>E</string>
|
||||
<key>totalNumberOfAnalyzerIssues</key>
|
||||
<integer>0</integer>
|
||||
<key>totalNumberOfErrors</key>
|
||||
<integer>1</integer>
|
||||
<key>totalNumberOfTestFailures</key>
|
||||
<integer>0</integer>
|
||||
<key>totalNumberOfWarnings</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
<key>signature</key>
|
||||
<string>Resolve Packages</string>
|
||||
<key>timeStartedRecording</key>
|
||||
<real>791987467.25816</real>
|
||||
<key>timeStoppedRecording</key>
|
||||
<real>791987467.36928797</real>
|
||||
<key>title</key>
|
||||
<string>Resolve Packages</string>
|
||||
<key>uniqueIdentifier</key>
|
||||
<string>C9B2536F-0143-4FD7-90C7-C8F37E9F59D6</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
||||
@@ -54,20 +54,11 @@
|
||||
0459D1B72EBA287900F2D189 /* KBSkinManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B62EBA287900F2D189 /* KBSkinManager.m */; };
|
||||
0459D1B82EBA287900F2D189 /* KBSkinManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B62EBA287900F2D189 /* KBSkinManager.m */; };
|
||||
0460866B2F18D75500757C95 /* ai_test.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 0460866A2F18D75500757C95 /* ai_test.m4a */; };
|
||||
046086732F191B6900757C95 /* KBAiMainVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086722F191B6900757C95 /* KBAiMainVC.m */; };
|
||||
046086752F191CC700757C95 /* AI技术分析.txt in Resources */ = {isa = PBXBuildFile; fileRef = 046086742F191CC700757C95 /* AI技术分析.txt */; };
|
||||
0460869A2F19238500757C95 /* KBAiWaveformView.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086992F19238500757C95 /* KBAiWaveformView.m */; };
|
||||
0460869C2F19238500757C95 /* KBAiRecordButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086972F19238500757C95 /* KBAiRecordButton.m */; };
|
||||
046086B12F19239B00757C95 /* SubtitleSync.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086AC2F19239B00757C95 /* SubtitleSync.m */; };
|
||||
046086B22F19239B00757C95 /* TTSServiceClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086B02F19239B00757C95 /* TTSServiceClient.m */; };
|
||||
046086B32F19239B00757C95 /* AudioSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086A22F19239B00757C95 /* AudioSessionManager.m */; };
|
||||
046086B42F19239B00757C95 /* LLMStreamClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086A82F19239B00757C95 /* LLMStreamClient.m */; };
|
||||
046086B52F19239B00757C95 /* Segmenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086AA2F19239B00757C95 /* Segmenter.m */; };
|
||||
046086B62F19239B00757C95 /* TTSPlaybackPipeline.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086AE2F19239B00757C95 /* TTSPlaybackPipeline.m */; };
|
||||
046086B72F19239B00757C95 /* ConversationOrchestrator.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086A62F19239B00757C95 /* ConversationOrchestrator.m */; };
|
||||
046086B82F19239B00757C95 /* ASRStreamClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0460869E2F19239B00757C95 /* ASRStreamClient.m */; };
|
||||
046086B92F19239B00757C95 /* AudioCaptureManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086A02F19239B00757C95 /* AudioCaptureManager.m */; };
|
||||
046086BA2F19239B00757C95 /* AudioStreamPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086A42F19239B00757C95 /* AudioStreamPlayer.m */; };
|
||||
046086BD2F1A039F00757C95 /* KBAICommentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086BC2F1A039F00757C95 /* KBAICommentView.m */; };
|
||||
046086CB2F1A092500757C95 /* comments_mock.json in Resources */ = {isa = PBXBuildFile; fileRef = 046086C62F1A092500757C95 /* comments_mock.json */; };
|
||||
046086CC2F1A092500757C95 /* KBAIReplyModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086CA2F1A092500757C95 /* KBAIReplyModel.m */; };
|
||||
@@ -79,7 +70,6 @@
|
||||
046131142ECF454500A6FADF /* KBKeyPreviewView.m in Sources */ = {isa = PBXBuildFile; fileRef = 046131132ECF454500A6FADF /* KBKeyPreviewView.m */; };
|
||||
0477BDF02EBB76E30055D639 /* HomeSheetVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDEF2EBB76E30055D639 /* HomeSheetVC.m */; };
|
||||
0477BDF32EBB7B850055D639 /* KBDirectionIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDF22EBB7B850055D639 /* KBDirectionIndicatorView.m */; };
|
||||
0477BDF72EBC63A80055D639 /* KBTestVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDF62EBC63A80055D639 /* KBTestVC.m */; };
|
||||
0477BDFA2EBC66340055D639 /* HomeHeadView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDF92EBC66340055D639 /* HomeHeadView.m */; };
|
||||
0477BDFD2EBC6A170055D639 /* HomeHotVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDFC2EBC6A170055D639 /* HomeHotVC.m */; };
|
||||
0477BE002EBC6A330055D639 /* HomeRankVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDFF2EBC6A330055D639 /* HomeRankVC.m */; };
|
||||
@@ -150,7 +140,6 @@
|
||||
048FFD302F29F3C3005D62AE /* KBAIMessageZanVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD2F2F29F3C3005D62AE /* KBAIMessageZanVC.m */; };
|
||||
048FFD332F29F3D2005D62AE /* KBAIMessageChatingVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD322F29F3D2005D62AE /* KBAIMessageChatingVC.m */; };
|
||||
048FFD342F29F400005D62AE /* KBAIMessageListVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD362F29F400005D62AE /* KBAIMessageListVC.m */; };
|
||||
048FFD362F29F88E005D62AE /* AIMessageVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD352F29F88E005D62AE /* AIMessageVM.m */; };
|
||||
048FFD372F29F410005D62AE /* KBAIMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD392F29F410005D62AE /* KBAIMessageCell.m */; };
|
||||
048FFD392F2A24C5005D62AE /* KBAIChatMessageCacheManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD382F2A24C5005D62AE /* KBAIChatMessageCacheManager.m */; };
|
||||
048FFD3C2F29F500005D62AE /* KBLikedCompanionModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD3B2F29F500005D62AE /* KBLikedCompanionModel.m */; };
|
||||
@@ -209,6 +198,9 @@
|
||||
04A9FE202EB893F10020DB6D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 04A9FE1E2EB893F10020DB6D /* Localizable.strings */; };
|
||||
04A9FE212EB893F10020DB6D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 04A9FE1E2EB893F10020DB6D /* Localizable.strings */; };
|
||||
04B5A1A22EEFA12300AAAAAA /* KBPayProductModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 04B5A1A12EEFA12300AAAAAA /* KBPayProductModel.m */; };
|
||||
04BBF89D2F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04BBF89A2F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.m */; };
|
||||
04BBF89E2F3ACD8800B1FBB2 /* KBTestVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04BBF89C2F3ACD8800B1FBB2 /* KBTestVC.m */; };
|
||||
04BBF9002F3C97CB00B1FBB2 /* DeepgramWebSocketClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 04BBF8FF2F3C97CB00B1FBB2 /* DeepgramWebSocketClient.m */; };
|
||||
04C6EABA2EAF86530089C901 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 04C6EAAE2EAF86530089C901 /* Assets.xcassets */; };
|
||||
04C6EABC2EAF86530089C901 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 04C6EAB12EAF86530089C901 /* LaunchScreen.storyboard */; };
|
||||
04C6EABD2EAF86530089C901 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 04C6EAB42EAF86530089C901 /* Main.storyboard */; };
|
||||
@@ -222,10 +214,7 @@
|
||||
04D1F6B32EDFF10A00B12345 /* KBSkinInstallBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 04D1F6B12EDFF10A00B12345 /* KBSkinInstallBridge.m */; };
|
||||
04E0383E2F1A7C30002CA5A0 /* KBCustomTabBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E0383D2F1A7C30002CA5A0 /* KBCustomTabBar.m */; };
|
||||
04E038D82F20BFFB002CA5A0 /* websocket-api.md in Resources */ = {isa = PBXBuildFile; fileRef = 04E038D72F20BFFB002CA5A0 /* websocket-api.md */; };
|
||||
04E038DD2F20C420002CA5A0 /* VoiceChatStreamingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E038DA2F20C420002CA5A0 /* VoiceChatStreamingManager.m */; };
|
||||
04E038DE2F20C420002CA5A0 /* VoiceChatWebSocketClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E038DC2F20C420002CA5A0 /* VoiceChatWebSocketClient.m */; };
|
||||
04E038E32F20E500002CA5A0 /* deepgramAPI.md in Resources */ = {isa = PBXBuildFile; fileRef = 04E038E22F20E500002CA5A0 /* deepgramAPI.md */; };
|
||||
04E038E82F20E877002CA5A0 /* DeepgramWebSocketClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E038E72F20E877002CA5A0 /* DeepgramWebSocketClient.m */; };
|
||||
04E038E92F20E877002CA5A0 /* DeepgramStreamingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E038E52F20E877002CA5A0 /* DeepgramStreamingManager.m */; };
|
||||
04E038EF2F21F0EC002CA5A0 /* AiVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E038EE2F21F0EC002CA5A0 /* AiVM.m */; };
|
||||
04E0394B2F236E75002CA5A0 /* KBChatUserMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E0394A2F236E75002CA5A0 /* KBChatUserMessageCell.m */; };
|
||||
@@ -238,6 +227,14 @@
|
||||
04E0B2022F300002002CA5A0 /* KBVoiceRecordManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E0B2012F300002002CA5A0 /* KBVoiceRecordManager.m */; };
|
||||
04E161832F10E6470022C23B /* normal_hei_them.zip in Resources */ = {isa = PBXBuildFile; fileRef = 04E161812F10E6470022C23B /* normal_hei_them.zip */; };
|
||||
04E161842F10E6470022C23B /* normal_them.zip in Resources */ = {isa = PBXBuildFile; fileRef = 04E161822F10E6470022C23B /* normal_them.zip */; };
|
||||
04F4C0AA2F32274000E8F08C /* KBPayMainVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0A92F32274000E8F08C /* KBPayMainVC.m */; };
|
||||
04F4C0AD2F32288600E8F08C /* KBPaySvipVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0AC2F32288600E8F08C /* KBPaySvipVC.m */; };
|
||||
04F4C0B02F322EF200E8F08C /* PagingViewTableHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0AF2F322EF200E8F08C /* PagingViewTableHeaderView.m */; };
|
||||
04F4C0B52F33053800E8F08C /* KBSvipBenefitCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0B22F33053800E8F08C /* KBSvipBenefitCell.m */; };
|
||||
04F4C0B62F33053800E8F08C /* KBSvipSubscribeCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0B42F33053800E8F08C /* KBSvipSubscribeCell.m */; };
|
||||
04F4C0BB2F3306CF00E8F08C /* KBSvipFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0BA2F3306CF00E8F08C /* KBSvipFlowLayout.m */; };
|
||||
04F4C0BC2F3306CF00E8F08C /* KBSvipBenefitBgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0B82F3306CF00E8F08C /* KBSvipBenefitBgView.m */; };
|
||||
04F4C0BF2F33369900E8F08C /* KBAIChatDeleteConfirmView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0BE2F33369900E8F08C /* KBAIChatDeleteConfirmView.m */; };
|
||||
04FC95672EB0546C007BD342 /* KBKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95652EB0546C007BD342 /* KBKey.m */; };
|
||||
04FC956A2EB05497007BD342 /* KBKeyButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95692EB05497007BD342 /* KBKeyButton.m */; };
|
||||
04FC956D2EB054B7007BD342 /* KBKeyboardView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC956C2EB054B7007BD342 /* KBKeyboardView.m */; };
|
||||
@@ -271,6 +268,8 @@
|
||||
04FEDC422F00040000999999 /* KBKeyboardSubscriptionFeatureMarqueeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FEDC412F00040000999999 /* KBKeyboardSubscriptionFeatureMarqueeView.m */; };
|
||||
05A1B2D12F5B1A2B3C4D5E60 /* KBSearchVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 05A1B2C52F5B1A2B3C4D5E60 /* KBSearchVM.m */; };
|
||||
05A1B2D22F5B1A2B3C4D5E60 /* KBSearchThemeModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 05A1B2C72F5B1A2B3C4D5E60 /* KBSearchThemeModel.m */; };
|
||||
0F2A10032F3C0001002CA5A0 /* KBChatMessageActionPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F2A10022F3C0001002CA5A0 /* KBChatMessageActionPopView.m */; };
|
||||
0F2A10132F3C0002002CA5A0 /* KBAIPersonaSidebarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F2A10122F3C0002002CA5A0 /* KBAIPersonaSidebarView.m */; };
|
||||
471CAD3574798685B72ADD55 /* KBMyTheme.m in Sources */ = {isa = PBXBuildFile; fileRef = 180D662EC4DB3A7FFF83FF18 /* KBMyTheme.m */; };
|
||||
49B63DBAEE9076C591E13D68 /* KBShopThemeTagModel.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A844CD2D8584596DBE6316 /* KBShopThemeTagModel.m */; };
|
||||
550CB2630FA4A7B4B9782EFA /* KBMyTheme.m in Sources */ = {isa = PBXBuildFile; fileRef = 180D662EC4DB3A7FFF83FF18 /* KBMyTheme.m */; };
|
||||
@@ -292,6 +291,7 @@
|
||||
A1B2C9262FC9000100000001 /* KBChatMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C9212FC9000100000001 /* KBChatMessage.m */; };
|
||||
A1B2C9272FC9000100000001 /* KBChatMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C9232FC9000100000001 /* KBChatMessageCell.m */; };
|
||||
A1B2C9282FC9000100000001 /* KBChatPanelView.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C9252FC9000100000001 /* KBChatPanelView.m */; };
|
||||
A1B2C9302FCA000100000001 /* KBChatLimitPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD232F28A836005D62AE /* KBChatLimitPopView.m */; };
|
||||
A1B2D7022EB8C00100000001 /* KBLangTestVC.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2D7012EB8C00100000001 /* KBLangTestVC.m */; };
|
||||
A1B2E1012EBC7AAA00000001 /* KBTopThreeView.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2E0022EBC7AAA00000001 /* KBTopThreeView.m */; };
|
||||
A1B2E1022EBC7AAA00000001 /* HomeHotCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2E0042EBC7AAA00000001 /* HomeHotCell.m */; };
|
||||
@@ -394,33 +394,15 @@
|
||||
0459D1B52EBA287900F2D189 /* KBSkinManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinManager.h; sourceTree = "<group>"; };
|
||||
0459D1B62EBA287900F2D189 /* KBSkinManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinManager.m; sourceTree = "<group>"; };
|
||||
0460866A2F18D75500757C95 /* ai_test.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = ai_test.m4a; sourceTree = "<group>"; };
|
||||
046086712F191B6900757C95 /* KBAiMainVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAiMainVC.h; sourceTree = "<group>"; };
|
||||
046086722F191B6900757C95 /* KBAiMainVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAiMainVC.m; sourceTree = "<group>"; };
|
||||
046086742F191CC700757C95 /* AI技术分析.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "AI技术分析.txt"; sourceTree = "<group>"; };
|
||||
046086962F19238500757C95 /* KBAiRecordButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAiRecordButton.h; sourceTree = "<group>"; };
|
||||
046086972F19238500757C95 /* KBAiRecordButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAiRecordButton.m; sourceTree = "<group>"; };
|
||||
046086982F19238500757C95 /* KBAiWaveformView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAiWaveformView.h; sourceTree = "<group>"; };
|
||||
046086992F19238500757C95 /* KBAiWaveformView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAiWaveformView.m; sourceTree = "<group>"; };
|
||||
0460869D2F19239B00757C95 /* ASRStreamClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASRStreamClient.h; sourceTree = "<group>"; };
|
||||
0460869E2F19239B00757C95 /* ASRStreamClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASRStreamClient.m; sourceTree = "<group>"; };
|
||||
0460869F2F19239B00757C95 /* AudioCaptureManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioCaptureManager.h; sourceTree = "<group>"; };
|
||||
046086A02F19239B00757C95 /* AudioCaptureManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AudioCaptureManager.m; sourceTree = "<group>"; };
|
||||
046086A12F19239B00757C95 /* AudioSessionManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioSessionManager.h; sourceTree = "<group>"; };
|
||||
046086A22F19239B00757C95 /* AudioSessionManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AudioSessionManager.m; sourceTree = "<group>"; };
|
||||
046086A32F19239B00757C95 /* AudioStreamPlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioStreamPlayer.h; sourceTree = "<group>"; };
|
||||
046086A42F19239B00757C95 /* AudioStreamPlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AudioStreamPlayer.m; sourceTree = "<group>"; };
|
||||
046086A52F19239B00757C95 /* ConversationOrchestrator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ConversationOrchestrator.h; sourceTree = "<group>"; };
|
||||
046086A62F19239B00757C95 /* ConversationOrchestrator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ConversationOrchestrator.m; sourceTree = "<group>"; };
|
||||
046086A72F19239B00757C95 /* LLMStreamClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LLMStreamClient.h; sourceTree = "<group>"; };
|
||||
046086A82F19239B00757C95 /* LLMStreamClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LLMStreamClient.m; sourceTree = "<group>"; };
|
||||
046086A92F19239B00757C95 /* Segmenter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Segmenter.h; sourceTree = "<group>"; };
|
||||
046086AA2F19239B00757C95 /* Segmenter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Segmenter.m; sourceTree = "<group>"; };
|
||||
046086AB2F19239B00757C95 /* SubtitleSync.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SubtitleSync.h; sourceTree = "<group>"; };
|
||||
046086AC2F19239B00757C95 /* SubtitleSync.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SubtitleSync.m; sourceTree = "<group>"; };
|
||||
046086AD2F19239B00757C95 /* TTSPlaybackPipeline.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TTSPlaybackPipeline.h; sourceTree = "<group>"; };
|
||||
046086AE2F19239B00757C95 /* TTSPlaybackPipeline.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TTSPlaybackPipeline.m; sourceTree = "<group>"; };
|
||||
046086AF2F19239B00757C95 /* TTSServiceClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TTSServiceClient.h; sourceTree = "<group>"; };
|
||||
046086B02F19239B00757C95 /* TTSServiceClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TTSServiceClient.m; sourceTree = "<group>"; };
|
||||
046086BB2F1A039F00757C95 /* KBAICommentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAICommentView.h; sourceTree = "<group>"; };
|
||||
046086BC2F1A039F00757C95 /* KBAICommentView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAICommentView.m; sourceTree = "<group>"; };
|
||||
046086C62F1A092500757C95 /* comments_mock.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = comments_mock.json; sourceTree = "<group>"; };
|
||||
@@ -442,8 +424,6 @@
|
||||
0477BDEF2EBB76E30055D639 /* HomeSheetVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HomeSheetVC.m; sourceTree = "<group>"; };
|
||||
0477BDF12EBB7B850055D639 /* KBDirectionIndicatorView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBDirectionIndicatorView.h; sourceTree = "<group>"; };
|
||||
0477BDF22EBB7B850055D639 /* KBDirectionIndicatorView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBDirectionIndicatorView.m; sourceTree = "<group>"; };
|
||||
0477BDF52EBC63A80055D639 /* KBTestVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBTestVC.h; sourceTree = "<group>"; };
|
||||
0477BDF62EBC63A80055D639 /* KBTestVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBTestVC.m; sourceTree = "<group>"; };
|
||||
0477BDF82EBC66340055D639 /* HomeHeadView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeHeadView.h; sourceTree = "<group>"; };
|
||||
0477BDF92EBC66340055D639 /* HomeHeadView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HomeHeadView.m; sourceTree = "<group>"; };
|
||||
0477BDFB2EBC6A170055D639 /* HomeHotVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeHotVC.h; sourceTree = "<group>"; };
|
||||
@@ -572,9 +552,7 @@
|
||||
048FFD2F2F29F3C3005D62AE /* KBAIMessageZanVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAIMessageZanVC.m; sourceTree = "<group>"; };
|
||||
048FFD312F29F3D2005D62AE /* KBAIMessageChatingVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAIMessageChatingVC.h; sourceTree = "<group>"; };
|
||||
048FFD322F29F3D2005D62AE /* KBAIMessageChatingVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAIMessageChatingVC.m; sourceTree = "<group>"; };
|
||||
048FFD342F29F88E005D62AE /* AIMessageVM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AIMessageVM.h; sourceTree = "<group>"; };
|
||||
048FFD352F29F400005D62AE /* KBAIMessageListVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAIMessageListVC.h; sourceTree = "<group>"; };
|
||||
048FFD352F29F88E005D62AE /* AIMessageVM.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AIMessageVM.m; sourceTree = "<group>"; };
|
||||
048FFD362F29F400005D62AE /* KBAIMessageListVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAIMessageListVC.m; sourceTree = "<group>"; };
|
||||
048FFD372F2A24C5005D62AE /* KBAIChatMessageCacheManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAIChatMessageCacheManager.h; sourceTree = "<group>"; };
|
||||
048FFD382F29F410005D62AE /* KBAIMessageCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAIMessageCell.h; sourceTree = "<group>"; };
|
||||
@@ -677,6 +655,12 @@
|
||||
04A9FE1D2EB893F10020DB6D /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
04B5A1A02EEFA12300AAAAAA /* KBPayProductModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBPayProductModel.h; sourceTree = "<group>"; };
|
||||
04B5A1A12EEFA12300AAAAAA /* KBPayProductModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBPayProductModel.m; sourceTree = "<group>"; };
|
||||
04BBF8992F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBKeyboardStressTestVC.h; sourceTree = "<group>"; };
|
||||
04BBF89A2F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBKeyboardStressTestVC.m; sourceTree = "<group>"; };
|
||||
04BBF89B2F3ACD8800B1FBB2 /* KBTestVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBTestVC.h; sourceTree = "<group>"; };
|
||||
04BBF89C2F3ACD8800B1FBB2 /* KBTestVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBTestVC.m; sourceTree = "<group>"; };
|
||||
04BBF8FE2F3C97CB00B1FBB2 /* DeepgramWebSocketClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeepgramWebSocketClient.h; sourceTree = "<group>"; };
|
||||
04BBF8FF2F3C97CB00B1FBB2 /* DeepgramWebSocketClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DeepgramWebSocketClient.m; sourceTree = "<group>"; };
|
||||
04C6EAAC2EAF86530089C901 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
04C6EAAD2EAF86530089C901 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
04C6EAAE2EAF86530089C901 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
@@ -700,15 +684,9 @@
|
||||
04E0383C2F1A7C30002CA5A0 /* KBCustomTabBar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBCustomTabBar.h; sourceTree = "<group>"; };
|
||||
04E0383D2F1A7C30002CA5A0 /* KBCustomTabBar.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBCustomTabBar.m; sourceTree = "<group>"; };
|
||||
04E038D72F20BFFB002CA5A0 /* websocket-api.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "websocket-api.md"; sourceTree = "<group>"; };
|
||||
04E038D92F20C420002CA5A0 /* VoiceChatStreamingManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VoiceChatStreamingManager.h; sourceTree = "<group>"; };
|
||||
04E038DA2F20C420002CA5A0 /* VoiceChatStreamingManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VoiceChatStreamingManager.m; sourceTree = "<group>"; };
|
||||
04E038DB2F20C420002CA5A0 /* VoiceChatWebSocketClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VoiceChatWebSocketClient.h; sourceTree = "<group>"; };
|
||||
04E038DC2F20C420002CA5A0 /* VoiceChatWebSocketClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VoiceChatWebSocketClient.m; sourceTree = "<group>"; };
|
||||
04E038E22F20E500002CA5A0 /* deepgramAPI.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = deepgramAPI.md; sourceTree = "<group>"; };
|
||||
04E038E42F20E877002CA5A0 /* DeepgramStreamingManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeepgramStreamingManager.h; sourceTree = "<group>"; };
|
||||
04E038E52F20E877002CA5A0 /* DeepgramStreamingManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DeepgramStreamingManager.m; sourceTree = "<group>"; };
|
||||
04E038E62F20E877002CA5A0 /* DeepgramWebSocketClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeepgramWebSocketClient.h; sourceTree = "<group>"; };
|
||||
04E038E72F20E877002CA5A0 /* DeepgramWebSocketClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DeepgramWebSocketClient.m; sourceTree = "<group>"; };
|
||||
04E038ED2F21F0EC002CA5A0 /* AiVM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AiVM.h; sourceTree = "<group>"; };
|
||||
04E038EE2F21F0EC002CA5A0 /* AiVM.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AiVM.m; sourceTree = "<group>"; };
|
||||
04E039422F236E75002CA5A0 /* KBChatAssistantMessageCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBChatAssistantMessageCell.h; sourceTree = "<group>"; };
|
||||
@@ -728,6 +706,22 @@
|
||||
04E0B2012F300002002CA5A0 /* KBVoiceRecordManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBVoiceRecordManager.m; sourceTree = "<group>"; };
|
||||
04E161812F10E6470022C23B /* normal_hei_them.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = normal_hei_them.zip; sourceTree = "<group>"; };
|
||||
04E161822F10E6470022C23B /* normal_them.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = normal_them.zip; sourceTree = "<group>"; };
|
||||
04F4C0A82F32274000E8F08C /* KBPayMainVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBPayMainVC.h; sourceTree = "<group>"; };
|
||||
04F4C0A92F32274000E8F08C /* KBPayMainVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBPayMainVC.m; sourceTree = "<group>"; };
|
||||
04F4C0AB2F32288600E8F08C /* KBPaySvipVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBPaySvipVC.h; sourceTree = "<group>"; };
|
||||
04F4C0AC2F32288600E8F08C /* KBPaySvipVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBPaySvipVC.m; sourceTree = "<group>"; };
|
||||
04F4C0AE2F322EF200E8F08C /* PagingViewTableHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PagingViewTableHeaderView.h; sourceTree = "<group>"; };
|
||||
04F4C0AF2F322EF200E8F08C /* PagingViewTableHeaderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PagingViewTableHeaderView.m; sourceTree = "<group>"; };
|
||||
04F4C0B12F33053800E8F08C /* KBSvipBenefitCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSvipBenefitCell.h; sourceTree = "<group>"; };
|
||||
04F4C0B22F33053800E8F08C /* KBSvipBenefitCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSvipBenefitCell.m; sourceTree = "<group>"; };
|
||||
04F4C0B32F33053800E8F08C /* KBSvipSubscribeCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSvipSubscribeCell.h; sourceTree = "<group>"; };
|
||||
04F4C0B42F33053800E8F08C /* KBSvipSubscribeCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSvipSubscribeCell.m; sourceTree = "<group>"; };
|
||||
04F4C0B72F3306CF00E8F08C /* KBSvipBenefitBgView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSvipBenefitBgView.h; sourceTree = "<group>"; };
|
||||
04F4C0B82F3306CF00E8F08C /* KBSvipBenefitBgView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSvipBenefitBgView.m; sourceTree = "<group>"; };
|
||||
04F4C0B92F3306CF00E8F08C /* KBSvipFlowLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSvipFlowLayout.h; sourceTree = "<group>"; };
|
||||
04F4C0BA2F3306CF00E8F08C /* KBSvipFlowLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSvipFlowLayout.m; sourceTree = "<group>"; };
|
||||
04F4C0BD2F33369900E8F08C /* KBAIChatDeleteConfirmView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAIChatDeleteConfirmView.h; sourceTree = "<group>"; };
|
||||
04F4C0BE2F33369900E8F08C /* KBAIChatDeleteConfirmView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAIChatDeleteConfirmView.m; sourceTree = "<group>"; };
|
||||
04FC953A2EAFAE56007BD342 /* KeyBoardPrefixHeader.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyBoardPrefixHeader.pch; sourceTree = "<group>"; };
|
||||
04FC95642EB0546C007BD342 /* KBKey.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBKey.h; sourceTree = "<group>"; };
|
||||
04FC95652EB0546C007BD342 /* KBKey.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBKey.m; sourceTree = "<group>"; };
|
||||
@@ -797,6 +791,10 @@
|
||||
05A1B2C52F5B1A2B3C4D5E60 /* KBSearchVM.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSearchVM.m; sourceTree = "<group>"; };
|
||||
05A1B2C62F5B1A2B3C4D5E60 /* KBSearchThemeModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSearchThemeModel.h; sourceTree = "<group>"; };
|
||||
05A1B2C72F5B1A2B3C4D5E60 /* KBSearchThemeModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSearchThemeModel.m; sourceTree = "<group>"; };
|
||||
0F2A10012F3C0001002CA5A0 /* KBChatMessageActionPopView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBChatMessageActionPopView.h; sourceTree = "<group>"; };
|
||||
0F2A10022F3C0001002CA5A0 /* KBChatMessageActionPopView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBChatMessageActionPopView.m; sourceTree = "<group>"; };
|
||||
0F2A10112F3C0002002CA5A0 /* KBAIPersonaSidebarView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAIPersonaSidebarView.h; sourceTree = "<group>"; };
|
||||
0F2A10122F3C0002002CA5A0 /* KBAIPersonaSidebarView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAIPersonaSidebarView.m; sourceTree = "<group>"; };
|
||||
180D662EC4DB3A7FFF83FF18 /* KBMyTheme.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyTheme.m; sourceTree = "<group>"; };
|
||||
2C1092FB2B452F95B15D4263 /* Pods_CustomKeyboard.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CustomKeyboard.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
35E2B1C590E060D912A4E7F4 /* KBShopThemeTagModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBShopThemeTagModel.h; sourceTree = "<group>"; };
|
||||
@@ -951,6 +949,16 @@
|
||||
04122FAF2EC73C0100EF7AB3 /* KBVipReviewItemCell.m */,
|
||||
04122FB12EC73C0100EF7AB3 /* KBVipReviewListCell.h */,
|
||||
04122FB22EC73C0100EF7AB3 /* KBVipReviewListCell.m */,
|
||||
04F4C0AE2F322EF200E8F08C /* PagingViewTableHeaderView.h */,
|
||||
04F4C0AF2F322EF200E8F08C /* PagingViewTableHeaderView.m */,
|
||||
04F4C0B12F33053800E8F08C /* KBSvipBenefitCell.h */,
|
||||
04F4C0B22F33053800E8F08C /* KBSvipBenefitCell.m */,
|
||||
04F4C0B32F33053800E8F08C /* KBSvipSubscribeCell.h */,
|
||||
04F4C0B42F33053800E8F08C /* KBSvipSubscribeCell.m */,
|
||||
04F4C0B72F3306CF00E8F08C /* KBSvipBenefitBgView.h */,
|
||||
04F4C0B82F3306CF00E8F08C /* KBSvipBenefitBgView.m */,
|
||||
04F4C0B92F3306CF00E8F08C /* KBSvipFlowLayout.h */,
|
||||
04F4C0BA2F3306CF00E8F08C /* KBSvipFlowLayout.m */,
|
||||
);
|
||||
path = V;
|
||||
sourceTree = "<group>";
|
||||
@@ -962,6 +970,10 @@
|
||||
04122F802EC5FC6F00EF7AB3 /* KBJfPay.m */,
|
||||
04122F8F2EC73AF700EF7AB3 /* KBVipPay.h */,
|
||||
04122F902EC73AF700EF7AB3 /* KBVipPay.m */,
|
||||
04F4C0A82F32274000E8F08C /* KBPayMainVC.h */,
|
||||
04F4C0A92F32274000E8F08C /* KBPayMainVC.m */,
|
||||
04F4C0AB2F32288600E8F08C /* KBPaySvipVC.h */,
|
||||
04F4C0AC2F32288600E8F08C /* KBPaySvipVC.m */,
|
||||
);
|
||||
path = VC;
|
||||
sourceTree = "<group>";
|
||||
@@ -1114,8 +1126,6 @@
|
||||
0460866E2F191A5100757C95 /* VC */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
046086712F191B6900757C95 /* KBAiMainVC.h */,
|
||||
046086722F191B6900757C95 /* KBAiMainVC.m */,
|
||||
048FFD092F273BFC005D62AE /* KBAIHomeVC.h */,
|
||||
048FFD0A2F273BFC005D62AE /* KBAIHomeVC.m */,
|
||||
048FFD2B2F29F356005D62AE /* KBAIMessageVC.h */,
|
||||
@@ -1137,42 +1147,20 @@
|
||||
0460866F2F191A5100757C95 /* VM */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0460869D2F19239B00757C95 /* ASRStreamClient.h */,
|
||||
0460869E2F19239B00757C95 /* ASRStreamClient.m */,
|
||||
0460869F2F19239B00757C95 /* AudioCaptureManager.h */,
|
||||
046086A02F19239B00757C95 /* AudioCaptureManager.m */,
|
||||
046086A12F19239B00757C95 /* AudioSessionManager.h */,
|
||||
046086A22F19239B00757C95 /* AudioSessionManager.m */,
|
||||
046086A32F19239B00757C95 /* AudioStreamPlayer.h */,
|
||||
046086A42F19239B00757C95 /* AudioStreamPlayer.m */,
|
||||
046086A52F19239B00757C95 /* ConversationOrchestrator.h */,
|
||||
046086A62F19239B00757C95 /* ConversationOrchestrator.m */,
|
||||
046086A72F19239B00757C95 /* LLMStreamClient.h */,
|
||||
046086A82F19239B00757C95 /* LLMStreamClient.m */,
|
||||
046086A92F19239B00757C95 /* Segmenter.h */,
|
||||
046086AA2F19239B00757C95 /* Segmenter.m */,
|
||||
046086AB2F19239B00757C95 /* SubtitleSync.h */,
|
||||
046086AC2F19239B00757C95 /* SubtitleSync.m */,
|
||||
046086AD2F19239B00757C95 /* TTSPlaybackPipeline.h */,
|
||||
046086AE2F19239B00757C95 /* TTSPlaybackPipeline.m */,
|
||||
046086AF2F19239B00757C95 /* TTSServiceClient.h */,
|
||||
046086B02F19239B00757C95 /* TTSServiceClient.m */,
|
||||
04E038D92F20C420002CA5A0 /* VoiceChatStreamingManager.h */,
|
||||
04E038DA2F20C420002CA5A0 /* VoiceChatStreamingManager.m */,
|
||||
04E038DB2F20C420002CA5A0 /* VoiceChatWebSocketClient.h */,
|
||||
04E038DC2F20C420002CA5A0 /* VoiceChatWebSocketClient.m */,
|
||||
04E038E42F20E877002CA5A0 /* DeepgramStreamingManager.h */,
|
||||
04E038E52F20E877002CA5A0 /* DeepgramStreamingManager.m */,
|
||||
04BBF8FE2F3C97CB00B1FBB2 /* DeepgramWebSocketClient.h */,
|
||||
04BBF8FF2F3C97CB00B1FBB2 /* DeepgramWebSocketClient.m */,
|
||||
04E0B1002F300001002CA5A0 /* KBVoiceToTextManager.h */,
|
||||
04E0B1012F300001002CA5A0 /* KBVoiceToTextManager.m */,
|
||||
04E0B2002F300002002CA5A0 /* KBVoiceRecordManager.h */,
|
||||
04E0B2012F300002002CA5A0 /* KBVoiceRecordManager.m */,
|
||||
04E038E62F20E877002CA5A0 /* DeepgramWebSocketClient.h */,
|
||||
04E038E72F20E877002CA5A0 /* DeepgramWebSocketClient.m */,
|
||||
04E038ED2F21F0EC002CA5A0 /* AiVM.h */,
|
||||
04E038EE2F21F0EC002CA5A0 /* AiVM.m */,
|
||||
048FFD342F29F88E005D62AE /* AIMessageVM.h */,
|
||||
048FFD352F29F88E005D62AE /* AIMessageVM.m */,
|
||||
);
|
||||
path = VM;
|
||||
sourceTree = "<group>";
|
||||
@@ -1207,12 +1195,14 @@
|
||||
0477BE012EBC6D420055D639 /* FunctionTest */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0477BDF52EBC63A80055D639 /* KBTestVC.h */,
|
||||
0477BDF62EBC63A80055D639 /* KBTestVC.m */,
|
||||
A1B2D7002EB8C00100000001 /* KBLangTestVC.h */,
|
||||
A1B2D7012EB8C00100000001 /* KBLangTestVC.m */,
|
||||
0459D1B22EBA284C00F2D189 /* KBSkinCenterVC.h */,
|
||||
0459D1B32EBA284C00F2D189 /* KBSkinCenterVC.m */,
|
||||
04BBF8992F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.h */,
|
||||
04BBF89A2F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.m */,
|
||||
04BBF89B2F3ACD8800B1FBB2 /* KBTestVC.h */,
|
||||
04BBF89C2F3ACD8800B1FBB2 /* KBTestVC.m */,
|
||||
);
|
||||
path = FunctionTest;
|
||||
sourceTree = "<group>";
|
||||
@@ -1431,6 +1421,8 @@
|
||||
04E039452F236E75002CA5A0 /* KBChatTableView.m */,
|
||||
04E039492F236E75002CA5A0 /* KBChatUserMessageCell.h */,
|
||||
04E0394A2F236E75002CA5A0 /* KBChatUserMessageCell.m */,
|
||||
0F2A10012F3C0001002CA5A0 /* KBChatMessageActionPopView.h */,
|
||||
0F2A10022F3C0001002CA5A0 /* KBChatMessageActionPopView.m */,
|
||||
04E039422F236E75002CA5A0 /* KBChatAssistantMessageCell.h */,
|
||||
04E039432F236E75002CA5A0 /* KBChatAssistantMessageCell.m */,
|
||||
04E039472F236E75002CA5A0 /* KBChatTimeCell.h */,
|
||||
@@ -1444,6 +1436,10 @@
|
||||
children = (
|
||||
048FFD222F28A836005D62AE /* KBChatLimitPopView.h */,
|
||||
048FFD232F28A836005D62AE /* KBChatLimitPopView.m */,
|
||||
0F2A10112F3C0002002CA5A0 /* KBAIPersonaSidebarView.h */,
|
||||
0F2A10122F3C0002002CA5A0 /* KBAIPersonaSidebarView.m */,
|
||||
04F4C0BD2F33369900E8F08C /* KBAIChatDeleteConfirmView.h */,
|
||||
04F4C0BE2F33369900E8F08C /* KBAIChatDeleteConfirmView.m */,
|
||||
);
|
||||
path = PopView;
|
||||
sourceTree = "<group>";
|
||||
@@ -1540,6 +1536,13 @@
|
||||
path = Localization;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
04BBF8E52F3B50C000B1FBB2 /* KeyboardViewControllerHelp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = KeyboardViewControllerHelp;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
04C6EAB92EAF86530089C901 /* keyBoard */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1573,6 +1576,7 @@
|
||||
04C6EAD42EAF870B0089C901 /* Info.plist */,
|
||||
04C6EAD52EAF870B0089C901 /* KeyboardViewController.h */,
|
||||
04C6EAD62EAF870B0089C901 /* KeyboardViewController.m */,
|
||||
04BBF8E52F3B50C000B1FBB2 /* KeyboardViewControllerHelp */,
|
||||
04C6EADE2EAF8D680089C901 /* PrefixHeader.pch */,
|
||||
04286A0A2ECD88B400CE730C /* KeyboardAssets.xcassets */,
|
||||
);
|
||||
@@ -2343,6 +2347,7 @@
|
||||
04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */,
|
||||
048FFD562F2B9C3D005D62AE /* KBChatAssistantCell.m in Sources */,
|
||||
048FFD572F2B9C3D005D62AE /* KBChatUserCell.m in Sources */,
|
||||
A1B2C9302FCA000100000001 /* KBChatLimitPopView.m in Sources */,
|
||||
04C6EADD2EAF8CEB0089C901 /* KBToolBar.m in Sources */,
|
||||
A1B2C9262FC9000100000001 /* KBChatMessage.m in Sources */,
|
||||
A1B2C9272FC9000100000001 /* KBChatMessageCell.m in Sources */,
|
||||
@@ -2408,11 +2413,14 @@
|
||||
04122F622EC5F41D00EF7AB3 /* KBUser.m in Sources */,
|
||||
04122F8B2EC6F7C800EF7AB3 /* IAPVerifyTransactionObj.m in Sources */,
|
||||
04B5A1A22EEFA12300AAAAAA /* KBPayProductModel.m in Sources */,
|
||||
04F4C0AD2F32288600E8F08C /* KBPaySvipVC.m in Sources */,
|
||||
046086D62F1A093400757C95 /* KBAICommentInputView.m in Sources */,
|
||||
046086D72F1A093400757C95 /* KBAICommentFooterView.m in Sources */,
|
||||
046086D82F1A093400757C95 /* KBAIReplyCell.m in Sources */,
|
||||
046086D92F1A093400757C95 /* KBAICommentHeaderView.m in Sources */,
|
||||
04E0394B2F236E75002CA5A0 /* KBChatUserMessageCell.m in Sources */,
|
||||
0F2A10032F3C0001002CA5A0 /* KBChatMessageActionPopView.m in Sources */,
|
||||
0F2A10132F3C0002002CA5A0 /* KBAIPersonaSidebarView.m in Sources */,
|
||||
04E0394C2F236E75002CA5A0 /* KBChatTimeCell.m in Sources */,
|
||||
04E0394D2F236E75002CA5A0 /* KBChatAssistantMessageCell.m in Sources */,
|
||||
04E0394E2F236E75002CA5A0 /* KBChatTableView.m in Sources */,
|
||||
@@ -2421,7 +2429,6 @@
|
||||
04122FAD2EC73C0100EF7AB3 /* KBVipSubscribeCell.m in Sources */,
|
||||
049FB31D2EC21BCD00FAB05D /* KBMyKeyboardCell.m in Sources */,
|
||||
048909F62EC0AAAA00FABA60 /* KBCategoryTitleCell.m in Sources */,
|
||||
046086732F191B6900757C95 /* KBAiMainVC.m in Sources */,
|
||||
048909F72EC0AAAA00FABA60 /* KBCategoryTitleView.m in Sources */,
|
||||
048FFD332F29F3D2005D62AE /* KBAIMessageChatingVC.m in Sources */,
|
||||
048FFD342F29F400005D62AE /* KBAIMessageListVC.m in Sources */,
|
||||
@@ -2440,6 +2447,7 @@
|
||||
046086CC2F1A092500757C95 /* KBAIReplyModel.m in Sources */,
|
||||
046086CD2F1A092500757C95 /* KBAICommentModel.m in Sources */,
|
||||
04791F8F2ED469C0004E8522 /* KBHostAppLauncher.m in Sources */,
|
||||
04F4C0BF2F33369900E8F08C /* KBAIChatDeleteConfirmView.m in Sources */,
|
||||
047C65582EBCC06D0035E841 /* HomeRankCardCell.m in Sources */,
|
||||
04D1F6B32EDFF10A00B12345 /* KBSkinInstallBridge.m in Sources */,
|
||||
04122F912EC73AF700EF7AB3 /* KBVipPay.m in Sources */,
|
||||
@@ -2475,6 +2483,7 @@
|
||||
048908E92EBF843000FABA60 /* KBSkinDetailHeaderCell.m in Sources */,
|
||||
04A9FE1B2EB892460020DB6D /* KBLocalizationManager.m in Sources */,
|
||||
048908BC2EBE1FCB00FABA60 /* BaseViewController.m in Sources */,
|
||||
04F4C0B02F322EF200E8F08C /* PagingViewTableHeaderView.m in Sources */,
|
||||
0498BD902EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */,
|
||||
A1F0C1C32FABCDEF12345678 /* KBInviteCodeModel.m in Sources */,
|
||||
A1F0C1D32FACAD0012345678 /* KBMaiPointReporter.m in Sources */,
|
||||
@@ -2487,7 +2496,6 @@
|
||||
0498BD712EE02A41006CC1D5 /* KBForgetPwdNewPwdVC.m in Sources */,
|
||||
048908EF2EBF861800FABA60 /* KBSkinSectionTitleCell.m in Sources */,
|
||||
0450AAE22EF03D5100B6AF06 /* KBPerson.swift in Sources */,
|
||||
04E038E82F20E877002CA5A0 /* DeepgramWebSocketClient.m in Sources */,
|
||||
04E038E92F20E877002CA5A0 /* DeepgramStreamingManager.m in Sources */,
|
||||
04E0B1022F300001002CA5A0 /* KBVoiceToTextManager.m in Sources */,
|
||||
04E0B2022F300002002CA5A0 /* KBVoiceRecordManager.m in Sources */,
|
||||
@@ -2499,18 +2507,18 @@
|
||||
048908D22EBF611D00FABA60 /* KBHistoryMoreCell.m in Sources */,
|
||||
04FC95D82EB1EA16007BD342 /* BaseCell.m in Sources */,
|
||||
0498BD852EE1B255006CC1D5 /* KBSignUtils.m in Sources */,
|
||||
0477BDF72EBC63A80055D639 /* KBTestVC.m in Sources */,
|
||||
04122F7E2EC5FC5500EF7AB3 /* KBJfPayCell.m in Sources */,
|
||||
048FFD502F2B52E7005D62AE /* AIReportVC.m in Sources */,
|
||||
049FB2402EC4B6EF00FAB05D /* KBULBridgeNotification.m in Sources */,
|
||||
04BBF9002F3C97CB00B1FBB2 /* DeepgramWebSocketClient.m in Sources */,
|
||||
04FC95C92EB1E4C9007BD342 /* BaseNavigationController.m in Sources */,
|
||||
048908DD2EBF67EB00FABA60 /* KBSearchResultVC.m in Sources */,
|
||||
05A1B2D12F5B1A2B3C4D5E60 /* KBSearchVM.m in Sources */,
|
||||
05A1B2D22F5B1A2B3C4D5E60 /* KBSearchThemeModel.m in Sources */,
|
||||
047C65102EBCA8DD0035E841 /* HomeRankContentVC.m in Sources */,
|
||||
047C655C2EBCD0F80035E841 /* UIView+KBShadow.m in Sources */,
|
||||
04E038DD2F20C420002CA5A0 /* VoiceChatStreamingManager.m in Sources */,
|
||||
04E038DE2F20C420002CA5A0 /* VoiceChatWebSocketClient.m in Sources */,
|
||||
04F4C0B52F33053800E8F08C /* KBSvipBenefitCell.m in Sources */,
|
||||
04F4C0B62F33053800E8F08C /* KBSvipSubscribeCell.m in Sources */,
|
||||
049FB2262EC3136D00FAB05D /* KBPersonInfoItemCell.m in Sources */,
|
||||
048908C32EBE32B800FABA60 /* KBSearchVC.m in Sources */,
|
||||
049FB20B2EC1C13800FAB05D /* KBSkinBottomActionView.m in Sources */,
|
||||
@@ -2518,7 +2526,7 @@
|
||||
04FC95DD2EB202A3007BD342 /* KBGuideVC.m in Sources */,
|
||||
04FC95E52EB220B5007BD342 /* UIColor+Extension.m in Sources */,
|
||||
048908E02EBF73DC00FABA60 /* MySkinVC.m in Sources */,
|
||||
048FFD362F29F88E005D62AE /* AIMessageVM.m in Sources */,
|
||||
04F4C0AA2F32274000E8F08C /* KBPayMainVC.m in Sources */,
|
||||
048908F22EC047FD00FABA60 /* KBShopHeadView.m in Sources */,
|
||||
0498BD742EE02E3D006CC1D5 /* KBRegistVerEmailVC.m in Sources */,
|
||||
049FB2292EC31BB000FAB05D /* KBChangeNicknamePopView.m in Sources */,
|
||||
@@ -2541,16 +2549,8 @@
|
||||
048FFD112F27432D005D62AE /* KBPersonaPageModel.m in Sources */,
|
||||
0498BD6B2EE025FC006CC1D5 /* KBForgetPwdVC.m in Sources */,
|
||||
048FFD182F2763A5005D62AE /* KBVoiceInputBar.m in Sources */,
|
||||
046086B12F19239B00757C95 /* SubtitleSync.m in Sources */,
|
||||
046086B22F19239B00757C95 /* TTSServiceClient.m in Sources */,
|
||||
046086B32F19239B00757C95 /* AudioSessionManager.m in Sources */,
|
||||
046086B42F19239B00757C95 /* LLMStreamClient.m in Sources */,
|
||||
046086B52F19239B00757C95 /* Segmenter.m in Sources */,
|
||||
046086B62F19239B00757C95 /* TTSPlaybackPipeline.m in Sources */,
|
||||
046086B72F19239B00757C95 /* ConversationOrchestrator.m in Sources */,
|
||||
046086B82F19239B00757C95 /* ASRStreamClient.m in Sources */,
|
||||
046086B92F19239B00757C95 /* AudioCaptureManager.m in Sources */,
|
||||
046086BA2F19239B00757C95 /* AudioStreamPlayer.m in Sources */,
|
||||
048908FE2EC0CC2400FABA60 /* UIScrollView+KBEmptyView.m in Sources */,
|
||||
0498BD7E2EE04F9C006CC1D5 /* KBTag.m in Sources */,
|
||||
04791F922ED48010004E8522 /* KBNoticeVC.m in Sources */,
|
||||
@@ -2579,6 +2579,8 @@
|
||||
047C65502EBCBA9E0035E841 /* KBShopVC.m in Sources */,
|
||||
0477BE042EBC83130055D639 /* HomeMainVC.m in Sources */,
|
||||
0477BDFD2EBC6A170055D639 /* HomeHotVC.m in Sources */,
|
||||
04F4C0BB2F3306CF00E8F08C /* KBSvipFlowLayout.m in Sources */,
|
||||
04F4C0BC2F3306CF00E8F08C /* KBSvipBenefitBgView.m in Sources */,
|
||||
04E039522F2387D2002CA5A0 /* KBAiChatMessage.m in Sources */,
|
||||
0460869A2F19238500757C95 /* KBAiWaveformView.m in Sources */,
|
||||
0460869C2F19238500757C95 /* KBAiRecordButton.m in Sources */,
|
||||
@@ -2596,6 +2598,8 @@
|
||||
A1B2E1012EBC7AAA00000001 /* KBTopThreeView.m in Sources */,
|
||||
A1B2E1022EBC7AAA00000001 /* HomeHotCell.m in Sources */,
|
||||
048FFD272F28C6CF005D62AE /* KBImagePositionButton.m in Sources */,
|
||||
04BBF89D2F3ACD8800B1FBB2 /* KBKeyboardStressTestVC.m in Sources */,
|
||||
04BBF89E2F3ACD8800B1FBB2 /* KBTestVC.m in Sources */,
|
||||
0459D1B72EBA287900F2D189 /* KBSkinManager.m in Sources */,
|
||||
04286A002ECAEF2B00CE730C /* KBMoneyBtn.m in Sources */,
|
||||
048908F52EC0496400FABA60 /* KBShopItemVC.m in Sources */,
|
||||
|
||||
@@ -57,8 +57,12 @@
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<RemoteRunnable
|
||||
runnableDebuggingMode = "0"
|
||||
BundleIdentifier = "com.loveKey.nyx"
|
||||
RemotePath = "/var/containers/Bundle/Application/E51DCFA2-A182-4B31-8A45-BCCF663ADCAA/keyBoard.app">
|
||||
</RemoteRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "727EC7522EAF848B00B36487"
|
||||
@@ -66,7 +70,14 @@
|
||||
BlueprintName = "keyBoard"
|
||||
ReferencedContainer = "container:keyBoard.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</MacroExpansion>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "OS_ACTIVITY_MODE"
|
||||
value = "disable"
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#import "KBLoginPopView.h"
|
||||
#import "KBSexSelVC.h"
|
||||
#import "KBKeyboardPermissionManager.h"
|
||||
#import "KBVipPay.h"
|
||||
#import "KBPayMainVC.h"
|
||||
#import "KBUserSessionManager.h"
|
||||
#import "KBLoginVC.h"
|
||||
#import "KBConfig.h"
|
||||
@@ -192,6 +192,9 @@ static NSTimeInterval const kKBSubscriptionPrefillTTL = 10 * 60.0;
|
||||
return YES;
|
||||
} else if ([host isEqualToString:@"recharge"]) { // kbkeyboard://recharge
|
||||
NSDictionary<NSString *, NSString *> *params = [self kb_queryParametersFromURL:url];
|
||||
NSString *vipType = [params[@"vipType"] lowercaseString];
|
||||
BOOL preferSvip = ([vipType isKindOfClass:NSString.class] && vipType.length > 0 &&
|
||||
([vipType isEqualToString:@"svip"] || [vipType isEqualToString:@"1"]));
|
||||
NSString *productId = params[@"productId"];
|
||||
BOOL autoPay = NO;
|
||||
NSString *autoFlag = params[@"autoPay"];
|
||||
@@ -203,6 +206,9 @@ static NSTimeInterval const kKBSubscriptionPrefillTTL = 10 * 60.0;
|
||||
autoPay = YES;
|
||||
}
|
||||
|
||||
KBPayMainVC *vc = [[KBPayMainVC alloc] init];
|
||||
vc.initialSelectedIndex = preferSvip ? 1 : 0;
|
||||
if (!preferSvip) {
|
||||
BOOL wantsPrefill = NO;
|
||||
NSString *prefillFlag = params[@"prefill"];
|
||||
if ([prefillFlag respondsToSelector:@selector(boolValue)] && prefillFlag.boolValue) {
|
||||
@@ -220,7 +226,6 @@ static NSTimeInterval const kKBSubscriptionPrefillTTL = 10 * 60.0;
|
||||
}
|
||||
}
|
||||
|
||||
KBVipPay *vc = [[KBVipPay alloc] init];
|
||||
if ([prefillPayload isKindOfClass:NSDictionary.class]) {
|
||||
NSArray *productsJSON = prefillPayload[@"products"];
|
||||
NSNumber *selectedIndexNumber = prefillPayload[@"selectedIndex"];
|
||||
@@ -232,6 +237,7 @@ static NSTimeInterval const kKBSubscriptionPrefillTTL = 10 * 60.0;
|
||||
} else {
|
||||
[vc configureWithProductId:productId autoPurchase:autoPay];
|
||||
}
|
||||
}
|
||||
[KB_CURRENT_NAV pushViewController:vc animated:true];
|
||||
return YES;
|
||||
}
|
||||
|
||||
22
keyBoard/Assets.xcassets/AI/ai_limit_close.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_limit_close@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_limit_close@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/AI/ai_limit_close.imageset/ai_limit_close@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
keyBoard/Assets.xcassets/AI/ai_limit_close.imageset/ai_limit_close@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
22
keyBoard/Assets.xcassets/AI/ai_limit_goto.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_limit_goto@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_limit_goto@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/AI/ai_limit_goto.imageset/ai_limit_goto@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
keyBoard/Assets.xcassets/AI/ai_limit_goto.imageset/ai_limit_goto@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
22
keyBoard/Assets.xcassets/AI/ai_limit_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_limit_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_limit_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/AI/ai_limit_icon.imageset/ai_limit_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 358 KiB |
BIN
keyBoard/Assets.xcassets/AI/ai_limit_icon.imageset/ai_limit_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 733 KiB |
22
keyBoard/Assets.xcassets/AI/ai_more_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_more_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_more_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/AI/ai_more_icon.imageset/ai_more_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
keyBoard/Assets.xcassets/AI/ai_more_icon.imageset/ai_more_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
22
keyBoard/Assets.xcassets/AI/ai_role_sel.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_role_sel@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_role_sel@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/AI/ai_role_sel.imageset/ai_role_sel@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
keyBoard/Assets.xcassets/AI/ai_role_sel.imageset/ai_role_sel@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
22
keyBoard/Assets.xcassets/AI/ai_search_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_search_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ai_search_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/AI/ai_search_icon.imageset/ai_search_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 923 B |
BIN
keyBoard/Assets.xcassets/AI/ai_search_icon.imageset/ai_search_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
22
keyBoard/Assets.xcassets/AI/chat_del_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "chat_del_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "chat_del_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/AI/chat_del_icon.imageset/chat_del_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
keyBoard/Assets.xcassets/AI/chat_del_icon.imageset/chat_del_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 105 KiB |
22
keyBoard/Assets.xcassets/AI/right_arrow_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "right_arrow_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "right_arrow_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/AI/right_arrow_icon.imageset/right_arrow_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 474 B |
BIN
keyBoard/Assets.xcassets/AI/right_arrow_icon.imageset/right_arrow_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 858 B |
22
keyBoard/Assets.xcassets/My/my_svip_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "my_svip_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "my_svip_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/My/my_svip_icon.imageset/my_svip_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
keyBoard/Assets.xcassets/My/my_svip_icon.imageset/my_svip_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
22
keyBoard/Assets.xcassets/Pay/SVIP_Normal_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "SVIP_Normal_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "SVIP_Normal_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/SVIP_Normal_icon.imageset/SVIP_Normal_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
keyBoard/Assets.xcassets/Pay/SVIP_Normal_icon.imageset/SVIP_Normal_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
22
keyBoard/Assets.xcassets/Pay/SVIP_Selected_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "SVIP_Selected_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "SVIP_Selected_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/SVIP_Selected_icon.imageset/SVIP_Selected_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
keyBoard/Assets.xcassets/Pay/SVIP_Selected_icon.imageset/SVIP_Selected_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 27 KiB |
22
keyBoard/Assets.xcassets/Pay/VIP_Normal_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "VIP_Normal_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "VIP_Normal_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/VIP_Normal_icon.imageset/VIP_Normal_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
keyBoard/Assets.xcassets/Pay/VIP_Normal_icon.imageset/VIP_Normal_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
22
keyBoard/Assets.xcassets/Pay/VIP_Selected_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "VIP_Selected_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "VIP_Selected_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/VIP_Selected_icon.imageset/VIP_Selected_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
keyBoard/Assets.xcassets/Pay/VIP_Selected_icon.imageset/VIP_Selected_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 27 KiB |
22
keyBoard/Assets.xcassets/Pay/pay_ais_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_ai_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_ai_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/pay_ais_icon.imageset/pay_ai_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
keyBoard/Assets.xcassets/Pay/pay_ais_icon.imageset/pay_ai_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 18 KiB |
22
keyBoard/Assets.xcassets/Pay/pay_chats_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_chat_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_chat_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/pay_chats_icon.imageset/pay_chat_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
keyBoard/Assets.xcassets/Pay/pay_chats_icon.imageset/pay_chat_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 24 KiB |
22
keyBoard/Assets.xcassets/Pay/pay_colorbg_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_colorbg_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_colorbg_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/pay_colorbg_icon.imageset/pay_colorbg_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
keyBoard/Assets.xcassets/Pay/pay_colorbg_icon.imageset/pay_colorbg_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 47 KiB |
22
keyBoard/Assets.xcassets/Pay/pay_graybg_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_graybg_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_graybg_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/pay_graybg_icon.imageset/pay_graybg_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
keyBoard/Assets.xcassets/Pay/pay_graybg_icon.imageset/pay_graybg_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
22
keyBoard/Assets.xcassets/Pay/pay_history_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_history_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_history_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/pay_history_icon.imageset/pay_history_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
keyBoard/Assets.xcassets/Pay/pay_history_icon.imageset/pay_history_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 22 KiB |
22
keyBoard/Assets.xcassets/Pay/pay_keyboards_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_keyboard_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_keyboard_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/pay_keyboards_icon.imageset/pay_keyboard_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
keyBoard/Assets.xcassets/Pay/pay_keyboards_icon.imageset/pay_keyboard_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 24 KiB |
22
keyBoard/Assets.xcassets/Pay/pay_leftline_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_leftline_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_leftline_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/pay_leftline_icon.imageset/pay_leftline_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 570 B |
BIN
keyBoard/Assets.xcassets/Pay/pay_leftline_icon.imageset/pay_leftline_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 841 B |
22
keyBoard/Assets.xcassets/Pay/pay_oks_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_oks_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_oks_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/pay_oks_icon.imageset/pay_oks_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 518 B |
BIN
keyBoard/Assets.xcassets/Pay/pay_oks_icon.imageset/pay_oks_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 999 B |
22
keyBoard/Assets.xcassets/Pay/pay_person_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_person_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_person_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/pay_person_icon.imageset/pay_person_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
keyBoard/Assets.xcassets/Pay/pay_person_icon.imageset/pay_person_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 17 KiB |
22
keyBoard/Assets.xcassets/Pay/pay_phone_icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_phone_icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "pay_phone_icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
keyBoard/Assets.xcassets/Pay/pay_phone_icon.imageset/pay_phone_icon@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
keyBoard/Assets.xcassets/Pay/pay_phone_icon.imageset/pay_phone_icon@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 18 KiB |