From: liangzhenying <867174124@qq.com> Date: Fri, 13 Mar 2026 09:16:10 +0000 (+0800) Subject: 改了画中画 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=8fd25e7c1f90bef6cc2c990d0d06991f900c4b0d;p=mirrors%2FShenYin.git 改了画中画 --- diff --git a/ShenYin/DeepChordsSdk/DCBleManager.swift b/ShenYin/DeepChordsSdk/DCBleManager.swift index 47fdc46..ca49314 100644 --- a/ShenYin/DeepChordsSdk/DCBleManager.swift +++ b/ShenYin/DeepChordsSdk/DCBleManager.swift @@ -35,7 +35,7 @@ public class DCBleManager{ full.append(data) full.addCrc() if let connectPeripheral = connectPeripheral, let writeCha = writeCha { -// DCLogger.debug("发",writeCha.uuid.uuidString,full.joined()) + DCLogger.debug("发",writeCha.uuid.uuidString,full.joined()) connectPeripheral.writeValue(full, for: writeCha, type:.withoutResponse) } } @@ -94,7 +94,7 @@ extension DCBleManager{ } else if data[1] == 0x0D{ let subData = data.subdata(in: 2..(_ target: CommomApi,completionHandler:@escaping ((T, String) -> Void)) { - Logger.debug("request => ",target.url) - guard let fileUrl = target.param["fileUrl"] as? URL else { - Logger.debug("后",target.param) - completionHandler(T(),"") - return - } - if FileManager.default.fileExists(atPath: fileUrl.path){ - Logger.debug("文件存在") - } - else{ - - Logger.debug("文件不存在") - completionHandler(T(),"") - return - } - Logger.debug(fileUrl) - let request = HTTP.POST(target.url, parameters: ["form-data": Upload(fileUrl: fileUrl)]) { response in - handleResponse(response, completionHandler: completionHandler) - } - request?.progress = { progress in - Logger.debug("上传进度: \(progress)%") - } - } +// static func uploadVoiceFile1(_ target: CommomApi,completionHandler:@escaping ((T, String) -> Void)) { +// Logger.debug("request => ",target.url) +// guard let fileUrl = target.param["fileUrl"] as? URL else { +// Logger.debug("后",target.param) +// completionHandler(T(),"") +// return +// } +// if FileManager.default.fileExists(atPath: fileUrl.path){ +// Logger.debug("文件存在") +// } +// else{ +// +// Logger.debug("文件不存在") +// completionHandler(T(),"") +// return +// } +// Logger.debug(fileUrl) +// let request = HTTP.POST(target.url, parameters: ["form-data": Upload(fileUrl: fileUrl)]) { response in +// handleResponse(response, completionHandler: completionHandler) +// } +// request?.progress = { progress in +// Logger.debug("上传进度: \(progress)%") +// } +// } static func uploadVoiceFile(_ target: CommomApi,completionHandler:@escaping ((T, String) -> Void)) { Logger.debug("request => ",target.url) guard let fileUrl = target.param["fileUrl"] as? URL else { diff --git a/ShenYin/ShenYin/Cache/SubTitleCache.swift b/ShenYin/ShenYin/Cache/SubTitleCache.swift index d2ede5d..9bb21a1 100644 --- a/ShenYin/ShenYin/Cache/SubTitleCache.swift +++ b/ShenYin/ShenYin/Cache/SubTitleCache.swift @@ -24,7 +24,7 @@ enum SubTitleShowType:Int,HandyJSONEnum{ } } } -enum SubTitleSFontType:Int,HandyJSONEnum{ +enum SubTitleFontType:Int,HandyJSONEnum{ case min = 12 case stand = 14 case big = 18 @@ -42,13 +42,30 @@ enum SubTitleSFontType:Int,HandyJSONEnum{ return .regular(CGFloat(self.rawValue)) } } +enum SubTitleSoureType:Int,HandyJSONEnum{ + case mic = 0 + case phone = 1 + var title:String{ + switch self { + case .mic: + return "麦克风".l10n + case .phone: + return "紧手机内部".l10n + } + } + var font:UIFont{ + return .regular(CGFloat(self.rawValue)) + } +} class SubTitleCacheModel:NSObject,HandyJSON,NSCoding { var lan_form:CCAiLanguage = .english var lan_to:CCAiLanguage = .chinse - var fontType:SubTitleSFontType = .stand + var soure:CCAiLanguage = .chinse + var fontType:SubTitleFontType = .stand var showType:SubTitleShowType = .all + var sourceType:SubTitleSoureType = .mic required public override init() { } func encode(with aCoder: NSCoder) { @@ -56,6 +73,7 @@ class SubTitleCacheModel:NSObject,HandyJSON,NSCoding { aCoder.encode(lan_to.rawValue, forKey: "lan_to") aCoder.encode(fontType.rawValue, forKey: "fontType") aCoder.encode(showType.rawValue, forKey: "showType") + aCoder.encode(sourceType.rawValue, forKey: "sourceType") } required init?(coder aDecoder: NSCoder) { @@ -63,11 +81,13 @@ class SubTitleCacheModel:NSObject,HandyJSON,NSCoding { let lanToString = aDecoder.decodeObject(forKey: "lan_to") as? String ?? "chinse" let fontTypeValue = aDecoder.decodeInteger(forKey: "fontType") let showTypeValue = aDecoder.decodeInteger(forKey: "showType") + let sourceTypeValue = aDecoder.decodeInteger(forKey: "sourceType") lan_form = CCAiLanguage(rawValue: lanFormString) ?? .english lan_to = CCAiLanguage(rawValue: lanToString) ?? .chinse - fontType = SubTitleSFontType(rawValue: fontTypeValue) ?? .stand + fontType = SubTitleFontType(rawValue: fontTypeValue) ?? .stand showType = SubTitleShowType(rawValue: showTypeValue) ?? .all + sourceType = SubTitleSoureType(rawValue: sourceTypeValue) ?? .mic } } diff --git a/ShenYin/ShenYin/Sdk/DcSdk/VC/AiFunctionVC.swift b/ShenYin/ShenYin/Sdk/DcSdk/VC/AiFunctionVC.swift index 5c1787e..6ba49cb 100644 --- a/ShenYin/ShenYin/Sdk/DcSdk/VC/AiFunctionVC.swift +++ b/ShenYin/ShenYin/Sdk/DcSdk/VC/AiFunctionVC.swift @@ -27,6 +27,8 @@ class AiFunctionVC:BaseVC{ override func setupViews() { navBar.title = device.productName navBar.backgroundColor = .color(.bgColor) +// navBar.rightView.isHidden = false +// navBar.rightView.setTitle("字幕翻译", for: .normal) view.backgroundColor = .color(.bgColor) // navBar.rightView.setImage(UIImage(named: "add_icon"), for: .normal) view.addSubview(bgView) @@ -57,6 +59,9 @@ class AiFunctionVC:BaseVC{ make.width.equalTo(kScreenWid) } } + override func navRightViewTapped() { + CCPage.gotoSubTitle() + } func updateUI(){ if CCBleManager.shared.isConected(mac: device.mac){ headerView.enable = true @@ -81,6 +86,7 @@ class AiFunctionVC:BaseVC{ CCPage.gotoFieldRecord() case .callRecord: CCPage.gotoCallRecord() +// CCPage.gotoSubTitle() case .chinese2English: CCPage.gotoChinese2Enlgish() case .recordTranslate: diff --git a/ShenYin/ShenYin/Sdk/DcSdk/VC/DcSubTitleVC.swift b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcSubTitleVC.swift new file mode 100644 index 0000000..e7f2a20 --- /dev/null +++ b/ShenYin/ShenYin/Sdk/DcSdk/VC/DcSubTitleVC.swift @@ -0,0 +1,184 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/3/5. +// + + +import UIKit +import AVKit + +@available(iOS 15.0, *) +class DcSubTitleVC: BaseVC { + let languageItem = LanguageSelectItem() + let toolBar = RecordBar() + let tableView = UITableView(frame: CGRectZero, style: .plain) + + + let pipManager = PipManager() + let sdk = DcSubTitle() + + override func viewDidLoad() { + super.viewDidLoad() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + let cache = SubTitleCache.shared.get() + languageItem.leftLabel.language = cache.lan_form + languageItem.rightLabel.language = cache.lan_to + } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + sdk.stopEarRecord() + } + override func setupViews() { + view.backgroundColor = .color(.bgColor) + navBar.do { v in + v.addSubview(languageItem) + v.rightView.isHidden = false +// v.rightView.setImage(UIImage(named: "set_right"), for: .normal) + navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal) + } + + view.addSubviews([toolBar,tableView]) + + toolBar.do { v in + v.delegate = self + v.leftItem.isHidden = true + v.rightItem.isHidden = true + } + languageItem.do { v in + v.languageChange = { (from,to) in + let cache = SubTitleCache.shared.get() + cache.lan_form = from + cache.lan_to = to + SubTitleCache.shared.save(cache) + } + } + tableView.do { v in + v.backgroundColor = .color(.bgColor) + v.delegate = self + v.dataSource = self + v.estimatedRowHeight = 60 + v.separatorStyle = .none + v.register(SubTitleCell.self, forCellReuseIdentifier: SubTitleCell.reuseID) + } +// let soureView = UIView() +// soureView.backgroundColor = .red +// view.addSubviews([soureView]) +// soureView.snp.makeConstraints { make in +// make.size.equalTo(10) +// make.center.equalToSuperview() +// } + pipManager.setupPictureInPicture(soureceView: self.view) + } + override func setupLayout() { + languageItem.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(80) + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().offset(5) + } + toolBar.snp.makeConstraints { make in + make.width.centerX.equalToSuperview() + make.height.equalTo(120) + make.bottom.equalToSuperview() + } + tableView.snp.makeConstraints { make in + make.top.equalToSuperview().inset(kNavBtm) + make.left.equalToSuperview() + make.width.equalTo(kScreenWid) + make.bottom.equalTo(toolBar.snp.top) + } + } + + override func setupData() { + setupSdk() + NotificationCenter.default.addObserver( + self, + selector: #selector(appDidEnterBackground(_:)), + name: UIApplication.willResignActiveNotification, + object: nil + ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(appWillEnterForeground(_:)), + name: UIApplication.didBecomeActiveNotification, + object: nil + ) + + } + override func navRightViewTapped() { + if !languageItem.enable { + return + } + let vc = SubTitleSetVC() + App.shared.goToPage(vc) + } + + @objc private func appDidEnterBackground(_ notification: Notification) { + if sdk.recording { + pipManager.startPictureInPicture() + } + } + + @objc private func appWillEnterForeground(_ notification: Notification) { + pipManager.stopPictureInPicture() + } + func setupSdk(){ + sdk.recordNeedReloadData = {[weak self] in + guard let self = self else{return} + if let model = sdk.dataList.last{ + pipManager.contentVC.updateRaw(model.send) + pipManager.contentVC.updateTrans(model.response) + tableView.reloadData() + if sdk.dataList.count > 0 { + tableView.scrollToRow(at: IndexPath(row: sdk.dataList.count - 1, section: 0), at: .bottom, animated: true) + } + } + } + } +} + + +@available(iOS 15.0, *) +extension DcSubTitleVC:RecordBarDelegate{ + func recordBarLanguageChange(_ bar: RecordBar) { + + sdk.stopEarRecord() + sdk.startEarRecord(from: languageItem.leftLabel.language, to: languageItem.rightLabel.language) + } + func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) { + if state == .start { + sdk.startEarRecord(from: languageItem.leftLabel.language, to: languageItem.rightLabel.language) + languageItem.enable = false + navBar.rightView.alpha = 0.5 + } + else if state == .stop{ + sdk.stopEarRecord() + languageItem.enable = true + navBar.rightView.alpha = 1 + } + } +} +@available(iOS 15.0, *) +extension DcSubTitleVC:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return sdk.dataList.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = SubTitleCell.cell(withTableView: tableView, indexPath: indexPath) + cell.data = sdk.dataList[indexPath.row] + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + + } +} diff --git a/ShenYin/ShenYin/Sdk/DcSdk/tool/CCConfig.swift b/ShenYin/ShenYin/Sdk/DcSdk/tool/CCConfig.swift index 7feb772..93e2c4d 100644 --- a/ShenYin/ShenYin/Sdk/DcSdk/tool/CCConfig.swift +++ b/ShenYin/ShenYin/Sdk/DcSdk/tool/CCConfig.swift @@ -6,36 +6,36 @@ // -//class CCConfig{ -// static var appLanguage = AppSupportLanguage.english -// -// static let wxAppID = "wxeb3291e720293abc" -// static let wxUniversalLink = "https://www.playboykq.com/app/" -// static let appStoreLink = "https://itunes.apple.com/cn/app/6757697090?mt=8" -// /// 版本检测 -// static let appStoreVersionURL = "https://itunes.apple.com/cn/lookup?id=6757697090" -// -// /// 服务器地址 -// static let baseURL = "https://app-api.deepchords.com" -// -// /// 用户隐私协议地址 -// static let yonghuBaseURL = "https://pub-cdn.playboykq.com" -// -//} - class CCConfig{ static var appLanguage = AppSupportLanguage.english - + static let wxAppID = "wxeb3291e720293abc" static let wxUniversalLink = "https://www.playboykq.com/app/" static let appStoreLink = "https://itunes.apple.com/cn/app/6757697090?mt=8" /// 版本检测 static let appStoreVersionURL = "https://itunes.apple.com/cn/lookup?id=6757697090" - + /// 服务器地址 - static let baseURL = "https://app-api.playboykq.com" - + static let baseURL = "https://app-api.deepchords.com" + /// 用户隐私协议地址 static let yonghuBaseURL = "https://pub-cdn.playboykq.com" - + } + +//class CCConfig{ +// static var appLanguage = AppSupportLanguage.english +// +// static let wxAppID = "wxeb3291e720293abc" +// static let wxUniversalLink = "https://www.playboykq.com/app/" +// static let appStoreLink = "https://itunes.apple.com/cn/app/6757697090?mt=8" +// /// 版本检测 +// static let appStoreVersionURL = "https://itunes.apple.com/cn/lookup?id=6757697090" +// +// /// 服务器地址 +// static let baseURL = "https://app-api.playboykq.com" +// +// /// 用户隐私协议地址 +// static let yonghuBaseURL = "https://pub-cdn.playboykq.com" +// +//} diff --git a/ShenYin/ShenYin/Sdk/DcSdk/tool/CCPage.swift b/ShenYin/ShenYin/Sdk/DcSdk/tool/CCPage.swift index 011e325..1183b6f 100644 --- a/ShenYin/ShenYin/Sdk/DcSdk/tool/CCPage.swift +++ b/ShenYin/ShenYin/Sdk/DcSdk/tool/CCPage.swift @@ -70,4 +70,16 @@ class CCPage{ App.shared.goToPage(DcDialogTranslateVC()) } } + static func gotoSubTitle(){ + if CCDevice.isJoy { + // App.shared.goToPage(JoyDialogTranslateVC()) + } + else{ + if #available(iOS 15.0, *) { + App.shared.goToPage(DcSubTitleVC()) + } else { + // Fallback on earlier versions + } + } + } } diff --git a/ShenYin/ShenYin/Sdk/DcSdk/tool/DcSubTitle.swift b/ShenYin/ShenYin/Sdk/DcSdk/tool/DcSubTitle.swift new file mode 100644 index 0000000..388e0f4 --- /dev/null +++ b/ShenYin/ShenYin/Sdk/DcSdk/tool/DcSubTitle.swift @@ -0,0 +1,97 @@ +// +// DcSubTitle.swift +// ShenYin +// +// Created by liangshi on 2026/3/13. +// + + +import UIKit +import DeepChordsSdk + +class DcSubTitle:NSObject{ + var dataList:[SubTitleCellModel] = [] + var recordNeedReloadData:(()->Void)? + var recording = false + override init() { + super.init() + setupSdk() + } + func setupSdk(){ + DCBleRecord.shared.asrSplitCallback = { [weak self] model in + guard let self = self else{return} +// Logger.debug("asr: \(model.text),index: \(model.index)") + let index = dataList.firstIndex (where: { item in + return item.index == model.index; + }) + if (index == nil) { + let item0 = SubTitleCellModel(send: model.text,index: model.index) + dataList.append(item0) + } else { + dataList[index!].send = model.text + } + recordNeedReloadData?() + } + DCBleRecord.shared.translateSplitCallback = { [weak self] model in + guard let self = self else{return} +// Logger.debug("tranlate: \(model.text),index: \(model.index)") + let index = dataList.firstIndex (where: { item in + return item.index == model.index; + }) + if (index == nil) { + let item0 = SubTitleCellModel(response: model.text,index: model.index) + dataList.append(item0) + } else { + dataList[index!].response = model.text + } + recordNeedReloadData?() + } +// DCBleRecord.shared.translateDataCallback = { data in +// PcmSound.sharedInstance().feed(data) +// } +// DCBleRecord.shared.codeCallback = { [weak self] code in +// guard let self = self else{return} +// if code == 20002 || code == 20003{ +// if let model = dataList.last,model.send.isEmpty{ +// dataList.removeLast() +// refreshData() +// } +// } +// } + DCBleRecord.shared.filePathCallBack = { [weak self] path in + guard let self = self else{return} + // self.recordBtn.isSelected = false + recording = false + } + } + + func startEarRecord(from:CCAiLanguage,to:CCAiLanguage) { + recording = true + dataList.removeAll() + recordNeedReloadData?() + + DCBleRecord.shared.fromLanguage = from.value + DCBleRecord.shared.toLanguage = to.value + DCBleRecord.shared.isTranslate = true + DCBleRecord.shared.isTTs = false + + if SubTitleCache.shared.get().sourceType == .mic{ + DCBleRecord.shared.start() + } + else{ + DCBleRecord.shared.start(type: 0x30) + } + + do { + try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: [.defaultToSpeaker]) + try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker) + } catch { + print(error) + } + } + + func stopEarRecord() { + DCBleRecord.shared.stop() + recording = false + } +} diff --git a/ShenYin/ShenYin/Sdk/JoySdk/VC/AiFunctionVC.swift b/ShenYin/ShenYin/Sdk/JoySdk/VC/AiFunctionVC.swift index cc8e189..0329b8b 100644 --- a/ShenYin/ShenYin/Sdk/JoySdk/VC/AiFunctionVC.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/VC/AiFunctionVC.swift @@ -84,13 +84,7 @@ class AiFunctionVC:BaseVC{ } } @objc func subTitleAction(){ - if #available(iOS 15.0, *) { -// if CCBleManager.shared.isConected(mac: device.mac){ - App.shared.goToPage(SubTitleVC()) -// } - } else { - // Fallback on earlier versions - } + CCPage.gotoSubTitle() } } diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleVC.swift b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoySubTitleVC.swift similarity index 62% rename from ShenYin/ShenYin/UI/Home/SubTitle/SubTitleVC.swift rename to ShenYin/ShenYin/Sdk/JoySdk/VC/JoySubTitleVC.swift index 159bcc7..4e33021 100644 --- a/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleVC.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/VC/JoySubTitleVC.swift @@ -1,31 +1,29 @@ +//// +//// Untitled.swift +//// ShenYin +//// +//// Created by liangshi on 2026/3/5. +//// // -// Untitled.swift -// ShenYin // -// Created by liangshi on 2026/3/5. -// - - import UIKit import AVKit import JoyGemAI -import SVProgressHUD @available(iOS 15.0, *) -class SubTitleVC: BaseVC { +class JoySubTitleVC: BaseVC { let languageItem = LanguageSelectItem() let toolBar = RecordBar() - var startButton : UIButton! - - var stopButton : UIButton! - let pipManager = PiPManager() - - - //画中画载体 - internal var sourceView : UIView = UIView() + let pipManager = PipManager() + var messageList : [(String,UInt)] = [] + var translateList : [(String,UInt)] = [] override func viewDidLoad() { super.viewDidLoad() } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) let cache = SubTitleCache.shared.get() @@ -34,7 +32,6 @@ class SubTitleVC: BaseVC { } override func setupViews() { view.backgroundColor = UIColor.white - sourceView.backgroundColor = .clear navBar.do { v in v.backgroundColor = .white v.addSubview(languageItem) @@ -42,7 +39,7 @@ class SubTitleVC: BaseVC { v.rightView.setImage(UIImage(named: "set_right"), for: .normal) } - view.addSubviews([sourceView,toolBar]) + view.addSubviews([toolBar]) toolBar.do { v in v.delegate = self @@ -50,14 +47,14 @@ class SubTitleVC: BaseVC { v.rightItem.isHidden = true } languageItem.do { v in - v.languageChange = {[weak self] (from,to) in - guard let self = self else { return } + v.languageChange = { (from,to) in let cache = SubTitleCache.shared.get() cache.lan_form = from cache.lan_to = to SubTitleCache.shared.save(cache) } } + pipManager.setupPictureInPicture(soureceView: self.navigationController!.view) } override func setupLayout() { languageItem.snp.makeConstraints { make in @@ -70,12 +67,6 @@ class SubTitleVC: BaseVC { make.height.equalTo(120) make.bottom.equalToSuperview() } - sourceView.snp.makeConstraints { make in - make.top.equalTo(kNavBtm + 20) - make.height.equalTo(200) - make.centerX.equalToSuperview() - make.width.equalTo(200) - } } override func setupData() { @@ -85,69 +76,68 @@ class SubTitleVC: BaseVC { } catch { print(error) } - JoyGemSubtitleManager.shared().delegate = self - // 初始化画中画管理器 - pipManager.setupPictureInPicture(sourceView: self.sourceView) - // 监听画中画状态变化 NotificationCenter.default.addObserver( self, - selector: #selector(pipStateChanged(_:)), - name: NSNotification.Name("PiPStateChanged"), + selector: #selector(appDidEnterBackground(_:)), + name: UIApplication.willResignActiveNotification, object: nil ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(appWillEnterForeground(_:)), + name: UIApplication.didBecomeActiveNotification, + object: nil + ) + } override func navRightViewTapped() { let vc = SubTitleSetVC() App.shared.goToPage(vc) } - - @objc private func pipStateChanged(_ notification: Notification) { - if let userInfo = notification.userInfo, - let isActive = userInfo["isActive"] as? Bool { - print("画中画状态: \(isActive ? "活跃" : "非活跃")") - } + + + @objc private func appDidEnterBackground(_ notification: Notification) { + pipManager.startPictureInPicture() + } + + @objc private func appWillEnterForeground(_ notification: Notification) { + pipManager.stopPictureInPicture() } } + + @available(iOS 15.0, *) -extension SubTitleVC:RecordBarDelegate{ +extension JoySubTitleVC:RecordBarDelegate{ func recordBarLanguageChange(_ bar: RecordBar) { - + } func recordBar(_ bar: RecordBar, stateChange state: RecordBarSate) { if state == .start { JoyGemSubtitleManager.shared().originalLanguage = languageItem.leftLabel.language.value JoyGemSubtitleManager.shared().targetLanguage = languageItem.rightLabel.language.value JoyGemSubtitleManager.shared().start() - - guard AVPictureInPictureController.isPictureInPictureSupported() else { - print("设备不支持画中画") - return - } - - pipManager.startPictureInPicture() } else if state == .stop{ JoyGemSubtitleManager.shared().stop() - - pipManager.stopPictureInPicture() } } } @available(iOS 15.0, *) -extension SubTitleVC : JoyGemSubtitleManagerDelegate { +extension JoySubTitleVC : JoyGemSubtitleManagerDelegate { func subtitleManager(_ manager: JoyGemSubtitleManager, didUpdateRaw model: JoyGemAsrModel) { debugPrint("text: \(model.text),index: \(model.index)") - pipManager.updateRaw(model.text,model.index) + updateRaw(model.text,model.index) } func subtitleManager(_ manager: JoyGemSubtitleManager, didUpdateTranslate model: JoyGemAsrModel) { debugPrint("translate text: \(model.text),index: \(model.index)") - pipManager.updateTrans(model.text,model.index) + updateTrans(model.text,model.index) } func subtitleManagerStopped(_ manager: JoyGemSubtitleManager) { @@ -170,4 +160,35 @@ extension SubTitleVC : JoyGemSubtitleManagerDelegate { } + func updateRaw(_ text: String,_ index: UInt) { + let msg = messageList.firstIndex (where: { (str,idx) in + return idx == index; + }) + if (msg == nil) { + messageList.append((text,index)) + } else { + messageList[msg!] = (text,index) + } + + let res = messageList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + pipManager.contentVC.updateRaw(res) + } + + func updateTrans(_ text: String,_ index: UInt) { + let msg = translateList.firstIndex (where: { (str,idx) in + return idx == index; + }) + if (msg == nil) { + translateList.append((text,index)) + } else { + translateList[msg!] = (text,index) + } + + let res = translateList.compactMap({ (str,idx) in + return str + }).joined(separator: "\n") + pipManager.contentVC.updateTrans(res) + } } diff --git a/ShenYin/ShenYin/Sdk/JoySdk/tool/CCPage.swift b/ShenYin/ShenYin/Sdk/JoySdk/tool/CCPage.swift index 28efa96..9da3a89 100644 --- a/ShenYin/ShenYin/Sdk/JoySdk/tool/CCPage.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/tool/CCPage.swift @@ -65,4 +65,16 @@ class CCPage{ // App.shared.goToPage(DcDialogTranslateVC()) } } + static func gotoSubTitle(){ + if CCDevice.isJoy { + if #available(iOS 15.0, *) { + App.shared.goToPage(JoySubTitleVC()) + } else { + // Fallback on earlier versions + } + } + else{ + + } + } } diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/PiPManager.swift b/ShenYin/ShenYin/UI/Home/SubTitle/PiPManager.swift deleted file mode 100644 index 5f7b0c5..0000000 --- a/ShenYin/ShenYin/UI/Home/SubTitle/PiPManager.swift +++ /dev/null @@ -1,191 +0,0 @@ -// -// Untitled.swift -// ShenYin -// -// Created by liangshi on 2026/3/5. -// - -import AVKit -import UIKit - -@available(iOS 15.0, *) -class PiPManager: NSObject, ObservableObject { - - var pipController: AVPictureInPictureController? - - var contentVC: DesktopLyricsContentVC! - - var messageList : [(String,UInt)] = [] - - var translateList : [(String,UInt)] = [] - - func setupPictureInPicture(sourceView : UIView) { - - contentVC = DesktopLyricsContentVC() - contentVC.preferredContentSize = CGSize(width: UIScreen.main.bounds.width, height: 240) // 画中画窗口大小 - - // 设置播放器 - pipController = AVPictureInPictureController(contentSource: AVPictureInPictureController.ContentSource( - activeVideoCallSourceView: sourceView, - contentViewController: contentVC - )) - pipController?.delegate = self - } - - - func startPictureInPicture() { - // 1. 检查场景状态 - if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene { - print("场景激活状态: \(scene.activationState.rawValue)") - print("场景状态描述: \(describeActivationState(scene.activationState))") - } else { - print("❌ 无法获取场景") - } - - pipController?.startPictureInPicture() - } - - private func describeActivationState(_ state: UIScene.ActivationState) -> String { - switch state { - case .unattached: return "unattached - 场景未连接到会话" - case .foregroundInactive: return "foregroundInactive - 前景但不活跃" - case .foregroundActive: return "foregroundActive - 前景且活跃 ✅" - case .background: return "background - 后台" - @unknown default: return "unknown" - } - } - - func stopPictureInPicture() { - pipController?.stopPictureInPicture() - } - - func updateRaw(_ text: String,_ index: UInt) { - let msg = messageList.firstIndex (where: { (str,idx) in - return idx == index; - }) - if (msg == nil) { - messageList.append((text,index)) - } else { - messageList[msg!] = (text,index) - } - - let res = messageList.compactMap({ (str,idx) in - return str - }).joined(separator: "\n") - contentVC?.updateRaw(res) - } - - func updateTrans(_ text: String,_ index: UInt) { - let msg = translateList.firstIndex (where: { (str,idx) in - return idx == index; - }) - if (msg == nil) { - translateList.append((text,index)) - } else { - translateList[msg!] = (text,index) - } - - let res = translateList.compactMap({ (str,idx) in - return str - }).joined(separator: "\n") - contentVC?.updateTrans(res) - } -} - -@available(iOS 15.0, *) -extension PiPManager: AVPictureInPictureControllerDelegate { - func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - print("画中画即将开始") - } - - func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - print("画中画已开始") - // 开始播放透明视频 - //player?.play() - } - - func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) { - print("画中画启动失败: \(error)") - } - - - func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - print("画中画已结束") -// messageList.removeAll() -// translateList.removeAll() - } -} -// 画中画内容视图控制器 -@available(iOS 15.0, *) -class DesktopLyricsContentVC : AVPictureInPictureVideoCallViewController { - var textView = UITextView() - var translateTextView = UITextView() - - override func viewDidLoad() { - super.viewDidLoad() - setupViews() - setupLayout() - } - func setupViews(){ - view.addSubviews([textView,translateTextView]) - view.backgroundColor = .clear - - textView.do { v in - v.textColor = UIColor.blue - v.text = "原文".l10n - v.font = UIFont.boldSystemFont(ofSize: 14) - v.backgroundColor = .clear - } - - translateTextView.do { v in - v.textColor = UIColor.orange - v.text = "译文".l10n - v.font = UIFont.boldSystemFont(ofSize: 14) - v.backgroundColor = .clear - } - } - func setupLayout(){ - let cache = SubTitleCache.shared.get() - - if cache.showType == .all { - textView.snp.makeConstraints { make in - make.top.equalToSuperview().inset(40) - make.leading.trailing.equalToSuperview() - make.bottom.equalTo(view.snp.centerY) - } - translateTextView.snp.makeConstraints { make in - make.leading.trailing.equalToSuperview() - make.top.equalTo(textView.snp.bottom) - make.height.equalTo(textView.snp.height) - } - textView.isHidden = false - translateTextView.isHidden = false - } - else if cache.showType == .from { - textView.snp.makeConstraints { make in - make.top.bottom.equalToSuperview().inset(40) - make.leading.trailing.equalToSuperview() - } - textView.isHidden = false - translateTextView.isHidden = true - } else { - translateTextView.snp.makeConstraints { make in - make.top.bottom.equalToSuperview().inset(40) - make.leading.trailing.equalToSuperview() - } - textView.isHidden = true - translateTextView.isHidden = false - } - - } - - func updateRaw(_ text: String) { - textView.text = text - textView.scrollRangeToVisible(NSRange(location: textView.text.count, length: 0)) - } - - func updateTrans(_ text : String) { - translateTextView.text = text - translateTextView.scrollRangeToVisible(NSRange(location: translateTextView.text.count, length: 0)) - } -} diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/PipContentVC.swift b/ShenYin/ShenYin/UI/Home/SubTitle/PipContentVC.swift new file mode 100644 index 0000000..62363ef --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/PipContentVC.swift @@ -0,0 +1,162 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/3/5. +// + +import AVKit +import UIKit + +@available(iOS 15.0, *) +class PipManager:NSObject,AVPictureInPictureControllerDelegate{ + var pipController: AVPictureInPictureController? + var contentVC: PipContentVC! + + + func setupPictureInPicture(soureceView:UIView) { + guard AVPictureInPictureController.isPictureInPictureSupported() else { + print("❌ 设备不支持画中画") + return + } + + contentVC = PipContentVC() + contentVC.preferredContentSize = CGSizeMake(kScreenWid, 200) + let contentSource = AVPictureInPictureController.ContentSource( + activeVideoCallSourceView:soureceView , + contentViewController: contentVC + ) + + pipController = AVPictureInPictureController(contentSource: contentSource) + pipController?.delegate = self + pipController?.canStartPictureInPictureAutomaticallyFromInline = false + } + + @objc func startPictureInPicture(){ + guard let pipController = pipController else { + print("❌ pipController 未初始化,无法启动画中画") + return + } + + if pipController.isPictureInPicturePossible { + print("✅ 画中画可以启动,准备开始...") + pipController.startPictureInPicture() + contentVC?.reset() + } else { + print("❌ 画中画当前无法启动") + } + } + @objc func stopPictureInPicture() { + guard let pipController = pipController else { + print("❌ pipController 未初始化,无法停止画中画") + return + } + + if pipController.isPictureInPictureActive { + print("✅ 画中画正在显示,准备停止...") + pipController.stopPictureInPicture() + print("✅ 画中画已停止") + } else { + print("⚠️ 画中画当前未显示") + } + } + + func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + print("画中画即将开始") + } + + func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + print("✅ 画中画已开始") + } + + func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) { + print("❌ 画中画启动失败: \(error)") + } + + func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + print("⏹️ 画中画即将结束") + } + + func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + print("🛑 画中画已结束") + } + +} + + + + + + + +// 画中画内容视图控制器 +@available(iOS 15.0, *) +class PipContentVC : AVPictureInPictureVideoCallViewController { + var textView = UITextView() + var translateTextView = UITextView() + + override func viewDidLoad() { + super.viewDidLoad() + view.addSubviews([textView,translateTextView]) + view.backgroundColor = .rgb(0x717171) + reset() + } + + func updateRaw(_ text: String) { + textView.text = text + textView.scrollRangeToVisible(NSRange(location: textView.text.count, length: 0)) + } + + func updateTrans(_ text : String) { + translateTextView.text = text + translateTextView.scrollRangeToVisible(NSRange(location: translateTextView.text.count, length: 0)) + } + func reset(){ + if (textView.superview == nil) { + return + } + let cache = SubTitleCache.shared.get() + + textView.do { v in + v.textColor = .rgb(0xD8D8D8) + v.font = cache.fontType.font + v.backgroundColor = .clear + } + + translateTextView.do { v in + v.textColor = .white + v.font = cache.fontType.font + v.backgroundColor = .clear + } + + if cache.showType == .all { + textView.snp.remakeConstraints { make in + make.top.equalToSuperview().inset(50) + make.leading.trailing.equalToSuperview().inset(16) + make.bottom.equalTo(view.snp.centerY) + } + translateTextView.snp.remakeConstraints { make in + make.leading.trailing.equalToSuperview().inset(16) + make.top.equalTo(textView.snp.bottom).offset(2) + make.bottom.equalToSuperview().inset(16) + } + textView.isHidden = false + translateTextView.isHidden = false + } + else if cache.showType == .from { + textView.snp.remakeConstraints { make in + make.top.bottom.equalToSuperview().inset(50) + make.leading.trailing.equalToSuperview().inset(16) + } + textView.isHidden = false + translateTextView.isHidden = true + } else { + translateTextView.snp.remakeConstraints { make in + make.top.bottom.equalToSuperview().inset(50) + make.leading.trailing.equalToSuperview().inset(16) + } + textView.isHidden = true + translateTextView.isHidden = false + } + } +} diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleCell.swift b/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleCell.swift new file mode 100644 index 0000000..cfde55a --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleCell.swift @@ -0,0 +1,143 @@ +// +// SubTitleCell.swift +// ShenYin +// +// Created by liangshi on 2026/3/13. +// + +class SubTitleCellModel{ + var send = "" + var response = "" + var index : Int = 0 + + init(send: String = "", response: String = "",index:Int = 0) { + self.send = send + self.response = response + self.index = index + } +} + + + + +class SubTitleCell:BaseTableViewCell{ + let bgView = UIView() + private let line = BrokenLineView() + let originalLabel = UILabel() + let translationLabel = UILabel() + let playIcon = UIImageView() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setupViews() { + contentView.backgroundColor = .clear + backgroundView?.backgroundColor = .clear + backgroundColor = .clear + + bgView.do { v in + v.yc.setCornerRadius(12) + v.backgroundColor = .color(.cellBg) + } + originalLabel.do { v in + v.font = .regular(14) + v.textColor = .color(.secondary) + v.numberOfLines = 0 + v.textAlignment = .left + } + translationLabel.do { v in + v.font = .regular(14) + v.textColor = .color(.first) + v.numberOfLines = 0 + v.textAlignment = .left + } + line.do { v in + } + playIcon.do { v in + v.image = UIImage(named: "play_animate_3") + v.animationImages = [UIImage(named: "play_animate_1")!,UIImage(named: "play_animate_2")!,UIImage(named: "play_animate_3")!] + v.animationDuration = 0.6 + v.animationRepeatCount = 0 + v.yc.addTap(target: self, action: #selector(playAction)) + v.isHidden = true + } + contentView.addSubview(bgView) + bgView.addSubviews([originalLabel,translationLabel,line,playIcon]) + } + + override func setupLayout() { + // bgView.snp.makeConstraints { make in + // make.top.bottom.equalToSuperview().inset(10) + // make.width.lessThanOrEqualTo(kScreenWid - 58) + // make.left.equalToSuperview().inset(18) + // } + // + originalLabel.snp.makeConstraints { make in + make.top.equalToSuperview().inset(14) + make.height.greaterThanOrEqualTo(15) + make.left.equalToSuperview().inset(12) + make.right.equalToSuperview().inset(50) + make.width.lessThanOrEqualTo(kScreenWid - 120) + } + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(12) + make.height.equalTo(1) + make.top.equalTo(originalLabel.snp.bottom).offset(15) + } + + translationLabel.snp.makeConstraints { make in + make.top.equalTo(line.snp.bottom).offset(15) + make.left.equalToSuperview().inset(12) + make.right.equalToSuperview().inset(12) + make.height.greaterThanOrEqualTo(15) + make.bottom.equalToSuperview().inset(14) + } + playIcon.snp.makeConstraints { make in + make.right.equalToSuperview().inset(12) + make.size.equalTo(24) + make.top.equalToSuperview().inset(12) + } + } + + var data:SubTitleCellModel!{ + didSet{ + originalLabel.text = data.send + translationLabel.text = data.response + +// if data.isPhone { +// bgView.snp.remakeConstraints { make in +// make.top.bottom.equalToSuperview().inset(10) +// // make.width.equalTo(kScreenWid - 78) +// make.right.equalToSuperview().inset(18) +// } +// bgView.backgroundColor = .color(.theme) +// originalLabel.textColor = UIColor.white.withAlphaComponent(0.8) +// translationLabel.textColor = .white +// } +// else{ + bgView.snp.remakeConstraints { make in + make.top.bottom.equalToSuperview().inset(10) + make.left.equalToSuperview().inset(18) + } + bgView.backgroundColor = .color(.cellBg) + originalLabel.textColor = .color(.secondary) + translationLabel.textColor = .color(.first) +// } + line.setNeedsLayout() + } + } + @objc func playAction(){ + if playIcon.isAnimating { + playIcon.stopAnimating() + } + else{ + playIcon.startAnimating() + } + } +} diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetCell.swift b/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetCell.swift index 7a52794..224097e 100644 --- a/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetCell.swift +++ b/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetCell.swift @@ -7,6 +7,7 @@ public enum SubTitleSetCellType: Int { case language + case soure case showType case font @@ -14,6 +15,8 @@ public enum SubTitleSetCellType: Int { switch self { case .language: return "语言".l10n + case .soure: + return "声源".l10n case .showType: return "字幕展示内容".l10n case .font: @@ -37,6 +40,9 @@ class SubTitleSetCell: BaseTableViewCell { if type == .language { valueLabel.text = model.lan_form.title + "-" + model.lan_to.title } + else if type == .soure { + valueLabel.text = model.sourceType.title + } else if type == .showType { valueLabel.text = model.showType.title } diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetVC.swift b/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetVC.swift index d35a33c..9f72411 100644 --- a/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetVC.swift +++ b/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleSetVC.swift @@ -8,7 +8,7 @@ import UIKit class SubTitleSetVC: BaseVC{ let tableView = UITableView(frame: .zero, style: .plain) - var list:[SubTitleSetCellType] = [.language,.showType,.font] + var list:[SubTitleSetCellType] = [.language,.soure,.showType,.font] let imgPicker = ImagePickerManager() override func viewDidLoad() { @@ -62,6 +62,8 @@ extension SubTitleSetVC:UITableViewDelegate,UITableViewDataSource { switch data { case .language: languageAction() + case .soure: + sourceTypeAction() case .showType: showTypeAction() case .font: @@ -89,6 +91,20 @@ extension SubTitleSetVC{ tableView.reloadData() } } + + @objc func sourceTypeAction(){ + let cache = SubTitleCache.shared.get() + let list:[SubTitleSoureType] = [.mic,.phone] + let picker = CustomPicker(title: "声音源".l10n, list: list.map{$0.title}) + picker.selectedIndex = list.firstIndex(where: {cache.sourceType.title == $0.title})! + picker.show() + picker.didSelected = {[weak self] (index,value) in + guard let self = self else { return } + cache.sourceType = list.first(where: {$0.title == value})! + SubTitleCache.shared.save(cache) + tableView.reloadData() + } + } @objc func showTypeAction(){ let cache = SubTitleCache.shared.get() let list:[SubTitleShowType] = [.all,.from,.to] @@ -104,7 +120,7 @@ extension SubTitleSetVC{ } @objc func fontAction(){ let cache = SubTitleCache.shared.get() - let list:[SubTitleSFontType] = [.min,.stand,.big] + let list:[SubTitleFontType] = [.min,.stand,.big] let picker = CustomPicker(title: "字体大小".l10n, list: list.map{$0.title}) picker.selectedIndex = list.firstIndex(where: {cache.fontType.title == $0.title})! picker.show() diff --git a/ShenYin/ShenYin/UI/common/LogVC.swift b/ShenYin/ShenYin/UI/common/LogVC.swift index 5511b90..d713487 100644 --- a/ShenYin/ShenYin/UI/common/LogVC.swift +++ b/ShenYin/ShenYin/UI/common/LogVC.swift @@ -6,6 +6,7 @@ // import UIKit +import DeepChordsSdk class LogVC: BaseVC { var textView = UITextView() @@ -32,7 +33,12 @@ class LogVC: BaseVC { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - textView.text = Logger.logs.joined() + if CCDevice.isJoy { + + } + else{ + textView.text = DCLogger.logs.joined() + } let range = NSMakeRange(textView.text.count - 1, 1) textView.scrollRangeToVisible(range) } diff --git a/ShenYin/ShenYin/UI/common/pop/LanguagePicker/LanguageSelectItem.swift b/ShenYin/ShenYin/UI/common/pop/LanguagePicker/LanguageSelectItem.swift index 19aa902..6ce55dd 100644 --- a/ShenYin/ShenYin/UI/common/pop/LanguagePicker/LanguageSelectItem.swift +++ b/ShenYin/ShenYin/UI/common/pop/LanguagePicker/LanguageSelectItem.swift @@ -12,6 +12,12 @@ class LanguageSelectItem: BaseView { let switcIcon = UIImageView() var languageChange:((_ from:CCAiLanguage,_ to:CCAiLanguage)->Void)? + var enable:Bool = true{ + didSet{ + self.alpha = enable ? 1:0.5 + } + } + override func setupViews() { addSubviews([bottonView]) bottonView.addSubviews([leftLabel,rightLabel,switcIcon]) @@ -58,6 +64,9 @@ class LanguageSelectItem: BaseView { } } @objc func switchAction(){ + if !enable { + return + } let text1 = leftLabel.language let text2 = rightLabel.language leftLabel.language = text2 @@ -67,6 +76,9 @@ class LanguageSelectItem: BaseView { languageChange?(leftLabel.language,rightLabel.language) } @objc func leftAction(){ + if !enable { + return + } let v = LanguagePickerView(languages: CCAiLanguage.all,isLeft: true,left: leftLabel.language,right: rightLabel.language) v.selectedIndex = v.list.firstIndex(where: {$0.title == leftLabel.language.title}) ?? 0 v.show() @@ -84,6 +96,9 @@ class LanguageSelectItem: BaseView { } } @objc func rightAction(){ + if !enable { + return + } let v = LanguagePickerView(languages: CCAiLanguage.all,isLeft: false,left: leftLabel.language,right: rightLabel.language) v.selectedIndex = v.list.firstIndex(where: {$0.title == rightLabel.language.title}) ?? 0 v.show()