linphone-ios/Linphone/UI/Main/Settings/ViewModel/SettingsViewModel.swift
2025-01-21 11:56:06 +01:00

377 lines
12 KiB
Swift

/*
* 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 <http://www.gnu.org/licenses/>.
*/
import linphonesw
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
}
}
}
}