/* * Copyright (c) 2010-2023 Belledonne Communications SARL. * * This file is part of linphone-iphone * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import linphonesw import SwiftUI class SettingsViewModel: ObservableObject { static let TAG = "[SettingsViewModel]" private var coreDelegate: CoreDelegate? // Security settings @Published var enableVfs: Bool = false // @Published var preventScreenshots: Bool = false // Calls settings @Published var adaptiveRateControl: Bool = false @Published var enableVideo: Bool = false @Published var autoRecord: Bool = false // Conversations settings @Published var autoDownload: Bool = false // Meetings settings @Published var defaultLayout: String = "" // Network settings @Published var useWifiOnly: Bool = false @Published var allowIpv6: Bool = false // Advanced settings @Published var enableFec: Bool = false @Published var mediaEncryption: String = "" @Published var mediaEncryptionMandatory: Bool = false @Published var acceptEarlyMedia: Bool = false @Published var allowOutgoingEarlyMedia: Bool = false @Published var deviceId: String = "" @Published var uploadServerUrl: String = "" @Published var remoteProvisioningUrl: String = "" @Published var inputAudioDeviceLabels: [String] = [] @Published var inputAudioDeviceValues: [AudioDevice] = [] @Published var inputAudioDeviceIndex: Int = 0 @Published var outputAudioDeviceLabels: [String] = [] @Published var outputAudioDeviceValues: [AudioDevice] = [] @Published var outputAudioDeviceIndex: Int = 0 @Published var audioCodecs: [CodecModel] = [] @Published var videoCodecs: [CodecModel] = [] init() { CoreContext.shared.doOnCoreQueue { core in let enableVfsTmp = CorePreferences.vfsEnabled let adaptiveRateControlTmp = core.adaptiveRateControlEnabled let enableVideoTmp = core.videoEnabled let autoRecordTmp = CorePreferences.automaticallyStartCallRecording let autoDownloadTmp = core.maxSizeForAutoDownloadIncomingFiles == 0 let defaultLayoutTmp = core.defaultConferenceLayout.rawValue == 0 ? String(localized: "settings_meetings_layout_mosaic_label") : String(localized: "settings_meetings_layout_active_speaker_label") let useWifiOnlyTmp = core.wifiOnlyEnabled let allowIpv6Tmp = core.ipv6Enabled // Advanced settings let enableFecTmp = core.fecEnabled var mediaEncryptionTmp = "" switch core.mediaEncryption { case .None: mediaEncryptionTmp = "None" case .SRTP: mediaEncryptionTmp = "SRTP" case .ZRTP: mediaEncryptionTmp = "ZRTP" case .DTLS: mediaEncryptionTmp = "DTLS" } let mediaEncryptionMandatoryTmp = core.isMediaEncryptionMandatory let acceptEarlyMediaTmp = CorePreferences.acceptEarlyMedia let allowOutgoingEarlyMediaTmp = CorePreferences.allowOutgoingEarlyMedia let deviceIdTmp = CorePreferences.deviceName let fileSharingServerUrlTmp = core.fileTransferServer let remoteProvisioningUrlTmp = core.provisioningUri DispatchQueue.main.async { self.enableVfs = enableVfsTmp self.adaptiveRateControl = adaptiveRateControlTmp self.enableVideo = enableVideoTmp self.autoRecord = autoRecordTmp self.autoDownload = autoDownloadTmp self.defaultLayout = defaultLayoutTmp self.useWifiOnly = useWifiOnlyTmp self.allowIpv6 = allowIpv6Tmp // Advanced settings self.enableFec = enableFecTmp self.mediaEncryption = mediaEncryptionTmp self.mediaEncryptionMandatory = mediaEncryptionMandatoryTmp self.acceptEarlyMedia = acceptEarlyMediaTmp self.allowOutgoingEarlyMedia = allowOutgoingEarlyMediaTmp self.deviceId = deviceIdTmp self.uploadServerUrl = fileSharingServerUrlTmp ?? "" self.remoteProvisioningUrl = remoteProvisioningUrlTmp ?? "" /* self.setupAudioDevices() self.coreDelegate = CoreDelegateStub( onAudioDevicesListUpdated: { (_: Core) in Log.info( "\(SettingsViewModel.TAG) Audio devices list has changed, update available input/output audio devices list" ) self.setupAudioDevices() } ) core.addDelegate(delegate: self.coreDelegate!) */ self.setupCodecs() } } } deinit { if let delegate = coreDelegate { CoreContext.shared.doOnCoreQueue { core in core.removeDelegate(delegate: delegate) } } } func downloadAndApplyRemoteProvisioning() { Log.info("\(SettingsViewModel.TAG) Updating remote provisioning URI now and then download/apply it") CoreContext.shared.doOnCoreQueue { core in if core.provisioningUri != self.remoteProvisioningUrl && !(core.provisioningUri == nil && self.remoteProvisioningUrl.isEmpty) { try? core.setProvisioninguri(newValue: self.remoteProvisioningUrl) Log.info("\(SettingsViewModel.TAG) Restarting the Core to apply configuration changes") core.stop() Log.info("\(SettingsViewModel.TAG) Core has been stopped, restarting it") try? core.start() Log.info("\(SettingsViewModel.TAG) Core has been restarted") } } } func setInputAudioDevice(index: Int) { CoreContext.shared.doOnCoreQueue { core in let audioDevice = self.inputAudioDeviceValues[index] core.defaultInputAudioDevice = audioDevice } } func setOutputAudioDevice(index: Int) { CoreContext.shared.doOnCoreQueue { core in let audioDevice = self.outputAudioDeviceValues[index] core.defaultOutputAudioDevice = audioDevice } } func setupAudioDevices() { CoreContext.shared.doOnCoreQueue { core in DispatchQueue.main.async { self.inputAudioDeviceLabels = [] self.inputAudioDeviceValues = [] self.inputAudioDeviceIndex = 0 self.outputAudioDeviceLabels = [] self.outputAudioDeviceValues = [] self.outputAudioDeviceIndex = 0 } // let audioSession = AVAudioSession.sharedInstance() // try? audioSession.setActive(true) // Input Audio Devices var inputIndex = 0 let defaultInputAudioDevice = core.defaultInputAudioDevice Log.info("\(SettingsViewModel.TAG) Current default input audio device is \(defaultInputAudioDevice?.id ?? "Error defaultInputAudioDevice id")") var inputAudioDeviceLabelsTmp = [String]() var inputAudioDeviceValuesTmp = [AudioDevice]() var inputAudioDeviceIndexTmp = inputIndex core.extendedAudioDevices.forEach { audioDevice in if audioDevice.hasCapability(capability: AudioDevice.Capabilities.CapabilityRecord) { inputAudioDeviceLabelsTmp.append(audioDevice.id) inputAudioDeviceValuesTmp.append(audioDevice) if audioDevice.id == defaultInputAudioDevice?.id { inputAudioDeviceIndexTmp = inputIndex } Log.info("\(SettingsViewModel.TAG) Input audio device is \(audioDevice.id) \(audioDevice.capabilities)") inputIndex += 1 } Log.info("\(SettingsViewModel.TAG) ---- 1 Input audio device is \(audioDevice.id) \(audioDevice.capabilities)") } DispatchQueue.main.async { self.inputAudioDeviceLabels = inputAudioDeviceLabelsTmp self.inputAudioDeviceValues = inputAudioDeviceValuesTmp self.inputAudioDeviceIndex = inputAudioDeviceIndexTmp } // Output Audio Devices var outputIndex = 0 let defaultOutputAudioDevice = core.defaultOutputAudioDevice Log.info("\(SettingsViewModel.TAG) Current default output audio device is \(defaultOutputAudioDevice?.id ?? "Error defaultOutputAudioDevice id")") var outputAudioDeviceLabelsTmp = [String]() var outputAudioDeviceValuesTmp = [AudioDevice]() var outputAudioDeviceIndexTmp = outputIndex core.extendedAudioDevices.forEach { audioDevice in if audioDevice.hasCapability(capability: AudioDevice.Capabilities.CapabilityPlay) { outputAudioDeviceLabelsTmp.append(audioDevice.id) outputAudioDeviceValuesTmp.append(audioDevice) if audioDevice.id == defaultOutputAudioDevice?.id { outputAudioDeviceIndexTmp = outputIndex } Log.info("\(SettingsViewModel.TAG) Output audio device is \(audioDevice.id) \(audioDevice.capabilities)") outputIndex += 1 } } DispatchQueue.main.async { self.outputAudioDeviceLabels = outputAudioDeviceLabelsTmp self.outputAudioDeviceValues = outputAudioDeviceValuesTmp self.outputAudioDeviceIndex = outputAudioDeviceIndexTmp } } } func setupCodecs() { CoreContext.shared.doOnCoreQueue { core in var audioCodecsList: [CodecModel] = [] for payload in core.audioPayloadTypes { let model = CodecModel( mimeType: payload.mimeType, clockRate: payload.clockRate, channels: payload.channels, recvFmtp: nil, isAudioCodec: true, enabled: payload.enabled(), onEnabledChanged: { enabled in payload.enable(enabled: enabled) } ) audioCodecsList.append(model) } var videoCodecsList: [CodecModel] = [] for payload in core.videoPayloadTypes { let model = CodecModel( mimeType: payload.mimeType, clockRate: -1, channels: -1, recvFmtp: payload.recvFmtp, isAudioCodec: false, enabled: payload.enabled(), onEnabledChanged: { enabled in payload.enable(enabled: enabled) } ) videoCodecsList.append(model) } DispatchQueue.main.async { self.audioCodecs = audioCodecsList self.videoCodecs = videoCodecsList } } } func saveChangesWhenLeaving() { CoreContext.shared.doOnCoreQueue { core in if CorePreferences.vfsEnabled != self.enableVfs { CorePreferences.vfsEnabled = self.enableVfs } if core.adaptiveRateControlEnabled != self.adaptiveRateControl { core.adaptiveRateControlEnabled = self.adaptiveRateControl } if core.videoEnabled != self.enableVideo { core.videoCaptureEnabled = self.enableVideo core.videoDisplayEnabled = self.enableVideo } if CorePreferences.automaticallyStartCallRecording != self.autoRecord { CorePreferences.automaticallyStartCallRecording = self.autoRecord } if (core.maxSizeForAutoDownloadIncomingFiles == 0) != self.autoDownload { core.maxSizeForAutoDownloadIncomingFiles = self.autoDownload ? 0 : -1 } if (core.defaultConferenceLayout.rawValue == 0) != (self.defaultLayout == String(localized: "settings_meetings_layout_mosaic_label")) { core.defaultConferenceLayout = self.defaultLayout == String(localized: "settings_meetings_layout_mosaic_label") ? .Grid : .ActiveSpeaker } if core.wifiOnlyEnabled != self.useWifiOnly { core.wifiOnlyEnabled = self.useWifiOnly } if core.ipv6Enabled != self.allowIpv6 { core.ipv6Enabled = self.allowIpv6 } if core.fecEnabled != self.enableFec { core.fecEnabled = self.enableFec } if (core.mediaEncryption == .None && self.mediaEncryption != "None") || (core.mediaEncryption == .SRTP && self.mediaEncryption != "SRTP") || (core.mediaEncryption == .ZRTP && self.mediaEncryption != "ZRTP") || (core.mediaEncryption == .DTLS && self.mediaEncryption != "DTLS") { switch self.mediaEncryption { case "None": try? core.setMediaencryption(newValue: .None) case "SRTP": try? core.setMediaencryption(newValue: .SRTP) case "ZRTP": try? core.setMediaencryption(newValue: .ZRTP) case "DTLS": try? core.setMediaencryption(newValue: .DTLS) default: break } } if core.isMediaEncryptionMandatory != self.mediaEncryptionMandatory { core.mediaEncryptionMandatory = self.mediaEncryptionMandatory } if CorePreferences.acceptEarlyMedia != self.acceptEarlyMedia { CorePreferences.acceptEarlyMedia = self.acceptEarlyMedia } if CorePreferences.allowOutgoingEarlyMedia != self.allowOutgoingEarlyMedia { CorePreferences.allowOutgoingEarlyMedia = self.allowOutgoingEarlyMedia } if CorePreferences.deviceName != self.deviceId { CorePreferences.deviceName = self.deviceId } if core.fileTransferServer != self.uploadServerUrl && !(core.fileTransferServer == nil && self.uploadServerUrl.isEmpty) { core.fileTransferServer = self.uploadServerUrl } } } }