From: liangzhenying <867174124@qq.com> Date: Fri, 6 Mar 2026 10:03:32 +0000 (+0800) Subject: 加了画中画 X-Git-Url: http://git.snicker.com.cn/Users/apple/Desktop/static/gitweb.css?a=commitdiff_plain;h=2187f6925cf091e27ae45bdeb21d4ac5bc05b901;p=mirrors%2FShenYin.git 加了画中画 --- diff --git a/ShenYin/DeepChordsSdk/DCSdk.swift b/ShenYin/DeepChordsSdk/DCSdk.swift index b6a2a7e..8238e97 100644 --- a/ShenYin/DeepChordsSdk/DCSdk.swift +++ b/ShenYin/DeepChordsSdk/DCSdk.swift @@ -25,7 +25,7 @@ public class DCSdk{ // StarburstConfig.shared().sdkFolder = NSHomeDirectory() + "/Documents/DeepChordsSdk" // 日志路径 // StarburstConfig.shared().logPath = logFilePath() - StarburstConfig.shared().logPath = nil +// StarburstConfig.shared().logPath = nil //是否下发文字到耳机仓 StarburstConfig.shared().enableTextSyncToDevice = false //主从耳机切换是否同步sdk信息到耳机 diff --git a/ShenYin/ShenYin.xcodeproj/project.pbxproj b/ShenYin/ShenYin.xcodeproj/project.pbxproj index a8d96c3..d74af2a 100644 --- a/ShenYin/ShenYin.xcodeproj/project.pbxproj +++ b/ShenYin/ShenYin.xcodeproj/project.pbxproj @@ -17,8 +17,8 @@ 644D1BB5EDD3EA1D77C57786 /* Pods_DeepChordsSdk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 248D8B53B5A178ACA5867275 /* Pods_DeepChordsSdk.framework */; }; 7666589AAA94A323B6090425 /* Pods_playboy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4F1BB919D567C3B6B0EBAA7 /* Pods_playboy.framework */; }; 7A6613B4B617D6E71D34944C /* Pods_ShenYin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 90FF0C8B0D4D6AE0933FA672 /* Pods_ShenYin.framework */; }; - 817510A595D00CAD05E3772C /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; - F2E449CAD5874BE652A69B50 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; + 817510A595D00CAD05E3772C /* (null) in Frameworks */ = {isa = PBXBuildFile; }; + F2E449CAD5874BE652A69B50 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -538,6 +538,11 @@ Sdk/JoySdk/VC/JoyTctyVC.swift, Sdk/JoySdk/VC/JoyXclyVC.swift, Sdk/JoySdk/VC/ModeVC.swift, + UI/Home/SubTitle/LanguagePickerView1.swift, + UI/Home/SubTitle/PiPManager.swift, + UI/Home/SubTitle/PipPicker.swift, + UI/Home/SubTitle/PipSetView.swift, + UI/Home/SubTitle/SubTitleVC.swift, ); target = 3FD1DFE32F0E00BF00A62733 /* playboy */; }; @@ -551,22 +556,31 @@ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ - 3FCC8B152F1E0280004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" build phase */ = { + 3FCC8B152F1E0280004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" phase from "ShenYin" target */ = { isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet; + attributesByRelativePath = { + Sdk/JoySdk/tool/JoyGemAI.framework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); + }; buildPhase = 3FEFE9092F1B252000D82F17 /* Embed Frameworks */; membershipExceptions = ( Sdk/JoySdk/tool/JoyGemAI.framework, ); }; - 3FCC8B1A2F1E030E004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" build phase */ = { + 3FCC8B1A2F1E030E004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" phase from "playboy" target */ = { isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet; + attributesByRelativePath = { + Sdk/DcSdk/tool/JLAudioUnitKit.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); + }; buildPhase = 3FD1DFEF2F0E00BF00A62733 /* Embed Frameworks */; membershipExceptions = ( Sdk/DcSdk/tool/JLAudioUnitKit.xcframework, ); }; - 3FFB77C62F17A11100EF9A60 /* Exceptions for "DeepChordsSdk" folder in "Embed Frameworks" build phase */ = { + 3FFB77C62F17A11100EF9A60 /* Exceptions for "DeepChordsSdk" folder in "Embed Frameworks" phase from "DeepChordsSdk" target */ = { isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet; + attributesByRelativePath = { + StarburstSdk.framework = (CodeSignOnCopy, RemoveHeadersOnCopy, ); + }; buildPhase = 3FFB77C52F17A11100EF9A60 /* Embed Frameworks */; membershipExceptions = ( StarburstSdk.framework, @@ -578,7 +592,7 @@ 0D54E0F02F0A61E600A80EE7 /* DeepChordsSdk */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( - 3FFB77C62F17A11100EF9A60 /* Exceptions for "DeepChordsSdk" folder in "Embed Frameworks" build phase */, + 3FFB77C62F17A11100EF9A60 /* Exceptions for "DeepChordsSdk" folder in "Embed Frameworks" phase from "DeepChordsSdk" target */, 3FFB77C82F17A14C00EF9A60 /* Exceptions for "DeepChordsSdk" folder in "playboy" target */, ); path = DeepChordsSdk; @@ -588,9 +602,9 @@ isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( 3F887BAE2E38D8DD00C98658 /* Exceptions for "ShenYin" folder in "ShenYin" target */, - 3FCC8B152F1E0280004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" build phase */, + 3FCC8B152F1E0280004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" phase from "ShenYin" target */, 3FD1DFF52F0E00BF00A62733 /* Exceptions for "ShenYin" folder in "playboy" target */, - 3FCC8B1A2F1E030E004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" build phase */, + 3FCC8B1A2F1E030E004C2A13 /* Exceptions for "ShenYin" folder in "Embed Frameworks" phase from "playboy" target */, 3F03696C2F2B604900F3CB95 /* Exceptions for "ShenYin" folder in "playboy copy" target */, ); path = ShenYin; @@ -619,7 +633,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F2E449CAD5874BE652A69B50 /* BuildFile in Frameworks */, + F2E449CAD5874BE652A69B50 /* (null) in Frameworks */, 7A6613B4B617D6E71D34944C /* Pods_ShenYin.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -629,7 +643,7 @@ buildActionMask = 2147483647; files = ( 3FFB77C12F179F3100EF9A60 /* DeepChordsSdk.framework in Frameworks */, - 817510A595D00CAD05E3772C /* BuildFile in Frameworks */, + 817510A595D00CAD05E3772C /* (null) in Frameworks */, 30B13DEF1AA0E4C54D873276 /* Pods_playboy.framework in Frameworks */, 7666589AAA94A323B6090425 /* Pods_playboy.framework in Frameworks */, ); @@ -880,10 +894,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ShenYin/Pods-ShenYin-resources-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ShenYin/Pods-ShenYin-resources-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ShenYin/Pods-ShenYin-resources.sh\"\n"; @@ -919,10 +937,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-playboy/Pods-playboy-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-playboy/Pods-playboy-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-playboy/Pods-playboy-frameworks.sh\"\n"; @@ -958,10 +980,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ShenYin/Pods-ShenYin-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ShenYin/Pods-ShenYin-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ShenYin/Pods-ShenYin-frameworks.sh\"\n"; @@ -1498,7 +1524,7 @@ CODE_SIGN_ENTITLEMENTS = ShenYin/PLAYBOY.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 202603031106; + CURRENT_PROJECT_VERSION = 202603051206; DEVELOPMENT_TEAM = JKLJ58J33Q; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1511,7 +1537,7 @@ ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "ShenYin/ShenYin copy-Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = PLAYBOY; + INFOPLIST_KEY_CFBundleDisplayName = "PLAYBOY眼镜"; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "扫描附近蓝牙设备需要获得权限"; INFOPLIST_KEY_NSCameraUsageDescription = "修改用户头像、提交意见反馈需要访问相册使用摄像头"; @@ -1530,7 +1556,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 0.01; PRODUCT_BUNDLE_IDENTIFIER = com.ios.playboy; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1554,7 +1580,7 @@ CODE_SIGN_ENTITLEMENTS = ShenYin/PLAYBOY.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 202603031106; + CURRENT_PROJECT_VERSION = 202603051206; DEVELOPMENT_TEAM = JKLJ58J33Q; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1567,7 +1593,7 @@ ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "ShenYin/ShenYin copy-Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = PLAYBOY; + INFOPLIST_KEY_CFBundleDisplayName = "PLAYBOY眼镜"; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "扫描附近蓝牙设备需要获得权限"; INFOPLIST_KEY_NSCameraUsageDescription = "修改用户头像、提交意见反馈需要访问相册使用摄像头"; @@ -1586,7 +1612,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 0.01; PRODUCT_BUNDLE_IDENTIFIER = com.ios.playboy; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/ShenYin/ShenYin.xcodeproj/xcshareddata/xcschemes/playboy.xcscheme b/ShenYin/ShenYin.xcodeproj/xcshareddata/xcschemes/playboy.xcscheme new file mode 100644 index 0000000..2cabc21 --- /dev/null +++ b/ShenYin/ShenYin.xcodeproj/xcshareddata/xcschemes/playboy.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Contents.json new file mode 100644 index 0000000..3fd78ab --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Slice 49@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Slice 49@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Slice 49@2x.png b/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Slice 49@2x.png new file mode 100644 index 0000000..f21e71b Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Slice 49@2x.png differ diff --git a/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Slice 49@3x.png b/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Slice 49@3x.png new file mode 100644 index 0000000..ab93842 Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/device/pip_close.imageset/Slice 49@3x.png differ diff --git a/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Contents.json new file mode 100644 index 0000000..dcf07d8 --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Slice 47@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Slice 47@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Slice 47@2x.png b/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Slice 47@2x.png new file mode 100644 index 0000000..4b11b18 Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Slice 47@2x.png differ diff --git a/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Slice 47@3x.png b/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Slice 47@3x.png new file mode 100644 index 0000000..781374a Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/device/pip_set.imageset/Slice 47@3x.png differ diff --git a/ShenYin/ShenYin/Sdk/JoySdk/VC/HomeDeviceVC.swift b/ShenYin/ShenYin/Sdk/JoySdk/VC/HomeDeviceVC.swift index 03dc84d..9743358 100644 --- a/ShenYin/ShenYin/Sdk/JoySdk/VC/HomeDeviceVC.swift +++ b/ShenYin/ShenYin/Sdk/JoySdk/VC/HomeDeviceVC.swift @@ -173,7 +173,13 @@ extension HomeDeviceVC:UICollectionViewDataSource,UICollectionViewDelegate,UICol } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let model = DeviceVM.shared.dataList[indexPath.row] - App.shared.goToPage(AiFunctionVC(device: model)) +// App.shared.goToPage(AiFunctionVC(device: model)) + + if #available(iOS 15.0, *) { + App.shared.goToPage(SubTitleVC()) + } else { + // Fallback on earlier versions + } } func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView{ diff --git a/ShenYin/ShenYin/ShenYin copy-Info.plist b/ShenYin/ShenYin/ShenYin copy-Info.plist index 8ca32a3..936acd4 100644 --- a/ShenYin/ShenYin/ShenYin copy-Info.plist +++ b/ShenYin/ShenYin/ShenYin copy-Info.plist @@ -51,9 +51,9 @@ bluetooth-central audio - UIViewControllerBasedStatusBarAppearance - UIDesignRequiresCompatibility + UIViewControllerBasedStatusBarAppearance + diff --git a/ShenYin/ShenYin/UI/Base/BaseTC.swift b/ShenYin/ShenYin/UI/Base/BaseTC.swift index ebd434e..a6716ae 100644 --- a/ShenYin/ShenYin/UI/Base/BaseTC.swift +++ b/ShenYin/ShenYin/UI/Base/BaseTC.swift @@ -19,7 +19,7 @@ class BaseTC:UITabBarController{ func setupUI(){ if CCDevice.isJoy{ let homeVC = HomeDeviceVC() -// let homeVC = CustomerVC() + let modeVC = ModeVC() let sleepVC = SleepVC() diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/LanguagePickerView1.swift b/ShenYin/ShenYin/UI/Home/SubTitle/LanguagePickerView1.swift new file mode 100644 index 0000000..636af0b --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/LanguagePickerView1.swift @@ -0,0 +1,306 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/3/5. +// + +import UIKit +import JoyGemAI +@available(iOS 15.0, *) +// MARK: - 数据模型 +struct LanguageModel { + + let code: String + let name: String + let nativeName: String + + static let chinese = LanguageModel(code: kJoyGemAILocalizationZH, name: "Chinese", nativeName: "中文") + static let english = LanguageModel(code: kJoyGemAILocalizationEN, name: "English", nativeName: "英语") + static let japanese = LanguageModel(code: kJoyGemAILocalizationJA, name: "Japanese", nativeName: "日本語") + static let korean = LanguageModel(code: kJoyGemAILocalizationKO, name: "Korean", nativeName: "韩语") + static let french = LanguageModel(code: kJoyGemAILocalizationFR, name: "French", nativeName: "法语") + static let spanish = LanguageModel(code: kJoyGemAILocalizationES, name: "Spanish", nativeName: "西班牙语") + static let portuguese = LanguageModel(code: kJoyGemAILocalizationPT, name: "Portuguese", nativeName: "葡萄牙语") + static let indonesia = LanguageModel(code: kJoyGemAILocalizationID, name: "Indonesia", nativeName: "印尼语") + static let russian_ori = LanguageModel(code: kJoyGemAILocalizationRU_Original, name: "Russian", nativeName: "俄语") + static let russian_tar = LanguageModel(code: kJoyGemAILocalizationRU_Target, name: "Russian", nativeName: "俄语") + static let malay = LanguageModel(code: kJoyGemAILocalizationMS, name: "Malay", nativeName: "马来语") + static let german = LanguageModel(code: kJoyGemAILocalizationDE, name: "German", nativeName: "德语") + static let filipino = LanguageModel(code: kJoyGemAILocalizationFIL, name: "Filipino", nativeName: "菲律宾语") + static let thai = LanguageModel(code: kJoyGemAILocalizationTH, name: "Thai", nativeName: "泰语") + static let arabic = LanguageModel(code: kJoyGemAILocalizationAR, name: "Arabic", nativeName: "阿拉伯语") + static let vietnamese = LanguageModel(code: kJoyGemAILocalizationVI, name: "Vietnamese", nativeName: "越南语") +} + +// 语言选择器的委托协议 +@available(iOS 15.0, *) +protocol LanguagePickerView1Delegate: AnyObject { + + func languagePickerView(_ pickerView: LanguagePickerView1, + didSelectSourceLanguage source: LanguageModel, + targetLanguage target: LanguageModel) + + func languagePickerViewDidTapSwap(_ pickerView: LanguagePickerView1) +} +@available(iOS 15.0, *) +class LanguagePickerView1: UIView { + + // MARK: - UI 组件 + private let sourcePickerView = UIPickerView() + private let targetPickerView = UIPickerView() + private let swapButton = UIButton(type: .system) + private let sourceLabel = UILabel() + private let targetLabel = UILabel() + + // MARK: - 属性 + weak var delegate: LanguagePickerView1Delegate? + + var selectedSourceLanguage: LanguageModel? { + didSet { updateSelection() } + } + + var selectedTargetLanguage: LanguageModel? { + didSet { updateSelection() } + } + + var languages: [LanguageModel] = [ + .chinese, .english, .japanese,.korean,.french,.spanish,.portuguese,.indonesia,.russian_ori,.malay,.german,.filipino,.thai,.arabic,.vietnamese + ] + + // MARK: - 初始化 + override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + setupDefaults() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupUI() + setupDefaults() + } + + // MARK: - UI 设置 + private func setupUI() { + backgroundColor = .systemBackground + + setupLabels() + setupPickerViews() + setupSwapButton() + setupConstraints() + } + + private func setupLabels() { + sourceLabel.text = "源语言" + sourceLabel.font = UIFont.boldSystemFont(ofSize: 16) + sourceLabel.textAlignment = .center + addSubview(sourceLabel) + + targetLabel.text = "目标语言" + targetLabel.font = UIFont.boldSystemFont(ofSize: 16) + targetLabel.textAlignment = .center + addSubview(targetLabel) + } + + private func setupPickerViews() { + // 源语言选择器 + sourcePickerView.translatesAutoresizingMaskIntoConstraints = false + sourcePickerView.dataSource = self + sourcePickerView.delegate = self + sourcePickerView.tag = 0 + addSubview(sourcePickerView) + + // 目标语言选择器 + targetPickerView.translatesAutoresizingMaskIntoConstraints = false + targetPickerView.dataSource = self + targetPickerView.delegate = self + targetPickerView.tag = 1 + addSubview(targetPickerView) + } + + private func setupSwapButton() { + swapButton.translatesAutoresizingMaskIntoConstraints = false + swapButton.setImage(UIImage(systemName: "arrow.left.arrow.right"), for: .normal) + swapButton.tintColor = .systemBlue + swapButton.backgroundColor = .systemGray6 + swapButton.layer.cornerRadius = 20 + swapButton.addTarget(self, action: #selector(swapButtonTapped), for: .touchUpInside) + addSubview(swapButton) + } + + private func setupConstraints() { + sourceLabel.snp.makeConstraints { make in + make.top.equalToSuperview().offset(8) + make.leading.equalToSuperview().offset(20) + make.trailing.equalTo(swapButton.snp.leading).offset(-20) + make.height.equalTo(20) + } + + targetLabel.snp.makeConstraints { make in + make.top.equalToSuperview().offset(8) + make.trailing.equalToSuperview().offset(-20) + make.leading.equalTo(swapButton.snp.trailing).offset(20) + make.height.equalTo(20) + } + + sourcePickerView.snp.makeConstraints { make in + make.top.equalTo(sourceLabel.snp.bottom).offset(8) + make.leading.equalToSuperview().offset(20) + make.trailing.equalTo(swapButton.snp.leading).offset(-20) + make.bottom.equalToSuperview().offset(-8) + } + + targetPickerView.snp.makeConstraints { make in + make.top.equalTo(targetLabel.snp.bottom).offset(8) + make.trailing.equalToSuperview().offset(-20) + make.leading.equalTo(swapButton.snp.trailing).offset(20) + make.bottom.equalToSuperview().offset(-8) + } + + swapButton.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.centerY.equalTo(sourcePickerView.snp.centerY) + make.height.equalTo(40) + make.width.equalTo(40) + } + } + + private func setupDefaults() { + // 设置默认选择 + if let chineseIndex = languages.firstIndex(where: { $0.code == "zh" }) { + sourcePickerView.selectRow(chineseIndex, inComponent: 0, animated: false) + selectedSourceLanguage = languages[chineseIndex] + } + + if let englishIndex = languages.firstIndex(where: { $0.code == "en" }) { + targetPickerView.selectRow(englishIndex, inComponent: 0, animated: false) + selectedTargetLanguage = languages[englishIndex] + } + } + + // MARK: - 公共方法 + func setLanguages(source: LanguageModel, target: LanguageModel) { + if let sourceIndex = languages.firstIndex(where: { $0.code == source.code }) { + sourcePickerView.selectRow(sourceIndex, inComponent: 0, animated: true) + selectedSourceLanguage = source + } + + if let targetIndex = languages.firstIndex(where: { $0.code == target.code }) { + targetPickerView.selectRow(targetIndex, inComponent: 0, animated: true) + selectedTargetLanguage = target + } + } + + func getSelectedLanguages() -> (source: LanguageModel?, target: LanguageModel?) { + return (selectedSourceLanguage, selectedTargetLanguage) + } + + // MARK: - 私有方法 + private func updateSelection() { + // 可以在这里添加选择更新后的逻辑 + print("源语言: \(selectedSourceLanguage?.nativeName ?? "无")") + print("目标语言: \(selectedTargetLanguage?.nativeName ?? "无")") + + // 通知委托 + if let source = selectedSourceLanguage, let target = selectedTargetLanguage { + delegate?.languagePickerView(self, didSelectSourceLanguage: source, targetLanguage: target) + } + } + + @objc private func swapButtonTapped() { + guard let source = selectedSourceLanguage, + let target = selectedTargetLanguage else { return } + + // 交换语言 + setLanguages(source: target, target: source) + + // 通知委托 + delegate?.languagePickerViewDidTapSwap(self) + } +} + +// MARK: - UIPickerView DataSource & Delegate +@available(iOS 15.0, *) +extension LanguagePickerView1: UIPickerViewDataSource, UIPickerViewDelegate { + + func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 1 + } + + func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + return languages.count + } + + func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + let language = languages[row] + return "\(language.nativeName) (\(language.name))" + } + + func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + let selectedLanguage = languages[row] + + if pickerView.tag == 0 { + selectedSourceLanguage = selectedLanguage + } else { + selectedTargetLanguage = selectedLanguage + } + + // 防止选择相同的语言 + validateSelection() + } + + func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { + return 44.0 + } + + func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { + let language = languages[row] + + let containerView = UIView() + containerView.backgroundColor = .clear + + let stackView = UIStackView() + stackView.axis = .vertical + stackView.alignment = .center + stackView.spacing = 2 + stackView.translatesAutoresizingMaskIntoConstraints = false + + let nativeLabel = UILabel() + nativeLabel.text = language.nativeName + nativeLabel.font = UIFont.systemFont(ofSize: 16, weight: .medium) + nativeLabel.textAlignment = .center + nativeLabel.textColor = .label + + let nameLabel = UILabel() + nameLabel.text = language.name + nameLabel.font = UIFont.systemFont(ofSize: 12, weight: .regular) + nameLabel.textColor = .secondaryLabel + nameLabel.textAlignment = .center + + stackView.addArrangedSubview(nativeLabel) + stackView.addArrangedSubview(nameLabel) + containerView.addSubview(stackView) + + NSLayoutConstraint.activate([ + stackView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor), + stackView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), + stackView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 8), + stackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8) + ]) + + return containerView + } + + private func validateSelection() { + guard let source = selectedSourceLanguage, + let target = selectedTargetLanguage, + source.code == target.code else { return } + + // 如果选择了相同的语言,自动调整目标语言 + if let alternativeIndex = languages.firstIndex(where: { $0.code != source.code }) { + targetPickerView.selectRow(alternativeIndex, inComponent: 0, animated: true) + selectedTargetLanguage = languages[alternativeIndex] + } + } +} diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/PiPManager.swift b/ShenYin/ShenYin/UI/Home/SubTitle/PiPManager.swift new file mode 100644 index 0000000..99aabad --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/PiPManager.swift @@ -0,0 +1,175 @@ +// +// 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 { + let setView = PipSetView() + var textView = UITextView() + var translateTextView = UITextView() + + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + setupLayout() + } + func setupViews(){ + view.addSubviews([setView,textView,translateTextView]) + + textView.do { v in + v.textColor = UIColor.white + v.text = "原文" + v.font = UIFont.boldSystemFont(ofSize: 14) + v.backgroundColor = UIColor.gray + } + + translateTextView.do { v in + v.textColor = UIColor.orange + v.text = "翻译" + v.font = UIFont.boldSystemFont(ofSize: 14) + v.backgroundColor = UIColor.gray + } + } + func setupLayout(){ + setView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.height.equalTo(34) + make.top.equalToSuperview() + } + 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) + } + + } + + 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/PipPicker.swift b/ShenYin/ShenYin/UI/Home/SubTitle/PipPicker.swift new file mode 100644 index 0000000..872364a --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/PipPicker.swift @@ -0,0 +1,164 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/3/6. +// + +import UIKit +class PipPicker: BaseView { + private let btn = UIButton(type: .custom) + private let tableView = UITableView(frame: .zero, style: .plain) + var list:[String] = [] + + var selectedIndex = 0{ + didSet{ + tableView.reloadData() + } + } + var didSelected: ((Int,String)->Void)? + private var title:String = "" + + init(list:[String]) { + super.init(frame: .zero) + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setupViews() { + addSubviews([btn,tableView]) + + btn.do { v in + v.setImage(UIImage(named: "close_icon"), for: .normal) + v.addTarget(self, action: #selector(closeAction), for: .touchUpInside) + } + tableView.do { v in + v.backgroundColor = .color(.cellBg) + v.delegate = self + v.dataSource = self + v.rowHeight = 52 + v.separatorStyle = .none + v.register(CustomButton.SelectCell.self, forCellReuseIdentifier: CustomButton.SelectCell.reuseID) + } + } + override func setupLayout() { + super.setupLayout() + btn.snp.makeConstraints { (m) in + m.bottom.equalToSuperview() + m.right.equalToSuperview() + m.size.equalTo(60) + } + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().inset(10) + } + } + @objc func closeAction(){ + + } +} +extension PipPicker:UITableViewDelegate,UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return list.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = CustomButton.SelectCell.cell(withTableView: tableView, indexPath: indexPath) + cell.titleLabel.text = list[indexPath.row] + cell.selectedIcon.isHidden = selectedIndex != indexPath.row + return cell + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + selectedIndex = indexPath.row + tableView.reloadData() + didSelected?(selectedIndex,list[selectedIndex]) + } +} + +fileprivate extension CustomButton { + class SelectCell: BaseTableViewCell { + let bgView = UIView() + let titleLabel = UILabel() + let selectedIcon = UIImageView() + let line = UIView() + + var fontType:FontType = .min{ + didSet{ + titleLabel.text = fontType.title + } + } + + var shareType:ShareType = .document{ + didSet{ + titleLabel.text = shareType.title + } + } + + var enable:Bool = true{ + didSet{ + if enable{ + titleLabel.textColor = .color(.first) + } + else{ + titleLabel.textColor = .color(.secondary) + } + } + } + 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.addSubview(bgView) + bgView.addSubviews([titleLabel,selectedIcon,line]) + + backgroundColor = .color(.bgColor) + contentView.backgroundColor = .clear + selectionStyle = .none + bgView.do { v in + v.backgroundColor = .color(.cellBg) + } + titleLabel.do { (v) in + v.textColor = .color(.first) + v.font = .bold(14) + v.textAlignment = .left + } + + selectedIcon.image = UIImage(named: "cell_selected") + line.do { v in + v.backgroundColor = .color(.separateLine) + } + } + + override func setupLayout() { + bgView.snp.makeConstraints { (m) in + m.left.right.equalToSuperview() + m.top.equalToSuperview() + m.bottom.equalToSuperview() + } + selectedIcon.snp.makeConstraints { (m) in + m.width.equalTo(21) + m.height.equalTo(16) + m.right.equalToSuperview().inset(21) + m.centerY.equalToSuperview() + } + titleLabel.snp.makeConstraints { (m) in + m.centerY.equalToSuperview() + m.left.equalToSuperview().offset(24) + m.height.equalToSuperview() + } + line.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(18) + make.bottom.equalToSuperview() + make.height.equalTo(0.4) + } + } + } + +} + diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/PipSetView.swift b/ShenYin/ShenYin/UI/Home/SubTitle/PipSetView.swift new file mode 100644 index 0000000..8d66cd3 --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/PipSetView.swift @@ -0,0 +1,77 @@ +// +// PipSetView.swift +// ShenYin +// +// Created by liangshi on 2026/3/6. +// + +class PipSetView: BaseView { + let setBtn = UIButton(type: .custom) + let closeBtn = UIButton(type: .custom) + let languageItem = UIButton(type: .custom) + let dispalyItem = UIButton(type: .custom) + + override func setupViews() { + addSubviews([setBtn,languageItem,dispalyItem,closeBtn]) + + setBtn.do { v in + v.setImage(UIImage(named: "pip_set"), for: .normal) + v.addTarget(self, action: #selector(setBtnAction), for: .touchUpInside) + } + closeBtn.do { v in + v.setImage(UIImage(named: "pip_close"), for: .normal) + v.addTarget(self, action: #selector(closeAction), for: .touchUpInside) + } + languageItem.do { v in + v.setTitle("", for: .normal) + v.addTarget(self, action: #selector(languageAction), for: .touchUpInside) + v.yc.setCornerRadius(12) + v.backgroundColor = .red + } + dispalyItem.do { v in + v.setTitle("", for: .normal) + v.addTarget(self, action: #selector(displayAction), for: .touchUpInside) + v.yc.setCornerRadius(12) + v.backgroundColor = .red + } + } + override func setupLayout() { + setBtn.snp.makeConstraints { make in + make.size.equalTo(24) + make.left.equalToSuperview().inset(16) + make.top.equalToSuperview().inset(10) + } + closeBtn.snp.makeConstraints { make in + make.size.equalTo(24) + make.right.equalToSuperview().inset(16) + make.centerY.equalTo(setBtn) + } + languageItem.snp.makeConstraints { make in + make.height.equalTo(24) + make.left.equalTo(setBtn.snp.right).offset(4) + make.centerY.equalTo(setBtn) + make.width.equalTo(57) + } + dispalyItem.snp.makeConstraints { make in + make.height.equalTo(24) + make.left.equalTo(languageItem.snp.right).offset(4) + make.centerY.equalTo(setBtn) + make.width.equalTo(57) + } + } + @objc func setBtnAction(){ + Logger.debug("setBtnAction") + } + @objc func languageAction(){ + Logger.debug("languageAction") + } + @objc func displayAction(){ + Logger.debug("displayAction") + let view = PipPicker(list: ["只显示原文","只显译文","原文 & 译文"]) + superview?.addSubview(view) + } + @objc func closeAction(){ + Logger.debug("closeAction") + + } +} diff --git a/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleVC.swift b/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleVC.swift new file mode 100644 index 0000000..915eed2 --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/SubTitle/SubTitleVC.swift @@ -0,0 +1,186 @@ +// +// Untitled.swift +// ShenYin +// +// Created by liangshi on 2026/3/5. +// + + +import UIKit +import AVKit +import JoyGemAI +import SVProgressHUD +@available(iOS 15.0, *) +class SubTitleVC: UIViewController { + + lazy var languagePickerView : LanguagePickerView1 = { + let pickerView = LanguagePickerView1() + pickerView.delegate = self + return pickerView + }() + + var startButton : UIButton! + + var stopButton : UIButton! + let pipManager = PiPManager() + + + //画中画载体 + internal var customView : UIView = UIView() + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = UIColor.white + title = "字幕翻译" + + self.navigationController?.navigationBar.tintColor = UIColor.black + do { + try AVAudioSession.sharedInstance().setCategory(.playback) + try AVAudioSession.sharedInstance().setActive(true) + } catch { + print(error) + } + + view.addSubview(languagePickerView) + languagePickerView.snp.makeConstraints { make in + make.top.equalTo(view.safeAreaLayoutGuide.snp.top) + make.leading.trailing.equalToSuperview() + make.height.equalTo(100) + } + + // 或者设置特定语言 + languagePickerView.setLanguages( + source: LanguageModel.chinese, + target: LanguageModel.english + ) + + view.addSubview(customView) + customView.snp.makeConstraints { make in + make.top.equalTo(languagePickerView.snp.bottom).offset(20) + make.height.equalTo(40) + make.centerX.equalToSuperview() + make.width.equalTo(200) + } + + startButton = UIButton(type: .system) + startButton.setTitle("开始", for: .normal) + startButton.backgroundColor = UIColor.systemGray6 + startButton.setTitleColor(UIColor.darkText, for: .normal) + startButton.addTarget(self, action: #selector(startRecord), for: .touchUpInside) + + stopButton = UIButton(type: .system) + stopButton.setTitle("结束", for: .normal) + stopButton.backgroundColor = UIColor.systemGray6 + stopButton.setTitleColor(UIColor.darkText, for: .normal) + stopButton.addTarget(self, action: #selector(endRecord), for: .touchUpInside) + + let stackView = UIStackView(arrangedSubviews: [startButton, stopButton]) + stackView.axis = .horizontal + stackView.distribution = .fillEqually + stackView.alignment = .center + stackView.spacing = 16 + + view.addSubview(stackView) + stackView.snp.makeConstraints { make in + make.height.equalTo(60) + make.leading.equalToSuperview().offset(20) + make.trailing.equalToSuperview().offset(-20) + make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom).offset(-20) + } + + // Do any additional setup after loading the view. + JoyGemSubtitleManager.shared().delegate = self + + setupPiPManager() + } + + private func setupPiPManager() { + // 初始化画中画管理器 + pipManager.setupPictureInPicture(sourceView: self.customView) + + // 监听画中画状态变化 + NotificationCenter.default.addObserver( + self, + selector: #selector(pipStateChanged(_:)), + name: NSNotification.Name("PiPStateChanged"), + object: nil + ) + } + + @objc func startRecord() { + JoyGemSubtitleManager.shared().start() + + guard AVPictureInPictureController.isPictureInPictureSupported() else { + print("设备不支持画中画") + return + } + + pipManager.startPictureInPicture() + } + + @objc func endRecord() { + JoyGemSubtitleManager.shared().stop() + + pipManager.stopPictureInPicture() + } + + @objc private func pipStateChanged(_ notification: Notification) { + if let userInfo = notification.userInfo, + let isActive = userInfo["isActive"] as? Bool { + print("画中画状态: \(isActive ? "活跃" : "非活跃")") + } + } +} + +// 实现委托方法 +@available(iOS 15.0, *) +extension SubTitleVC: LanguagePickerView1Delegate { + + func languagePickerView(_ pickerView: LanguagePickerView1, + didSelectSourceLanguage source: LanguageModel, + targetLanguage target: LanguageModel) { + print("语言选择更新: \(source.nativeName) -> \(target.nativeName)") + // 更新你的翻译逻辑等 + JoyGemSubtitleManager.shared().originalLanguage = source.code + JoyGemSubtitleManager.shared().targetLanguage = target.code + } + + func languagePickerViewDidTapSwap(_ pickerView: LanguagePickerView1) { + print("交换了语言") + } +} + +@available(iOS 15.0, *) +extension SubTitleVC : JoyGemSubtitleManagerDelegate { + + func subtitleManager(_ manager: JoyGemSubtitleManager, didUpdateRaw model: JoyGemAsrModel) { + debugPrint("text: \(model.text),index: \(model.index)") + pipManager.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) + } + + func subtitleManagerStopped(_ manager: JoyGemSubtitleManager) { + + } + + func subtitleManager(_ manager: JoyGemSubtitleManager, stateChange code: JoyGemStateCode) { + + } + + func subtitleManager(_ manager: JoyGemSubtitleManager, onError code: JoyGemErrorCode) { + + } + + func subTitleManager(_ manager: JoyGemSubtitleManager, stateChange code: JoyGemStateCode) { + debugPrint("code: \(code)") + } + + func subTitleManager(_ manager: JoyGemSubtitleManager, onError code: JoyGemErrorCode) { + + } + +}