#import <JoyGemAI/JoyGemCallManager.h>
#import <JoyGemAI/JoyGemEnum.h>
#import <JoyGemAI/JoyGemDeviceManager.h>
+#import <JoyGemAI/JoyGemSubtitleManager.h>
@required
-(void)callManager:(JoyGemCallManager*)manager didUpdateMonoAsr:(JoyGemAsrModel*)model;
+/// 录音时的声波强度回调
+/// - Parameters:
+/// - list: 强度数组
+/// - timestamp: 时间戳(ms)
+@optional
+-(void)callManager:(JoyGemCallManager*)manager monoIntensity:(NSArray<NSNumber*>*)list timestamp:(NSTimeInterval)timestamp;
+
@end
@protocol JoyGemCallStereoDelegate <NSObject>
@required
-(void)callManager:(JoyGemCallManager*)manager didUpdateStereoAsr:(JoyGemAsrModel*)model channel:(NSUInteger)index;
+/// 录音时的声波强度回调
+/// - Parameters:
+/// - lists: 强度数组(二维数组,0为左声道,1为右声道)
+/// - timestamp: 时间戳(ms)
+@optional
+-(void)callManager:(JoyGemCallManager*)manager stereoIntensity:(NSArray<NSArray<NSNumber*>*>*)lists timestamp:(NSTimeInterval)timestamp;
+
@end
-(void)chatManager:(JoyGemChatManager*)manager onError:(JoyGemErrorCode)code;
@optional
--(void)chatManagerStopped:(JoyGemChatManager*)manager DEPRECATED_MSG_ATTRIBUTE("Use isActive");
+-(void)chatManagerStopped:(JoyGemChatManager*)manager DEPRECATED_MSG_ATTRIBUTE("Use isActive instead");
@end
/// 开启单次对话
/// - Note: 如正在播报,需要提前关闭tts后再调用,否则开启无效
-/// - TODO: 待容错处理
- (void)startSingleChat;
/// 结束单次对话
/// - Note:请勿在开启单次对话后立刻调用该方法,否则耳机可能不会处理该次请求
-/// - TODO: 待容错处理
- (void)stopSingleChat;
/// 开启连续对话
/// - list: 角色列表
-(void)getSpeakerList:(void (^)(NSArray<JoyGemSpeaker *>*))list;
-///配置当前角色
+/// 配置当前角色
-(void)setSpeaker:(JoyGemSpeaker*)speaker;
+/// 重置为默认角色
+-(void)resetSpearker;
+
+/// 进入英语口语陪练模式
+-(void)startSpoken;
+
+/// 退出英语口语陪练模式
+-(void)stopSpoken;
+
@end
NS_ASSUME_NONNULL_END
@property (nonatomic, strong) NSString *type;/// 类型
@property (nonatomic, strong) NSString *role;/// 角色
-@end
+///默认角色
++(JoyGemSpeaker*)defaultRole;
+
+///默认的口语陪练角色
++(JoyGemSpeaker*)defaultSpoken;
+@end
///ASR对象
@interface JoyGemAsrModel : NSObject
@end
+///录音声纹识别
+@interface JoyGemRecordSpeaker : NSObject
+
+@property (nonatomic, copy, nullable) NSString *speakerId; /// 说话人ID(字符串,如 "1")
+@property (nonatomic, assign) NSInteger startTime; /// 起始时间(ms)
+@property (nonatomic, assign) NSInteger endTime; /// 结束时间(ms)
+@property (nonatomic, copy, nullable) NSString *text; /// 该段文本
+@property (nonatomic, assign) NSInteger index; /// 该段确定序号
+
+@end
+
NS_ASSUME_NONNULL_END
//印尼语 id-ID
extern NSString* const kJoyGemAILocalizationID;
-//俄语 ru-RU
-extern NSString* const kJoyGemAILocalizationRU;
+//俄语(输入) ru-RU
+extern NSString* const kJoyGemAILocalizationRU_Original;
+
+//俄语(输出) ru
+extern NSString* const kJoyGemAILocalizationRU_Target;
//马来语 ms-MY
extern NSString* const kJoyGemAILocalizationMS;
+//德语 de-DE
+extern NSString* const kJoyGemAILocalizationDE;
+
+//菲律宾语 fil-PH
+extern NSString* const kJoyGemAILocalizationFIL;
+
+//泰语 th-TH
+extern NSString* const kJoyGemAILocalizationTH;
+
+//阿拉伯语 ar-SA
+extern NSString* const kJoyGemAILocalizationAR;
+
+//越南语 vi
+extern NSString* const kJoyGemAILocalizationVI;
+
+
+
///MARK: 蓝牙相关
///
///AI功能使用的蓝牙服务
@class JoyGemRecordManager;
@class JoyGemAsrModel;
+@class JoyGemRecordSpeaker;
//现场录音的委托类
@protocol JoyGemRecordManagerDelegate <NSObject>
@optional
-(void)recordManagerCompleted:(JoyGemRecordManager*)manager;
+/// 录音时的声波强度回调
+/// - Parameters:
+/// - list: 强度数组
+/// - timestamp: 时间戳(ms)
+@optional
+-(void)recordManager:(JoyGemRecordManager*)manager intensity:(NSArray<NSNumber*>*)list timestamp:(NSTimeInterval)timestamp;
+
+/// 录音时的说话人区分
+/// - Parameters:
+/// - list: 强度数组
+/// - timestamp: 时间戳(ms)
+@optional
+-(void)recordManager:(JoyGemRecordManager*)manager speakers:(NSArray<JoyGemRecordSpeaker*>*)list;
+
@end
/// 现场录音的管理类
/// 是否正在暂停中
@property(nonatomic,assign) BOOL isPause;
+/// 是否开启声纹识别
+@property(nonatomic,assign) BOOL useVpr;
+
/// 实例对象
+ (instancetype)shared;
/// 恢复录音
- (void)resume;
+
+
@end
NS_ASSUME_NONNULL_END
/// - Parameters:
/// - data: 流数据 pcm格式
@required
--(void)siDeviceManager:(JoyGemSIDeviceManager*)manager didUpdateAudioData:(NSData*)data;
+-(void)siDeviceManager:(JoyGemSIDeviceManager*)manager didUpdateAudioData:(NSData*)data sampleRate:(NSUInteger)sampleRate;
///结束时的回调
/// - Parameters:
/// - Parameters:
/// - data: 流数据 pcm格式
@required
--(void)siManager:(JoyGemSIManager*)manager didUpdateAudioData:(NSData*)data;
+-(void)siManager:(JoyGemSIManager*)manager didUpdateAudioData:(NSData*)data sampleRate:(NSUInteger)sampleRate;
///录音状态切换时
-(void)siManager:(JoyGemSIManager*)manager stateChange:(JoyGemStateCode)code;
--- /dev/null
+//
+// JoyGemSubtitleManager.h
+// JoyGemAI
+//
+// Created by jjjs on 2025/10/17.
+//
+
+#import <Foundation/Foundation.h>
+#import <JoyGemAI/JoyGemEnum.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class JoyGemSubtitleManager;
+@class JoyGemAsrModel;
+
+/// 字幕翻译的委托
+@protocol JoyGemSubtitleManagerDelegate <NSObject>
+
+///录音文字识别时
+/// - Parameters:
+/// - model: asr识别的对象
+@required
+-(void)subtitleManager:(JoyGemSubtitleManager*)manager didUpdateRaw:(JoyGemAsrModel*)model;
+
+///录音文字翻译时
+/// - Parameters:
+/// - model: asr识别的对象
+@required
+-(void)subtitleManager:(JoyGemSubtitleManager*)manager didUpdateTranslate:(JoyGemAsrModel*)model;
+
+///结束时的回调
+/// - Parameters:
+/// - code: 录音结束的原因
+-(void)subtitleManagerStopped:(JoyGemSubtitleManager*)manager;
+
+///录音状态回调
+/// - Parameters:
+/// - code: 状态变化
+-(void)subtitleManager:(JoyGemSubtitleManager*)manager stateChange:(JoyGemStateCode)code;
+
+/// 录音错误回调
+/// - Parameters:
+/// - mode:错误代码
+-(void)subtitleManager:(JoyGemSubtitleManager*)manager onError:(JoyGemErrorCode)code;
+
+@end
+
+//字幕翻译的管理类
+@interface JoyGemSubtitleManager : NSObject
+
+/// 委托类
+/// - Description: 字幕翻译状态委托
+@property(nonatomic, weak) id<JoyGemSubtitleManagerDelegate> delegate;
+
+/// 耳机识别语言
+@property(nonatomic, strong) NSString* originalLanguage;
+
+/// 耳机输出语言
+@property(nonatomic, strong) NSString* targetLanguage;
+
+/// 是否正在录音
+@property(nonatomic, assign) BOOL isRecording;
+
+/// 是否支持字幕翻译功能
+@property(nonatomic, assign) BOOL support;
+
+
+/// 实例对象
++ (instancetype)shared;
+
+/// 开始字幕翻译
+- (void)start;
+
+/// 结束字幕翻译
+- (void)stop;
+
+@end
+
+NS_ASSUME_NONNULL_END
</data>
<key>Headers/JoyGemAI.h</key>
<data>
- 8NnRIJGe/ATf7a6jdPAfly29UC4=
+ 7HO7XUA3NdXWlvtUWyiYdTaaZ4M=
</data>
<key>Headers/JoyGemAIManager.h</key>
<data>
</data>
<key>Headers/JoyGemCallManager.h</key>
<data>
- BWJeSQyHYVE1TwiSgvXYhNdyJXI=
+ j3rnAsYkKzhbISebVqN00PCSeTk=
</data>
<key>Headers/JoyGemChatManager.h</key>
<data>
- I9SSrf6xzcSE2qKnMMmiSe9IV1I=
+ RmM4HQA4Ay7VLvTFWPw7s69sqBk=
</data>
<key>Headers/JoyGemDeviceManager.h</key>
<data>
</data>
<key>Headers/JoyGemModel.h</key>
<data>
- ZQ5Ie1ErW3QF78XXxz/ydxsSQ5E=
+ WP0CE48Wvoq3FJX4ldz90bcNpTw=
</data>
<key>Headers/JoyGemParam.h</key>
<data>
- SzxTsmYo/XvsD3Uz2iqjnRDkRnA=
+ fq/eezzfuJFDMFtH+PXLOMYQQCs=
</data>
<key>Headers/JoyGemRecordManager.h</key>
<data>
- pKeMD6Xg3Br6ZOWB/BxyqAicCfc=
+ GzBtgXRPoBuyG0YwvFmoGA8Yg20=
</data>
<key>Headers/JoyGemSIDeviceManager.h</key>
<data>
- Cn0/thrZRr+pJ+skbuRbfJJirmA=
+ aH1rBy/dU7IVJkpw/BVW3BvfNtk=
</data>
<key>Headers/JoyGemSIManager.h</key>
<data>
- UiwORLtCgzF+QX/hXpK2w63Yx4Q=
+ 363bn6W4/w8F3xEYQrU0LEVD/XM=
+ </data>
+ <key>Headers/JoyGemSubtitleManager.h</key>
+ <data>
+ fDUdqGo5wSc78huSI4OUK8Qr8AU=
</data>
<key>Info.plist</key>
<data>
<dict>
<key>hash2</key>
<data>
- nO8YFPNcoOIKUrpS9Pd4/T9V98f16ACbB7LFROxEPhQ=
+ AWVDZqJirRMWfVQc7FdxP1cjTTx2LWTSTwHDN0/ySRA=
</data>
</dict>
<key>Headers/JoyGemAIManager.h</key>
<dict>
<key>hash2</key>
<data>
- 4aqJC5wgayXjaD7XHafngtRL7PSKbqPdFujG27R7l4Q=
+ eKpRq8GaSD9YaI7Tfafc4BVLc/Vz2rS7cEcx8W6S+EI=
</data>
</dict>
<key>Headers/JoyGemChatManager.h</key>
<dict>
<key>hash2</key>
<data>
- v9CC1YyWez/AeMXpqpyouz+LNIkBqq147kBPoPYEVK8=
+ TrJNORF2yq5ZRf++PfcoxEwE0ic9W2VxQ+Nz7M7WKPM=
</data>
</dict>
<key>Headers/JoyGemDeviceManager.h</key>
<dict>
<key>hash2</key>
<data>
- rFoRM/fmnvDGWCISlMyWSioYrwvoetaKOU8WrgOCBNw=
+ 0kLzuK/HuYBvk9JPhWYbR2z7t1XSX83X5Am9ZD+vLXw=
</data>
</dict>
<key>Headers/JoyGemParam.h</key>
<dict>
<key>hash2</key>
<data>
- oXJjxfeaUmV4FnIP5DUB7MLED0QsP5hp0v0AqO8ttMs=
+ QKmLGG6IwVxT2QonEGJzea6rMbUX0PBNGoWpzqSNRbY=
</data>
</dict>
<key>Headers/JoyGemRecordManager.h</key>
<dict>
<key>hash2</key>
<data>
- C4rHebQlX6cKQWVmjh1cDetbe6cC+glcl2deSRF3r/o=
+ ep9EdHop9Qn0M9nCT7YwmHXxJh/XXCjBqtYjErKFd+8=
</data>
</dict>
<key>Headers/JoyGemSIDeviceManager.h</key>
<dict>
<key>hash2</key>
<data>
- bkQU7XO+s/wbDHlWf9VM2kvGxB5myT2n0bEp+ac6tHo=
+ x40L/ElpDJCMjpEBZZancy57toshn0DM6MOhkwnIMYQ=
</data>
</dict>
<key>Headers/JoyGemSIManager.h</key>
<dict>
<key>hash2</key>
<data>
- 5nObgPQx+QBZs8lbacCMnhBUJs629P/ZuVFJytUIV44=
+ NTKnVzoipmhtgLLzocc8J5kT4Q2qmUmVhZ3MgZUrbkA=
+ </data>
+ </dict>
+ <key>Headers/JoyGemSubtitleManager.h</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ jGDopDnKVm0z/p6dEPEs54wWT6iwbetpqbTXTk8zoAQ=
</data>
</dict>
<key>Modules/JoyGemAI.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo</key>
// https://dc-cloud-pub.tos-cn-guangzhou.volces.com/apple-app-site-association
// static let appStoreLink = "https://itunes.apple.com/cn/app/6738022284?mt=8"
static let appStoreLink = "https://itunes.apple.com/cn/app/6752551504?mt=8"
-
/// 版本检测
static let appStoreVersionURL = "https://itunes.apple.com/cn/lookup?id=6752551504"
+
+ /// 服务器地址
+ static let baseURL = "http://118.145.178.188:32000"
+// static let baseURL = "https://app-api.deepchords.com"
+ /// 用户隐私协议地址
+ static let yonghuBaseURL = "https://pub-tos.deepchords.com"
+
}
{
// return URL(string: "https://app-api.raisound.com")!
// http://118.145.178.188:32000/uc/country/list
- // return URL(string: "https://app-api.deepchords.com")!
- return "http://118.145.178.188:32000"
+// return "https://app-api.deepchords.com"
+// return "http://118.145.178.188:32000"
+ return DCConfig.baseURL
}
var url:String{
--- /dev/null
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "日历_(1)@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "日历_(1)@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
}
extension Chinese2EnglishVC:JoyGemSIDeviceManagerDelegate{
+
+
func siDeviceManagerStopped(_ manager: JoyGemSIDeviceManager, reason code: JoyGemRecordStopCode) {
Logger.debug("停止",code.rawValue)
if code == .passively {
}
refreshData()
}
-
- func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data) {
+ func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data, sampleRate: UInt) {
pcmData.append(data)
PcmSound.sharedInstance().feed(data)
}
+// func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data) {
+// pcmData.append(data)
+// PcmSound.sharedInstance().feed(data)
+// }
func siDeviceManager(_ manager: JoyGemSIDeviceManager, update mode: JoyGemRecordMode) {
var tmpList : [(String,UInt)] = []
var tmpOtherList : [(String,UInt)] = []
var otherPcm = Data()
-
+ deinit {
+ PcmSound.sharedInstance().stopAudioUnit()
+ PcmSound.sharedInstance().cleanup()
+ }
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension DialogTranslateVC : JoyGemSIManagerDelegate {
+
+
func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) {
if let model = dataList.last,model.send.isEmpty{
dataList.removeLast()
func siManagerStopped(_ manager: JoyGemSIManager) {
}
-
- func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data) {
+ func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data, sampleRate: UInt) {
PcmSound.sharedInstance().feed(data)
}
+// func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data) {
+// PcmSound.sharedInstance().feed(data)
+// }
func siManager(_ manager: JoyGemSIManager, onError code: JoyGemErrorCode) {
}
extension DialogTranslateVC : JoyGemSIDeviceManagerDelegate {
+
+
func siDeviceManagerStopped(_ manager: JoyGemSIDeviceManager, reason code: JoyGemRecordStopCode) {
if let model = dataList.last,model.send.isEmpty{
}).joined(separator: "\n")
refreshData()
}
-
- func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data) {
- //otherPcm.append(data)
+ func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data, sampleRate: UInt) {
PcmSound.sharedInstance().feed(data)
}
+// func siDeviceManager(_ manager: JoyGemSIDeviceManager, didUpdateAudioData data: Data) {
+// //otherPcm.append(data)
+// PcmSound.sharedInstance().feed(data)
+// }
func siDeviceManager(_ manager: JoyGemSIDeviceManager, update mode: JoyGemRecordMode) {
let dateLabel = UILabel()
let typeLabel = UILabel()
var longPressCallback:((_ cell:HistoryRecordCell)->Void)?
+ var collectStateChangeCallback:((_ cell:HistoryRecordCell)->Void)?
+ var supportLongPress = true
var model:RecordHistoryModel?{
didSet{
self.localModel = localModel
RecordCache.shared.add(localModel)
}
+ collectStateChangeCallback?(self)
}
@objc func longPressAction(_ gesture: UILongPressGestureRecognizer) {
+ if supportLongPress == false {
+ return
+ }
if gesture.state == .began {
bgView.backgroundColor = .lightGray.withAlphaComponent(0.5)
longPressCallback?(self)
let textField = UITextField()
let icon = UIImageView(image: UIImage(named: "search_2"), )
let cancelBtn = UIButton(type: .custom)
+ let calendarBtn = UIButton(type: .custom)
weak var delegate:RecordSearchBarDelegate?
override init(frame: CGRect) {
v.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
// v.backgroundColor = .yellow
}
-
+ calendarBtn.do { v in
+ v.setImage(UIImage(named: "calendar_icon"), for: .normal)
+ v.addTarget(self, action: #selector(calendarAction), for: .touchUpInside)
+ // v.backgroundColor = .yellow
+ }
bgView.do { v in
v.yc.setCornerRadius(8)
}
addSubviews([bgView,cancelBtn])
- bgView.addSubview(icon)
- bgView.addSubview(textField)
+ bgView.addSubviews([icon,textField,calendarBtn])
}
override func setupLayout() {
cancelBtn.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalToSuperview().inset(12)
}
+ calendarBtn.snp.makeConstraints { make in
+ make.right.equalToSuperview().inset(6)
+ make.centerY.equalToSuperview()
+ make.size.equalTo(18)
+ }
textField.snp.makeConstraints { make in
make.left.equalToSuperview().inset(34)
make.top.bottom.equalToSuperview()
- make.right.equalToSuperview()
+ make.right.equalTo(calendarBtn.snp.left).offset(-20)
}
}
@objc func cancelAction(){
App.shared.popPage(animated: true)
}
-
+ @objc func calendarAction(){
+
+ }
}
extension RecordSearchBar:UITextFieldDelegate{
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
override func setupViews() {
navBar.do { v in
v.title = "云存储空间".l10n
-// let items = [NavigationRightItem(image: UIImage(named: "filter"),target: self, action: #selector(filterAction)),NavigationRightItem(image: UIImage(named: "search"), target: self,action: #selector(searchAction))]
- let items = [NavigationRightItem(image: UIImage(named: "filter"),target: self, action: #selector(filterAction))]
+ let items = [NavigationRightItem(image: UIImage(named: "filter"),target: self, action: #selector(filterAction)),NavigationRightItem(image: UIImage(named: "search"), target: self,action: #selector(searchAction))]
+// let items = [NavigationRightItem(image: UIImage(named: "filter"),target: self, action: #selector(filterAction))]
v.setupRightViews(items)
}
refreshAction()
}
@objc func searchAction(){
-
+ let vc = CloudSearchVC(list: vm.list)
+ vc.deleteCallback = { [weak self] deleteList in
+ guard let self = self else{return}
+ for item in deleteList{
+ vm.list.removeAll(where: {$0.id == item.id})
+ }
+ tableView.reloadData()
+ }
+ vc.collectStateChangeCallback = { [weak self] in
+ self?.tableView.reloadData()
+ }
+ App.shared.goToPage(vc)
}
@objc func filterAction(){
let list = FiltterRecordType.alls.map({$0.title})
--- /dev/null
+//
+// CloudSearchVC.swift
+// ShenYin
+//
+// Created by liangshi on 2025/11/13.
+//
+
+import UIKit
+class CloudSearchVC: BaseVC{
+ let searchBar = RecordSearchBar()
+ let tableView = UITableView(frame: .zero, style: .plain)
+ var orginList = [RecordHistoryModel]()
+ var searchList = [RecordHistoryModel]()
+ var deleteCallback:((_ list:[RecordHistoryModel])->Void)?
+ var collectStateChangeCallback:(()->Void)?
+
+ init(list:[RecordHistoryModel]) {
+ orginList = list
+ searchList = orginList
+ super.init()
+ }
+
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ // Do any additional setup after loading the view.
+ }
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+ tableView.reloadData()
+ }
+ override func setupViews() {
+ navBar.addSubview(searchBar)
+ view.addSubviews([tableView])
+
+ searchBar.do { v in
+ v.delegate = self
+ }
+
+ tableView.do { v in
+ v.backgroundColor = .rgb(0xF7F7F7)
+ v.delegate = self
+ v.dataSource = self
+ v.estimatedRowHeight = 84
+ v.separatorStyle = .none
+ // v.isEditing = true
+ v.register(HistoryRecordCell.self, forCellReuseIdentifier: HistoryRecordCell.reuseID)
+ }
+
+ }
+ override func setupLayout() {
+ searchBar.snp.makeConstraints { make in
+ make.edges.equalToSuperview()
+ }
+ tableView.snp.makeConstraints { make in
+ make.left.right.equalToSuperview()
+ make.top.equalTo(kNavBtm)
+ make.bottom.equalToSuperview()
+ }
+ }
+ override func setupData() {
+
+ }
+
+}
+
+extension CloudSearchVC:UITableViewDelegate,UITableViewDataSource {
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return searchList.count
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let cell = HistoryRecordCell.cell(withTableView: tableView, indexPath: indexPath)
+ cell.supportLongPress = false
+ cell.model = searchList[indexPath.row]
+ cell.collectStateChangeCallback = { [weak self] cell in
+ self?.collectStateChangeCallback?()
+ }
+ return cell
+ }
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ let model = searchList[indexPath.row]
+ let vc = RecordDetailVC(remoteData: model)
+ vc.delegateCallback = {[weak self] in
+ guard let self = self else{return}
+ deleteCallback?([model])
+ searchList.remove(at: indexPath.row)
+ orginList.removeAll(where: {$0 === model})
+ tableView.reloadData()
+ }
+ App.shared.goToPage(vc)
+ }
+ func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
+ if editingStyle == .delete {
+ CustomAlert.showWith(title: "您要删除这条录音吗?".l10n, messgage: "删除后,这些记录将无法恢复。".l10n, cancelCallback: {}) {[weak self] in
+ guard let self = self else{return}
+ let model = searchList[indexPath.row]
+ searchList.remove(at: indexPath.row)
+ orginList.removeAll(where: {$0 === model})
+ tableView.deleteRows(at: [indexPath], with:.fade)
+ HttpClient.request( CommomApi.deleteVoiceRecord(recordId: model.id)) {[weak self](result:ResponseBaseModel,res) in
+ if result.isSuccess {
+ self?.deleteCallback?([model])
+ }
+ }
+ }
+ }
+ }
+ func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
+ return true
+ }
+ func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
+ return "删除".l10n
+ }
+
+}
+extension CloudSearchVC:RecordSearchBarDelegate{
+ func searchBarTextDidChange(_ toolBar: RecordSearchBar, _ text: String) {
+ if text.isEmpty {
+ searchList = orginList
+ }
+ else{
+ var list = [RecordHistoryModel]()
+ for item in orginList {
+ if item.name.uppercased() .contains(text.uppercased()) {
+ list.append(item)
+ }
+ }
+ searchList = list
+ }
+ tableView.reloadData()
+ }
+
+
+}
}
tableView.reloadData()
}
+ vc.collectStateChangeCallback = { [weak self] model in
+ guard let self = self else { return }
+ guard let data = list.filter({$0.createTime == model.createTime}).first else{
+ return
+ }
+ data.favorite = model.favorite
+ tableView.reloadData()
+ }
App.shared.goToPage(vc)
}
@objc func filterAction(){
var orginList = [RecordDetailModel]()
var searchList = [RecordDetailModel]()
var deleteCallback:((_ list:[RecordDetailModel])->Void)?
+ var collectStateChangeCallback:((RecordDetailModel)->Void)?
// init(data:[RecordDetailModel]) {
// list = data
// super.init()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = HistoryRecordCell.cell(withTableView: tableView, indexPath: indexPath)
+ cell.supportLongPress = false
cell.localModel = searchList[indexPath.row]
+ cell.collectStateChangeCallback = { [weak self] cell in
+ self?.collectStateChangeCallback?(cell.localModel!)
+ }
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
YLprogressHUD.showAutoHiden("已复制")
}
@objc func shareAction(){
- if let text = textView.text{
+ if let text = textView.text,text.isNotEmpty{
let vc = UIActivityViewController(activityItems: [text], applicationActivities: nil)
// 对于iPad,设置弹出源
// vc.popoverPresentationController?.sourceView = self.view
var translateList : [(String,UInt)] = []
var recordFilePath : String?
-
+ deinit {
+ PcmSound.sharedInstance().stopAudioUnit()
+ PcmSound.sharedInstance().cleanup()
+ }
override func setupViews() {
view.addSubviews([toolBar,textView,translateTextView])
}
extension RecordTranslateVC : JoyGemSIManagerDelegate {
+
+
func siManagerStopped(_ manager: JoyGemSIManager, reason code: JoyGemRecordStopCode) {
if (code == .actively) {
// YLprogressHUD.showAutoHiden("用户主动结束")
debugPrint("code: \(code)")
}
- func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data) {
+// func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data) {
+// PcmSound.sharedInstance().feed(data)
+// }
+ func siManager(_ manager: JoyGemSIManager, didUpdateAudioData data: Data, sampleRate: UInt) {
PcmSound.sharedInstance().feed(data)
}
-
func siManager(_ manager: JoyGemSIManager, onError code: JoyGemErrorCode) {
}
}
}
static func getDN(finished:@escaping ((_ mac:String?)->Void)) {
+// DispatchQueue.main.asyncAfter(deadline:.now() + 2) {
+// print("延迟2秒后执行")
+//
+// }
JoyGemAIManager.shared().device.getDN { deviceName in
finished(deviceName)
} error: { error in
- Logger.debug("\(error!.localizedDescription)")
+ Logger.debug("获取dn失败","\(error!.localizedDescription)")
finished(nil)
}
}
@MainActor static func start(){
// NFX.sharedInstance().start()
- DCConfig.appLanguage = LanuageCache.get()
+ DCConfig.appLanguage = LanuageCache.get()
IASignWithWeChat.shared.register()
LoginVM.fetchCountryList {result in
}
-
// AppUpdateVM.shared.checkedUpdate()
}
else{
App.shared.goToLogin()
}
-
-// let list = DeviceCache.shared.getDeviceList()
-// Logger.debug("缓存设备",list)
-// if list.count > 0 {
-// CacheManager.shared.currentDevice = list.first
-// BleManager.shared.startScanDevice()
-// App.shared.enter()
-// }
-
-// let model = RecordDetailModel()
-// model.type = .callingRecord
-// model.origin = "深圳今天下雨吗?明天下雨吗,后天下雨吗,未来一周下雨吗"
-// model.translate = ""
-// model.path = ""
-// model.name = ""
-// App.shared.goToPage(RecordDetailVC(record: model))
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
stopScanDevice()
Logger.debug("蓝牙已连接 \(peripheral)")
- DeviceCache.saveLastConnected(mac: connectPeripheral!.mac)
- DeviceCache.shared.add(connectPeripheral!.device.identifier.uuidString, mac: connectPeripheral!.mac)
+
peripheral.delegate = self
peripheral.discoverServices([CBUUID(string: kJoyGemAIServiceUUID)]);
- self.isConnetd = true
- didConnectDeviceCallback?(connectPeripheral!)
- NotificationManager.post(type: .deviceValidChange, objtect: nil)
- DeviceVM.updateOtaVersion()
+
+// DeviceCache.saveLastConnected(mac: connectPeripheral!.mac)
+// DeviceCache.shared.add(connectPeripheral!.device.identifier.uuidString, mac: connectPeripheral!.mac)
+// self.isConnetd = true
+// didConnectDeviceCallback?(connectPeripheral!)
+// NotificationManager.post(type: .deviceValidChange, objtect: nil)
+// DeviceVM.updateOtaVersion()
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
Logger.debug("peripheral: %@ failed to connect", peripheral)
}
func joyGemAuthSuccessCallback() {
+ Logger.debug("鉴权成功")
PcmSound.sharedInstance().setup(16000, withchannels: 1)
let options: AVAudioSession.CategoryOptions = [.allowBluetooth]
do {
} catch {
Logger.debug(error)
}
+
+ DeviceCache.saveLastConnected(mac: connectPeripheral!.mac)
+ DeviceCache.shared.add(connectPeripheral!.device.identifier.uuidString, mac: connectPeripheral!.mac)
+ self.isConnetd = true
+ didConnectDeviceCallback?(connectPeripheral!)
+ NotificationManager.post(type: .deviceValidChange, objtect: nil)
+ DeviceVM.updateOtaVersion()
}
func joyGemAction(_ action: JoyGemActionMode) {
let fileManager = FileManager.default
if let appSupportDirectory = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first {
let logDirectory = appSupportDirectory.appendingPathComponent("log")
-
// 检查 log 文件夹是否存在
if !fileManager.fileExists(atPath: logDirectory.path) {
do {
override func setupData() {
let lan = DCConfig.appLanguage.yonghuCode
- let fileUrl = "https://dc-cloud-pub.tos-cn-guangzhou.volces.com/protocol/\(lan)/\(type.rawValue).pdf"
+// let fileUrl = "https://dc-cloud-pub.tos-cn-guangzhou.volces.com/protocol/\(lan)/\(type.rawValue).pdf"
+ let fileUrl = DCConfig.yonghuBaseURL + "/protocol/\(lan)/\(type.rawValue).pdf"
+
self.dowmload(url:fileUrl)
}
func dowmload(url:String){
case .indonesian:
return kJoyGemAILocalizationID
case .russian:
- return kJoyGemAILocalizationRU
+ return kJoyGemAILocalizationRU_Target
case .malay:
return kJoyGemAILocalizationMS
}
var lanCode:String{
switch self {
case .chinse:
- return "zh-CN"
+ return "zh_CN"
case .Traditional_Chinese:
- return "zh-TW"
+ return "zh_TW"
case .english:
- return "en-US"
+ return "en_US"
case .japanese:
return "ja"
case .korean: