diff --git a/Linphone.xcodeproj/project.pbxproj b/Linphone.xcodeproj/project.pbxproj index 195605b87..3f72db35d 100644 --- a/Linphone.xcodeproj/project.pbxproj +++ b/Linphone.xcodeproj/project.pbxproj @@ -129,6 +129,7 @@ D78290BB2ADD40B2004AA85C /* ContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78290BA2ADD40B2004AA85C /* ContactViewModel.swift */; }; D783C77C2B1089B200622CC2 /* assistant_linphone_default_values in Resources */ = {isa = PBXBuildFile; fileRef = D783C77A2B1089B200622CC2 /* assistant_linphone_default_values */; }; D783C77D2B1089B200622CC2 /* assistant_third_party_default_values in Resources */ = {isa = PBXBuildFile; fileRef = D783C77B2B1089B200622CC2 /* assistant_third_party_default_values */; }; + D78607712D36CB8A009E6A7E /* SettingsAdvancedFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78607702D36CB87009E6A7E /* SettingsAdvancedFragment.swift */; }; D78E06282BE3811D00CE3783 /* CallStatsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78E06272BE3811D00CE3783 /* CallStatsModel.swift */; }; D78E062A2BEA698E00CE3783 /* MediaEncryptedSheetBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78E06292BEA698E00CE3783 /* MediaEncryptedSheetBottomSheet.swift */; }; D78E062C2BEA69BC00CE3783 /* CallStatisticsSheetBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78E062B2BEA69BC00CE3783 /* CallStatisticsSheetBottomSheet.swift */; }; @@ -329,6 +330,7 @@ D78290BA2ADD40B2004AA85C /* ContactViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactViewModel.swift; sourceTree = ""; }; D783C77A2B1089B200622CC2 /* assistant_linphone_default_values */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = assistant_linphone_default_values; sourceTree = ""; }; D783C77B2B1089B200622CC2 /* assistant_third_party_default_values */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = assistant_third_party_default_values; sourceTree = ""; }; + D78607702D36CB87009E6A7E /* SettingsAdvancedFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAdvancedFragment.swift; sourceTree = ""; }; D78E06272BE3811D00CE3783 /* CallStatsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallStatsModel.swift; sourceTree = ""; }; D78E06292BEA698E00CE3783 /* MediaEncryptedSheetBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaEncryptedSheetBottomSheet.swift; sourceTree = ""; }; D78E062B2BEA69BC00CE3783 /* CallStatisticsSheetBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallStatisticsSheetBottomSheet.swift; sourceTree = ""; }; @@ -939,6 +941,7 @@ D7DC096B2CFA192F00A6D47C /* Fragments */ = { isa = PBXGroup; children = ( + D78607702D36CB87009E6A7E /* SettingsAdvancedFragment.swift */, D732C38B2D311D2100F78100 /* SettingsFragment.swift */, D7C5003F2D27F16900DD53EC /* AccountSettingsFragment.swift */, D7DC096E2CFA1D7400A6D47C /* AccountProfileFragment.swift */, @@ -1199,6 +1202,7 @@ D796F2002B0BB61A0041115F /* ToastViewModel.swift in Sources */, D7C3650A2AF001C300FE6142 /* EditContactFragment.swift in Sources */, D7A03FBD2ACC2DB60081A588 /* ContactsView.swift in Sources */, + D78607712D36CB8A009E6A7E /* SettingsAdvancedFragment.swift in Sources */, 66E50A492BD12B2300AD61CA /* MeetingsView.swift in Sources */, D719ABCF2ABC779A00B41C10 /* AccountLoginViewModel.swift in Sources */, D717630D2BD7BD0E00464097 /* ParticipantsListFragment.swift in Sources */, diff --git a/Linphone/Core/CorePreferences.swift b/Linphone/Core/CorePreferences.swift index 0f110b469..99eb1548e 100644 --- a/Linphone/Core/CorePreferences.swift +++ b/Linphone/Core/CorePreferences.swift @@ -224,6 +224,24 @@ class CorePreferences { } } + static var acceptEarlyMedia: Bool { + get { + return Config.get().getBool(section: "sip", key: "incoming_calls_early_media", defaultValue: false) + } + set { + Config.get().setBool(section: "sip", key: "incoming_calls_early_media", value: newValue) + } + } + + static var allowOutgoingEarlyMedia: Bool { + get { + return Config.get().getBool(section: "sip", key: "real_early_media", defaultValue: false) + } + set { + Config.get().setBool(section: "sip", key: "real_early_media", value: newValue) + } + } + private func copy(from: String, to: String, overrideIfExists: Bool = false) { let fileManager = FileManager.default if fileManager.fileExists(atPath: to), !overrideIfExists { diff --git a/Linphone/Localizable.xcstrings b/Linphone/Localizable.xcstrings index b1a67bb9b..708540cc0 100644 --- a/Linphone/Localizable.xcstrings +++ b/Linphone/Localizable.xcstrings @@ -3825,6 +3825,9 @@ } } } + }, + "DTLS" : { + }, "Error" : { "localizations" : { @@ -5469,6 +5472,9 @@ } } } + }, + "None" : { + }, "notification_chat_message_reaction_received" : { "localizations" : { @@ -5651,6 +5657,193 @@ } } }, + "settings_advanced_accept_early_media_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accept early media" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accepter l'early media" + } + } + } + }, + "settings_advanced_allow_outgoing_early_media_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allow outgoing early media" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Autoriser l'early media pour les appels sortants" + } + } + } + }, + "settings_advanced_audio_codecs_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Audio codecs" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Codecs audio" + } + } + } + }, + "settings_advanced_audio_devices_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Audio devices" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Périphériques audio" + } + } + } + }, + "settings_advanced_device_id" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Device ID" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom de l'appareil" + } + } + } + }, + "settings_advanced_device_id_hint" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alpha-numerical characters only" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caractères alpha-numériques uniquement" + } + } + } + }, + "settings_advanced_download_apply_remote_provisioning" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download & apply" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Télécharger & appliquer" + } + } + } + }, + "settings_advanced_input_audio_device_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Default input audio device" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Périphérique de capture par défaut" + } + } + } + }, + "settings_advanced_media_encryption_mandatory_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Media encryption mandatory" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rendre le chiffrement du média obligatoire" + } + } + } + }, + "settings_advanced_output_audio_device_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Default output audio device" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Périphérique d'écoute par défaut" + } + } + } + }, + "settings_advanced_remote_provisioning_url" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote provisioning URL" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL de configuration distante" + } + } + } + }, "settings_advanced_title" : { "extractionState" : "manual", "localizations" : { @@ -5668,6 +5861,40 @@ } } }, + "settings_advanced_upload_server_url" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "File sharing server URL" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL du serveur de partage de fichier" + } + } + } + }, + "settings_advanced_video_codecs_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Video codecs" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Codecs vidéo" + } + } + } + }, "settings_calls_adaptive_rate_control_title" : { "extractionState" : "manual", "localizations" : { @@ -6630,6 +6857,9 @@ }, "sip.linphone.org" : { "shouldTranslate" : false + }, + "SRTP" : { + }, "start" : { "localizations" : { @@ -6909,6 +7139,9 @@ }, "You will change this mode later" : { + }, + "ZRTP" : { + } }, "version" : "1.0" diff --git a/Linphone/UI/Main/Fragments/SideMenu.swift b/Linphone/UI/Main/Fragments/SideMenu.swift index bbad9bbea..60e764f23 100644 --- a/Linphone/UI/Main/Fragments/SideMenu.swift +++ b/Linphone/UI/Main/Fragments/SideMenu.swift @@ -130,6 +130,7 @@ struct SideMenu: View { iconName: "gear", title: "settings_title" ).onTapGesture { + self.menuClose() withAnimation { isShowSettingsFragment = true } diff --git a/Linphone/UI/Main/Settings/Fragments/SettingsAdvancedFragment.swift b/Linphone/UI/Main/Settings/Fragments/SettingsAdvancedFragment.swift new file mode 100644 index 000000000..aa58ad13b --- /dev/null +++ b/Linphone/UI/Main/Settings/Fragments/SettingsAdvancedFragment.swift @@ -0,0 +1,388 @@ +/* + * 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 SwiftUI +import UniformTypeIdentifiers + +struct SettingsAdvancedFragment: View { + @ObservedObject var settingsViewModel: SettingsViewModel + + @Environment(\.dismiss) var dismiss + + @State var audioDevicesIsOpen: Bool = false + @State var audioCodecsIsOpen: Bool = false + @State var videoCodecsIsOpen: Bool = false + + @FocusState var isDeviceIdFocused: Bool + @FocusState var isUploadServerUrlFocused: Bool + @FocusState var isRemoteProvisioningUrlFocused: Bool + + var body: some View { + ZStack { + VStack(spacing: 1) { + Rectangle() + .foregroundColor(Color.orangeMain500) + .edgesIgnoringSafeArea(.top) + .frame(height: 0) + + HStack { + Image("caret-left") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.orangeMain500) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.all, 10) + .padding(.top, 4) + .padding(.leading, -10) + .onTapGesture { + dismiss() + } + + Text("settings_title") + .default_text_style_orange_800(styleSize: 16) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.top, 4) + .lineLimit(1) + + Spacer() + } + .frame(maxWidth: .infinity) + .frame(height: 50) + .padding(.horizontal) + .padding(.bottom, 4) + .background(.white) + + ScrollView { + VStack(spacing: 0) { + VStack(spacing: 30) { + Toggle("settings_calls_enable_fec_title", isOn: $settingsViewModel.enableFec) + .default_text_style_700(styleSize: 15) + + VStack(alignment: .leading) { + Text("call_stats_media_encryption_title") + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + Menu { + Button("None") { settingsViewModel.mediaEncryption = "None"} + Button("SRTP") { settingsViewModel.mediaEncryption = "SRTP"} + Button("ZRTP") { settingsViewModel.mediaEncryption = "ZRTP"} + Button("DTLS") { settingsViewModel.mediaEncryption = "DTLS"} + } label: { + Text(settingsViewModel.mediaEncryption) + .default_text_style(styleSize: 15) + .frame(maxWidth: .infinity, alignment: .leading) + Image("caret-down") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 20, height: 20) + } + .frame(height: 25) + .padding(.horizontal, 20) + .padding(.vertical, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(Color.gray200, lineWidth: 1) + ) + } + + Toggle("settings_advanced_media_encryption_mandatory_title", isOn: $settingsViewModel.mediaEncryptionMandatory) + .default_text_style_700(styleSize: 15) + + Toggle("settings_advanced_accept_early_media_title", isOn: $settingsViewModel.acceptEarlyMedia) + .default_text_style_700(styleSize: 15) + + Toggle("settings_advanced_allow_outgoing_early_media_title", isOn: $settingsViewModel.allowOutgoingEarlyMedia) + .default_text_style_700(styleSize: 15) + + VStack(alignment: .leading) { + Text("settings_advanced_device_id") + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + TextField("settings_advanced_device_id_hint", text: $settingsViewModel.deviceId) + .default_text_style(styleSize: 15) + .frame(height: 25) + .padding(.horizontal, 20) + .padding(.vertical, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(isDeviceIdFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1) + ) + .focused($isDeviceIdFocused) + } + + VStack(alignment: .leading) { + Text("settings_advanced_upload_server_url") + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + TextField("settings_advanced_upload_server_url", text: $settingsViewModel.uploadServerUrl) + .default_text_style(styleSize: 15) + .frame(height: 25) + .padding(.horizontal, 20) + .padding(.vertical, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(isUploadServerUrlFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1) + ) + .focused($isUploadServerUrlFocused) + } + + VStack(alignment: .leading) { + Text("settings_advanced_remote_provisioning_url") + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + TextField("settings_advanced_remote_provisioning_url", text: $settingsViewModel.remoteProvisioningUrl) + .default_text_style(styleSize: 15) + .frame(height: 25) + .padding(.horizontal, 20) + .padding(.vertical, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(isRemoteProvisioningUrlFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1) + ) + .focused($isRemoteProvisioningUrlFocused) + } + + HStack { + Spacer() + + Button( + action: { + settingsViewModel.downloadAndApplyRemoteProvisioning() + }, label: { + Text("settings_advanced_download_apply_remote_provisioning") + .default_text_style_white_600(styleSize: 15) + } + ) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background(settingsViewModel.remoteProvisioningUrl.isEmpty ? Color.orangeMain100 : Color.orangeMain500) + .cornerRadius(60) + .disabled(settingsViewModel.remoteProvisioningUrl.isEmpty) + } + } + .padding(.vertical, 30) + .padding(.horizontal, 20) + .background(Color.gray100) + + /* + HStack(alignment: .center) { + Text("settings_advanced_audio_devices_title") + .default_text_style_800(styleSize: 18) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + + Image(audioDevicesIsOpen ? "caret-up" : "caret-down") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c600) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.all, 10) + } + .padding(.vertical, 10) + .padding(.horizontal, 20) + .background(Color.gray100) + .onTapGesture { + withAnimation { + audioDevicesIsOpen.toggle() + } + } + + if audioDevicesIsOpen { + VStack(spacing: 0) { + VStack(spacing: 30) { + VStack(alignment: .leading) { + Text("call_stats_media_encryption_title") + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + Menu { + Button("None") { } // settingsViewModel.defaultLayout = ""} + Button("SRTP") { } // settingsViewModel.defaultLayout = ""} + Button("ZRTP") { } // settingsViewModel.defaultLayout = ""} + Button("DTLS") { } // settingsViewModel.defaultLayout = ""} + } label: { + Text("ZRTP") + .default_text_style(styleSize: 15) + .frame(maxWidth: .infinity, alignment: .leading) + Image("caret-down") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 20, height: 20) + } + .frame(height: 25) + .padding(.horizontal, 20) + .padding(.vertical, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(Color.gray200, lineWidth: 1) + ) + } + + VStack(alignment: .leading) { + Text("call_stats_media_encryption_title") + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + Menu { + Button("None") { } // settingsViewModel.defaultLayout = ""} + Button("SRTP") { } // settingsViewModel.defaultLayout = ""} + Button("ZRTP") { } // settingsViewModel.defaultLayout = ""} + Button("DTLS") { } // settingsViewModel.defaultLayout = ""} + } label: { + Text("ZRTP") + .default_text_style(styleSize: 15) + .frame(maxWidth: .infinity, alignment: .leading) + Image("caret-down") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 20, height: 20) + } + .frame(height: 25) + .padding(.horizontal, 20) + .padding(.vertical, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(Color.gray200, lineWidth: 1) + ) + } + } + .padding(.vertical, 30) + .padding(.horizontal, 20) + } + .background(.white) + .cornerRadius(15) + .padding(.horizontal) + .zIndex(-1) + .transition(.move(edge: .top)) + } + + HStack(alignment: .center) { + Text("settings_advanced_audio_codecs_title") + .default_text_style_800(styleSize: 18) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + + Image(audioCodecsIsOpen ? "caret-up" : "caret-down") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c600) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.all, 10) + } + .padding(.vertical, 10) + .padding(.horizontal, 20) + .background(Color.gray100) + .onTapGesture { + withAnimation { + audioCodecsIsOpen.toggle() + } + } + + if audioCodecsIsOpen { + VStack(spacing: 0) { + VStack(spacing: 30) { + Toggle("settings_calls_adaptive_rate_control_title", isOn: $settingsViewModel.adaptiveRateControl) + .default_text_style_700(styleSize: 15) + + Toggle("settings_calls_enable_video_title", isOn: $settingsViewModel.enableVideo) + .default_text_style_700(styleSize: 15) + + Toggle("settings_calls_auto_record_title", isOn: $settingsViewModel.autoRecord) + .default_text_style_700(styleSize: 15) + } + .padding(.vertical, 30) + .padding(.horizontal, 20) + } + .background(.white) + .cornerRadius(15) + .padding(.horizontal) + .zIndex(-2) + .transition(.move(edge: .top)) + } + + HStack(alignment: .center) { + Text("settings_advanced_video_codecs_title") + .default_text_style_800(styleSize: 18) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + + Image(videoCodecsIsOpen ? "caret-up" : "caret-down") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c600) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.all, 10) + } + .padding(.vertical, 10) + .padding(.horizontal, 20) + .background(Color.gray100) + .onTapGesture { + withAnimation { + videoCodecsIsOpen.toggle() + } + } + + if videoCodecsIsOpen { + VStack(spacing: 0) { + VStack(spacing: 30) { + Toggle("settings_conversations_auto_download_title", isOn: $settingsViewModel.autoDownload) + .default_text_style_700(styleSize: 15) + } + .padding(.vertical, 30) + .padding(.horizontal, 20) + } + .background(.white) + .cornerRadius(15) + .padding(.horizontal) + .zIndex(-3) + .transition(.move(edge: .top)) + } + */ + } + } + .background(Color.gray100) + } + .background(Color.gray100) + } + .navigationTitle("") + .navigationBarHidden(true) + } +} diff --git a/Linphone/UI/Main/Settings/Fragments/SettingsFragment.swift b/Linphone/UI/Main/Settings/Fragments/SettingsFragment.swift index 9bf990bda..c65572ca0 100644 --- a/Linphone/UI/Main/Settings/Fragments/SettingsFragment.swift +++ b/Linphone/UI/Main/Settings/Fragments/SettingsFragment.swift @@ -25,7 +25,6 @@ struct SettingsFragment: View { @Binding var isShowSettingsFragment: Bool - @State private var isOn: Bool = false @State var securityIsOpen: Bool = false @State var callsIsOpen: Bool = false @State var conversationsIsOpen: Bool = false @@ -440,28 +439,29 @@ struct SettingsFragment: View { .transition(.move(edge: .top)) } */ - HStack(alignment: .center) { - Text("settings_advanced_title") - .default_text_style_800(styleSize: 18) - .frame(maxWidth: .infinity, alignment: .leading) + NavigationLink(destination: { + SettingsAdvancedFragment(settingsViewModel: settingsViewModel) + }, label: { + HStack(alignment: .center) { + Text("settings_advanced_title") + .default_text_style_800(styleSize: 18) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + + Image("caret-right") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c600) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.all, 10) + } + .frame(maxWidth: .infinity) - Spacer() - - Image("caret-right") - .renderingMode(.template) - .resizable() - .foregroundStyle(Color.grayMain2c600) - .frame(width: 25, height: 25, alignment: .leading) - .padding(.all, 10) - } + }) .padding(.vertical, 10) .padding(.horizontal, 20) .background(Color.gray100) - .onTapGesture { - withAnimation { - //networkIsOpen.toggle() - } - } } } .background(Color.gray100) diff --git a/Linphone/UI/Main/Settings/ViewModel/SettingsViewModel.swift b/Linphone/UI/Main/Settings/ViewModel/SettingsViewModel.swift index b32962b16..d4562adbc 100644 --- a/Linphone/UI/Main/Settings/ViewModel/SettingsViewModel.swift +++ b/Linphone/UI/Main/Settings/ViewModel/SettingsViewModel.swift @@ -27,7 +27,7 @@ class SettingsViewModel: ObservableObject { // Security settings @Published var enableVfs: Bool = false - //@Published var preventScreenshots: Bool = false + // @Published var preventScreenshots: Bool = false // Calls settings @Published var adaptiveRateControl: Bool = false @@ -44,6 +44,16 @@ class SettingsViewModel: ObservableObject { @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 = "" + init() { CoreContext.shared.doOnCoreQueue { core in @@ -60,6 +70,26 @@ class SettingsViewModel: ObservableObject { 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 @@ -74,6 +104,17 @@ class SettingsViewModel: ObservableObject { 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.coreDelegate = CoreDelegateStub( onAudioDevicesListUpdated: { (_: Core) in Log.info( @@ -95,6 +136,22 @@ class SettingsViewModel: ObservableObject { } } + 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 saveChangesWhenLeaving() { CoreContext.shared.doOnCoreQueue { core in if CorePreferences.vfsEnabled != self.enableVfs { @@ -129,6 +186,49 @@ class SettingsViewModel: ObservableObject { 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 + } } } }