+//
+// Untitled.swift
+// ShenYin
+//
+// Created by liangshi on 2026/4/8.
+//
+
+//
+// DialogRecordBar.swift
+// ShenYin
+//
+// Created by liangshi on 2025/9/16.
+//
+import DGActivityIndicatorView
+
+enum DialogRecordDeviceType{
+ case earphone
+ case phone
+}
+enum DialogRecordDeviceState{
+ case start
+ case stop
+}
+enum DialogRecordLayoutType{
+ case normal
+ case spolit
+}
+protocol DialogRecordBarDelegate:NSObject {
+ func recordBar(_ bar:DialogRecordBar,layoutChange :DialogRecordLayoutType)
+ func recordBar(_ bar:DialogRecordBar, device:DialogRecordDeviceType,stateChange state:DialogRecordDeviceState)
+}
+
+extension UIView{
+ static let converView = UIView()
+ func updateConnectState(_ state:Bool) {
+ if state {
+ if UIView.converView.superview == nil {
+ return
+ }
+ self.alpha = 1
+ UIView.converView.removeFromSuperview()
+ }
+ else{
+ if UIView.converView.superview != nil {
+ return
+ }
+ self.alpha = 0.3
+ UIView.converView.backgroundColor = .clear
+ self.addSubview(UIView.converView)
+ UIView.converView.snp.makeConstraints { make in
+ make.edges.equalToSuperview()
+ }
+ }
+ }
+}
+
+class DialogRecordBar: BaseView {
+ let leftItem = DialogItem()
+ let rightItem = DialogItem()
+ let switchBtn = UIButton()
+
+ weak var delegate:DialogRecordBarDelegate?
+ var navBar:BaseNavigationBar = BaseNavigationBar()
+
+ var isBusy:Bool{
+ return leftItem.recordItem.activeView.animating || leftItem.recordItem.loadView.animating || rightItem.recordItem.activeView.animating || rightItem.recordItem.loadView.animating
+ }
+
+ override func setupViews() {
+ backgroundColor = .clear
+
+ addSubviews([leftItem,rightItem,switchBtn])
+ switchBtn.do { v in
+ v.setImage(UIImage(named: "split_screen_1"), for: .normal)
+ v.setImage(UIImage(named: "split_screen_2"), for: .selected)
+ v.addTarget(self, action: #selector(switchBtnAction), for: .touchUpInside)
+ }
+ leftItem.do { v in
+ v.recordItem.setImage(UIImage(named: "dialog_device_ear"), for: .normal)
+ v.recordItem.setImage(UIImage(named: "dialog_device_ear"), for: .selected)
+
+ v.recordItem.addTarget(self, action: #selector(leftRecordBtnAction), for: .touchUpInside)
+ v.languageItem.yc.addTap(target: self, action: #selector(leftLanguageAction))
+ v.languageItem.language = AiLanguageCache.get(type: .dialog_left) ?? .chinse
+ }
+ rightItem.do { v in
+ v.recordItem.setImage(UIImage(named: "dialog_device_phone"), for: .normal)
+ v.recordItem.setImage(UIImage(named: "dialog_device_phone"), for: .selected)
+
+
+ v.recordItem.addTarget(self, action: #selector(rightRecordBtnAction), for: .touchUpInside)
+ v.languageItem.yc.addTap(target: self, action: #selector(rightLanguageAction))
+ v.languageItem.language = AiLanguageCache.get(type: .dialog_right) ?? .english
+ }
+ }
+
+ override func setupLayout() {
+ switchBtn.snp.remakeConstraints { make in
+ make.size.equalTo(38)
+ make.centerX.equalToSuperview()
+ make.top.equalToSuperview().inset(10)
+ }
+ leftItem.snp.remakeConstraints { make in
+ make.width.equalTo(100)
+ make.height.equalTo(98)
+ make.top.equalToSuperview().inset(10)
+ make.right.equalToSuperview().offset(-(kScreenWid/2 + 60))
+ }
+ rightItem.snp.remakeConstraints { make in
+ make.width.height.equalTo(leftItem)
+ make.top.equalToSuperview().inset(10)
+ make.left.equalToSuperview().offset(kScreenWid/2 + 60)
+ }
+ }
+ func splitLayout(){
+ switchBtn.snp.remakeConstraints { make in
+ make.size.equalTo(38)
+ make.right.equalToSuperview().inset(36)
+ make.top.equalToSuperview().inset(10)
+ }
+ leftItem.snp.remakeConstraints { make in
+ make.width.equalTo(100)
+ make.height.equalTo(98)
+ make.top.equalToSuperview().inset(10)
+ make.right.equalToSuperview().offset(-(kScreenWid/2 - 33))
+ }
+ }
+ func stopRecord(_ type:DialogRecordDeviceType){
+ if type == .earphone {
+ leftItem.recordItem.isSelected = false
+ leftItem.recordItem.stopAnimating(finished: { [weak self] in
+ guard let self = self else { return }
+ leftItem.recordItem.alpha = 1
+ leftItem.languageItem.alpha = 1
+ rightItem.recordItem.alpha = 1
+ rightItem.languageItem.alpha = 1
+ })
+ }
+ else{
+ rightItem.recordItem.isSelected = false
+ rightItem.recordItem.stopAnimating(finished: { [weak self] in
+ guard let self = self else { return }
+ leftItem.recordItem.alpha = 1
+ leftItem.languageItem.alpha = 1
+ rightItem.recordItem.alpha = 1
+ rightItem.languageItem.alpha = 1
+ })
+ }
+ }
+ override func setupData() {
+ NotificationManager.add(self, selector: #selector(deviceChange), forName: .deviceValidChange)
+ }
+ @objc func deviceChange(){
+ self.updateConnectState(CCBleManager.shared.isConneted)
+ }
+
+ @objc func switchBtnAction(){
+ switchBtn.isSelected = !switchBtn.isSelected
+ let type:DialogRecordLayoutType = switchBtn.isSelected ? .spolit:.normal
+ delegate?.recordBar(self, layoutChange: type)
+
+ if type == .normal {
+ navBar.titleLabel.isHidden = false
+ addSubview(rightItem)
+ setupLayout()
+ rightItem.setupLayout()
+ navBar.frame = CGRectMake(0, 0, kScreenWid, kNavBtm)
+ rightItem.transform = CGAffineTransform(rotationAngle: .pi*2)
+ }
+ else{
+ splitLayout()
+ rightItem.setupLayout()
+ rightItem.transform = CGAffineTransform(rotationAngle: .pi)
+
+ navBar.titleLabel.isHidden = true
+ navBar.addSubview(rightItem)
+ navBar.frame = CGRectMake(0, 0, kScreenWid, CCConfig.dialogRecordBarHeight)
+ navBar.superview?.bringSubviewToFront(navBar)
+ rightItem.snp.remakeConstraints { make in
+ make.centerX.equalToSuperview()
+ make.bottom.equalToSuperview()
+ make.width.height.equalTo(leftItem)
+ }
+ }
+ }
+
+ @objc func leftRecordBtnAction(){
+ if rightItem.recordItem.activeView.animating || rightItem.recordItem.loadView.animating || leftItem.recordItem.loadView.animating{
+ return
+ }
+ if leftItem.recordItem.activeView.animating{
+ delegate?.recordBar(self, device: .earphone,stateChange: .stop)
+ stopRecord(.earphone)
+ return
+ }
+
+ leftItem.recordItem.isSelected = !leftItem.recordItem.isSelected
+ if leftItem.recordItem.isSelected {
+ leftItem.recordItem.alpha = 1
+ leftItem.languageItem.alpha = 0.3
+ rightItem.recordItem.alpha = 0.3
+ rightItem.languageItem.alpha = 0.3
+
+ leftItem.recordItem.startAnimating()
+ delegate?.recordBar(self, device: .earphone,stateChange: .start)
+ }
+ else{
+ stopRecord(.earphone)
+ delegate?.recordBar(self, device: .earphone,stateChange: .stop)
+ }
+ }
+ @objc func rightRecordBtnAction(){
+ if leftItem.recordItem.activeView.animating || leftItem.recordItem.loadView.animating || rightItem.recordItem.loadView.animating{
+ return
+ }
+ if rightItem.recordItem.activeView.animating{
+ stopRecord(.phone)
+ delegate?.recordBar(self, device: .phone,stateChange: .stop)
+ return
+ }
+
+ rightItem.recordItem.isSelected = !rightItem.recordItem.isSelected
+
+ if rightItem.recordItem.isSelected {
+ leftItem.recordItem.alpha = 0.3
+ leftItem.languageItem.alpha = 0.3
+ rightItem.recordItem.alpha = 1
+ rightItem.languageItem.alpha = 0.3
+
+ rightItem.recordItem.startAnimating()
+ delegate?.recordBar(self, device: .phone,stateChange: .start)
+ }
+ else{
+ stopRecord(.phone)
+ delegate?.recordBar(self, device: .phone,stateChange: .stop)
+ }
+ }
+
+ @objc func leftLanguageAction(){
+ if self.isBusy {
+ return
+ }
+ let v = LanguagePickerView(languages: CCAiLanguage.all,isLeft: true,left: leftItem.languageItem.language,right: rightItem.languageItem.language)
+ v.selectedIndex = v.list.firstIndex(where: {$0.title == leftItem.languageItem.language.title}) ?? 0
+ v.show()
+ v.didSelected = {[weak self] (index,value) in
+ guard let self = self else { return }
+ if index == 0 {
+ leftItem.languageItem.language = value
+ AiLanguageCache.save(type: .dialog_left, lan: value)
+ }
+ else{
+ rightItem.languageItem.language = value
+ AiLanguageCache.save(type: .dialog_right, lan: value)
+ }
+ }
+ }
+ @objc func rightLanguageAction(){
+ if self.isBusy{
+ return
+ }
+ let v = LanguagePickerView(languages: CCAiLanguage.all,isLeft: false,left: leftItem.languageItem.language,right: rightItem.languageItem.language)
+ v.selectedIndex = v.list.firstIndex(where: {$0.title == rightItem.languageItem.language.title}) ?? 0
+ v.show()
+ v.didSelected = {[weak self] (index,value) in
+ guard let self = self else { return }
+ if index == 0 {
+ leftItem.languageItem.language = value
+ AiLanguageCache.save(type: .dialog_left, lan: value)
+ }
+ else{
+ rightItem.languageItem.language = value
+ AiLanguageCache.save(type: .dialog_right, lan: value)
+ }
+ }
+ }
+
+ class DialogItem: UIView {
+ let languageItem = RecordLanguageItem()
+ let recordItem = DialogRecordingButton()
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ setupViews()
+ setupLayout()
+ }
+
+ @MainActor required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ func setupViews() {
+// backgroundColor = .yellow
+ addSubviews([languageItem,recordItem])
+ }
+
+ func setupLayout() {
+ recordItem.snp.remakeConstraints { make in
+ make.width.equalToSuperview()
+ make.height.equalTo(50)
+ make.top.equalToSuperview().inset(0)
+ make.centerX.equalToSuperview()
+ }
+ languageItem.snp.remakeConstraints { make in
+ make.height.equalTo(40)
+ make.top.equalTo(recordItem.snp.bottom)
+ make.centerX.equalToSuperview()
+ }
+
+ }
+ }
+ class RecordLanguageItem: BaseView {
+ let icon = UIImageView()
+ let titleLabel = UILabel()
+ let valueLabel = UILabel()
+
+ var language:CCAiLanguage = CCAiLanguage.chinse{
+ didSet{
+ titleLabel.text = language.title
+ }
+ }
+
+ override func setupViews() {
+ addSubviews([titleLabel,valueLabel,icon])
+ icon.do { v in
+ v.image = UIImage(named: "polygon")
+ }
+ titleLabel.do { v in
+ v.textColor = .color(.level1)
+ v.font = .bold(16)
+ }
+ valueLabel.do { v in
+ v.textColor = .color(.level2)
+ v.font = .regular(10)
+ }
+ }
+
+ override func setupLayout() {
+ icon.snp.makeConstraints { make in
+ make.size.equalTo(12)
+ make.top.equalToSuperview().inset(10)
+ make.right.equalToSuperview()
+ }
+ titleLabel.snp.makeConstraints { make in
+ make.right.equalTo(icon.snp.left).offset(-5)
+ make.centerY.equalTo(icon)
+ make.left.equalToSuperview()
+ }
+ valueLabel.snp.makeConstraints { make in
+ make.right.equalTo(titleLabel)
+ make.top.equalTo(titleLabel.snp.bottom).offset(5)
+ make.bottom.equalToSuperview().inset(12)
+ }
+ }
+ }
+
+ class DialogRecordingButton:UIButton{
+ let activeView = DGActivityIndicatorView(type: .lineScalePulseOut,tintColor: UIColor.white,size: 25)!
+ let loadView = DGActivityIndicatorView(type: .ballClipRotate,tintColor: UIColor.white)!
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ activeView.backgroundColor = .color(.theme)
+ activeView.yc.setCornerRadius(25)
+ addSubview(activeView)
+ activeView.snp.remakeConstraints { make in
+ make.edges.equalToSuperview()
+ }
+
+ loadView.yc.setCornerRadius(25)
+ loadView.backgroundColor = .color(.theme)
+ addSubview(loadView)
+ loadView.snp.remakeConstraints { make in
+ make.edges.equalToSuperview()
+ }
+
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+ func startAnimating(finished:(()->Void)? = nil){
+ loadView.isHidden = true
+ activeView.isHidden = false
+ activeView.startAnimating()
+ }
+ func stopAnimating(finished:@escaping (()->Void)){
+ activeView.stopAnimating()
+ activeView.isHidden = true
+
+ loadView.isHidden = false
+ loadView.startAnimating()
+ DispatchQueue.main.asyncAfter(deadline:.now() + 0.3) { [weak self] in
+ self?.loadView.stopAnimating()
+ finished()
+ }
+ }
+ }
+
+}