From eb68b50a43e049d11d336ab125c64e08c3b3ea3b Mon Sep 17 00:00:00 2001 From: Benoit Martins Date: Fri, 10 Jan 2025 16:08:14 +0100 Subject: [PATCH] Add settings view --- Linphone.xcodeproj/project.pbxproj | 14 + Linphone/Core/CorePreferences.swift | 241 +++++ Linphone/Localizable.xcstrings | 879 ++++++++++++++++++ Linphone/UI/Main/ContentView.swift | 13 +- Linphone/UI/Main/Fragments/SideMenu.swift | 13 +- .../Settings/Fragments/SettingsFragment.swift | 476 ++++++++++ .../ViewModel/SettingsViewModel.swift | 134 +++ 7 files changed, 1767 insertions(+), 3 deletions(-) create mode 100644 Linphone/Core/CorePreferences.swift create mode 100644 Linphone/UI/Main/Settings/ViewModel/SettingsViewModel.swift diff --git a/Linphone.xcodeproj/project.pbxproj b/Linphone.xcodeproj/project.pbxproj index 0d0386081..195605b87 100644 --- a/Linphone.xcodeproj/project.pbxproj +++ b/Linphone.xcodeproj/project.pbxproj @@ -103,6 +103,7 @@ D732A9132B04C7A300DB42BA /* HistoryListFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A9122B04C7A300DB42BA /* HistoryListFragment.swift */; }; D732A9152B04C7FE00DB42BA /* HistoryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A9142B04C7FE00DB42BA /* HistoryListViewModel.swift */; }; D732A91B2B061BD900DB42BA /* HistoryListBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A91A2B061BD900DB42BA /* HistoryListBottomSheet.swift */; }; + D732C38C2D311D2500F78100 /* SettingsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732C38B2D311D2100F78100 /* SettingsFragment.swift */; }; D73449992BC6932A00778C56 /* MeetingWaitingRoomFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73449982BC6932A00778C56 /* MeetingWaitingRoomFragment.swift */; }; D734499B2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D734499A2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift */; }; D748BF2C2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */; }; @@ -114,6 +115,9 @@ D74C9D012ACB098C0021626A /* PermissionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74C9D002ACB098C0021626A /* PermissionManager.swift */; }; D74DA0122C047F0700A8561D /* HistoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74DA0112C047F0700A8561D /* HistoryModel.swift */; }; D750D3392AD3E6EE00EC99C5 /* PopupLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D750D3382AD3E6EE00EC99C5 /* PopupLoadingView.swift */; }; + D756C8152D34FF9200A58F2F /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D756C8142D34FF8900A58F2F /* SettingsViewModel.swift */; }; + D756C8172D352C5F00A58F2F /* CorePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D756C8162D352C5600A58F2F /* CorePreferences.swift */; }; + D756C8182D352C5F00A58F2F /* CorePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D756C8162D352C5600A58F2F /* CorePreferences.swift */; }; D75759322B56D40900E7AC10 /* ZRTPPopup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75759312B56D40900E7AC10 /* ZRTPPopup.swift */; }; D759CB642C3FBD4200AC35E8 /* StartConversationFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D759CB632C3FBD4200AC35E8 /* StartConversationFragment.swift */; }; D759CB662C3FBE1D00AC35E8 /* StartConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D759CB652C3FBE1D00AC35E8 /* StartConversationViewModel.swift */; }; @@ -300,6 +304,7 @@ D732A9122B04C7A300DB42BA /* HistoryListFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListFragment.swift; sourceTree = ""; }; D732A9142B04C7FE00DB42BA /* HistoryListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListViewModel.swift; sourceTree = ""; }; D732A91A2B061BD900DB42BA /* HistoryListBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListBottomSheet.swift; sourceTree = ""; }; + D732C38B2D311D2100F78100 /* SettingsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFragment.swift; sourceTree = ""; }; D73449982BC6932A00778C56 /* MeetingWaitingRoomFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingWaitingRoomFragment.swift; sourceTree = ""; }; D734499A2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingWaitingRoomViewModel.swift; sourceTree = ""; }; D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdPartySipAccountLoginFragment.swift; sourceTree = ""; }; @@ -311,6 +316,8 @@ D74C9D002ACB098C0021626A /* PermissionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionManager.swift; sourceTree = ""; }; D74DA0112C047F0700A8561D /* HistoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryModel.swift; sourceTree = ""; }; D750D3382AD3E6EE00EC99C5 /* PopupLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupLoadingView.swift; sourceTree = ""; }; + D756C8142D34FF8900A58F2F /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = ""; }; + D756C8162D352C5600A58F2F /* CorePreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CorePreferences.swift; sourceTree = ""; }; D75759312B56D40900E7AC10 /* ZRTPPopup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZRTPPopup.swift; sourceTree = ""; }; D759CB632C3FBD4200AC35E8 /* StartConversationFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartConversationFragment.swift; sourceTree = ""; }; D759CB652C3FBE1D00AC35E8 /* StartConversationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartConversationViewModel.swift; sourceTree = ""; }; @@ -620,6 +627,7 @@ D719ABC72ABC6FB200B41C10 /* Core */ = { isa = PBXGroup; children = ( + D756C8162D352C5600A58F2F /* CorePreferences.swift */, D719ABC82ABC6FD700B41C10 /* CoreContext.swift */, 66C491FA2B24D32600CEA16D /* CoreExtension.swift */, ); @@ -931,6 +939,7 @@ D7DC096B2CFA192F00A6D47C /* Fragments */ = { isa = PBXGroup; children = ( + D732C38B2D311D2100F78100 /* SettingsFragment.swift */, D7C5003F2D27F16900DD53EC /* AccountSettingsFragment.swift */, D7DC096E2CFA1D7400A6D47C /* AccountProfileFragment.swift */, ); @@ -947,6 +956,7 @@ D7DC096D2CFA194600A6D47C /* ViewModel */ = { isa = PBXGroup; children = ( + D756C8142D34FF8900A58F2F /* SettingsViewModel.swift */, D7C500412D2BE96E00DD53EC /* AccountSettingsViewModel.swift */, D7DC09702CFDBF8300A6D47C /* AccountProfileViewModel.swift */, ); @@ -1147,6 +1157,7 @@ 66FBFC492B83BD2400BC6AB1 /* ConfigExtension.swift in Sources */, 66FBFC4A2B83BD3300BC6AB1 /* FileUtils.swift in Sources */, 66FBFC4B2B83BD7B00BC6AB1 /* CoreExtension.swift in Sources */, + D756C8172D352C5F00A58F2F /* CorePreferences.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1252,6 +1263,7 @@ 6613A0B42BAEBE3F008923A4 /* MeetingViewModel.swift in Sources */, D7173EBE2B7A5C0A00BCC481 /* LinphoneUtils.swift in Sources */, 66C492012B24DB6900CEA16D /* Log.swift in Sources */, + D756C8182D352C5F00A58F2F /* CorePreferences.swift in Sources */, C6A5A9432C10B5ED0070FEA4 /* DecodableExtension.swift in Sources */, D714035B2BE11E00004BD8CA /* CallMediaEncryptionModel.swift in Sources */, D748BF2C2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift in Sources */, @@ -1273,6 +1285,7 @@ D72A9A052B9750A1000DC093 /* UIList.swift in Sources */, D726E43D2B19E4FE0083C415 /* StartCallFragment.swift in Sources */, 66E56BCC2BA9A1E0006CE56F /* MeetingsListItemModel.swift in Sources */, + D756C8152D34FF9200A58F2F /* SettingsViewModel.swift in Sources */, D7B99E9B2B29F7C300BE7BF2 /* ActivityIndicator.swift in Sources */, 66F626B22BCEBB86003E2DEC /* AddParticipantsFragment.swift in Sources */, D72343302ACEFEF8009AA24E /* QrCodeScannerFragment.swift in Sources */, @@ -1290,6 +1303,7 @@ D7CEE0352B7A210300FD79B7 /* ConversationsView.swift in Sources */, D717071E2AC5922E0037746F /* ColorExtension.swift in Sources */, D71968922B86369D00DF4459 /* ChatBubbleView.swift in Sources */, + D732C38C2D311D2500F78100 /* SettingsFragment.swift in Sources */, D78290B82ADD3910004AA85C /* ContactsFragment.swift in Sources */, D7DA67642ACCB31700E95002 /* ProfileModeFragment.swift in Sources */, D7CEE03D2B7A23B200FD79B7 /* ConversationsListFragment.swift in Sources */, diff --git a/Linphone/Core/CorePreferences.swift b/Linphone/Core/CorePreferences.swift new file mode 100644 index 000000000..0f110b469 --- /dev/null +++ b/Linphone/Core/CorePreferences.swift @@ -0,0 +1,241 @@ +/* + * 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 Foundation +import linphonesw + +class CorePreferences { + static var printLogsInLogcat: Bool { + get { + return Config.get().getBool(section: "app", key: "debug", defaultValue: true) + } + set { + Config.get().setBool(section: "app", key: "debug", value: newValue) + } + } + + static var firstLaunch: Bool { + get { + return Config.get().getBool(section: "app", key: "first_6.0_launch", defaultValue: true) + } + set { + Config.get().setBool(section: "app", key: "first_6.0_launch", value: newValue) + } + } + + static var linphoneConfigurationVersion: Int { + get { + return Config.get().getInt(section: "app", key: "config_version", defaultValue: 52005) + } + set { + Config.get().setInt(section: "app", key: "config_version", value: newValue) + } + } + + static var checkForUpdateServerUrl: String { + get { + return Config.get().getString(section: "misc", key: "version_check_url_root", defaultString: "") + } + set { + Config.get().setString(section: "misc", key: "version_check_url_root", value: newValue) + } + } + + static var conditionsAndPrivacyPolicyAccepted: Bool { + get { + return Config.get().getBool(section: "app", key: "read_and_agree_terms_and_privacy", defaultValue: false) + } + set { + Config.get().setBool(section: "app", key: "read_and_agree_terms_and_privacy", value: newValue) + } + } + + static var publishPresence: Bool { + get { + return Config.get().getBool(section: "app", key: "publish_presence", defaultValue: true) + } + set { + Config.get().setBool(section: "app", key: "publish_presence", value: newValue) + } + } + + static var keepServiceAlive: Bool { + get { + return Config.get().getBool(section: "app", key: "keep_service_alive", defaultValue: false) + } + set { + Config.get().setBool(section: "app", key: "keep_service_alive", value: newValue) + } + } + + static var deviceName: String { + get { + return Config.get().getString(section: "app", key: "device", defaultString: "").trimmingCharacters(in: .whitespaces) + } + set { + Config.get().setString(section: "app", key: "device", value: newValue.trimmingCharacters(in: .whitespaces)) + } + } + + static var routeAudioToSpeakerWhenVideoIsEnabled: Bool { + get { + return Config.get().getBool(section: "app", key: "route_audio_to_speaker_when_video_enabled", defaultValue: true) + } + set { + Config.get().setBool(section: "app", key: "route_audio_to_speaker_when_video_enabled", value: newValue) + } + } + + static var automaticallyStartCallRecording: Bool { + get { + return Config.get().getBool(section: "app", key: "auto_start_call_record", defaultValue: false) + } + set { + Config.get().setBool(section: "app", key: "auto_start_call_record", value: newValue) + } + } + + static var showDialogWhenCallingDeviceUuidDirectly: Bool { + get { + return Config.get().getBool(section: "app", key: "show_confirmation_dialog_zrtp_trust_call", defaultValue: true) + } + set { + Config.get().setBool(section: "app", key: "show_confirmation_dialog_zrtp_trust_call", value: newValue) + } + } + + static var markConversationAsReadWhenDismissingMessageNotification: Bool { + get { + return Config.get().getBool(section: "app", key: "mark_as_read_notif_dismissal", defaultValue: false) + } + set { + Config.get().setBool(section: "app", key: "mark_as_read_notif_dismissal", value: newValue) + } + } + + static var contactsFilter: String { + get { + return Config.get().getString(section: "ui", key: "contacts_filter", defaultString: "") + } + set { + Config.get().setString(section: "ui", key: "contacts_filter", value: newValue) + } + } + + static var showFavoriteContacts: Bool { + get { + return Config.get().getBool(section: "ui", key: "show_favorites_contacts", defaultValue: true) + } + set { + Config.get().setBool(section: "ui", key: "show_favorites_contacts", value: newValue) + } + } + + static var voiceRecordingMaxDuration: Int { + get { + return Config.get().getInt(section: "app", key: "voice_recording_max_duration", defaultValue: 600000) + } + set { + Config.get().setInt(section: "app", key: "voice_recording_max_duration", value: newValue) + } + } + + static var darkMode: Int { + get { + if !darkModeAllowed { return 0 } + return Config.get().getInt(section: "app", key: "dark_mode", defaultValue: -1) + } + set { + Config.get().setInt(section: "app", key: "dark_mode", value: newValue) + } + } + + static var enableSecureMode: Bool { + get { + return Config.get().getBool(section: "ui", key: "enable_secure_mode", defaultValue: true) + } + set { + Config.get().setBool(section: "ui", key: "enable_secure_mode", value: newValue) + } + } + + static var themeMainColor: String { + get { + return Config.get().getString(section: "ui", key: "theme_main_color", defaultString: "orange") + } + set { + Config.get().setString(section: "ui", key: "theme_main_color", value: newValue) + } + } + + static var darkModeAllowed: Bool { + return Config.get().getBool(section: "ui", key: "dark_mode_allowed", defaultValue: true) + } + + static var changeMainColorAllowed: Bool { + return Config.get().getBool(section: "ui", key: "change_main_color_allowed", defaultValue: false) + } + + static var hideSettings: Bool { + return Config.get().getBool(section: "ui", key: "hide_settings", defaultValue: false) + } + + static var maxAccountsCount: Int { + return Config.get().getInt(section: "ui", key: "max_account", defaultValue: 0) + } + + /* + static var configPath: String { + return context.view.window?.rootViewController?.view.frame.origin.x ?? "" + "/.linphonerc" + } + + static var factoryConfigPath: String { + return context.view.window?.rootViewController?.view.frame.origin.x ?? "" + "/linphonerc" + } + + func copyAssetsFromPackage() { + copy(from: "linphonerc_default", to: configPath) + copy(from: "linphonerc_factory", to: factoryConfigPath, overrideIfExists: true) + } + */ + + static var vfsEnabled: Bool { + get { + return Config.get().getBool(section: "app", key: "vfs_enabled", defaultValue: false) + } + set { + Config.get().setBool(section: "app", key: "vfs_enabled", value: newValue) + } + } + + private func copy(from: String, to: String, overrideIfExists: Bool = false) { + let fileManager = FileManager.default + if fileManager.fileExists(atPath: to), !overrideIfExists { + return + } + + if let assetPath = Bundle.main.path(forResource: from, ofType: "") { + do { + try fileManager.copyItem(atPath: assetPath, toPath: to) + } catch { + print("Error copying file: \(error)") + } + } + } +} diff --git a/Linphone/Localizable.xcstrings b/Linphone/Localizable.xcstrings index a23575b43..b1a67bb9b 100644 --- a/Linphone/Localizable.xcstrings +++ b/Linphone/Localizable.xcstrings @@ -5668,6 +5668,885 @@ } } }, + "settings_calls_adaptive_rate_control_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adaptive rate control" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrôle qualité adaptatif" + } + } + } + }, + "settings_calls_auto_record_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatically start recording calls" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Démarrer l'enregistrement des appels automatiquement" + } + } + } + }, + "settings_calls_calibrate_echo_canceller_done" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ ms" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ ms" + } + } + } + }, + "settings_calls_calibrate_echo_canceller_done_no_echo" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "no echo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "pas d'écho" + } + } + } + }, + "settings_calls_calibrate_echo_canceller_failed" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "failed" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "échec" + } + } + } + }, + "settings_calls_calibrate_echo_canceller_in_progress" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "in progress" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "en cours" + } + } + } + }, + "settings_calls_calibrate_echo_canceller_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Calibrate echo canceller" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Calibrer l'annulateur d'écho" + } + } + } + }, + "settings_calls_change_ringtone_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Change ringtone" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Changer de sonnerie" + } + } + } + }, + "settings_calls_echo_canceller_subtitle" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prevents echo from being heard by remote end if no hardware echo canceller is available" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Évite que de l'écho soit entendu par votre correspondant si un annulateur matériel n'est pas disponible" + } + } + } + }, + "settings_calls_echo_canceller_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Use software echo canceller" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utiliser l'annulateur d'écho logiciel" + } + } + } + }, + "settings_calls_enable_fec_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable video FEC" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer la FEC vidéo" + } + } + } + }, + "settings_calls_enable_video_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable video" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer la vidéo" + } + } + } + }, + "settings_calls_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Calls" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appels" + } + } + } + }, + "settings_calls_vibrate_while_ringing_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vibrate while incoming call is ringing" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vibrer lors de la réception d'un appel" + } + } + } + }, + "settings_contacts_add_carddav_server_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add CardDAV address book" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouter un carnet d'adresse CardDAV" + } + } + } + }, + "settings_contacts_add_ldap_server_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add LDAP server" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouter un serveur LDAP" + } + } + } + }, + "settings_contacts_carddav_deleted_toast" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "CardDAV account deleted" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compte CardDAV supprimé" + } + } + } + }, + "settings_contacts_carddav_mandatory_field_not_filled_toast" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Please fill in at least the display name and server URL" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Veuillez remplir au moins le nom d'affichage et l'URL du serveur" + } + } + } + }, + "settings_contacts_carddav_name_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Display name" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom d'affichage" + } + } + } + }, + "settings_contacts_carddav_password_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mot de passe" + } + } + } + }, + "settings_contacts_carddav_realm_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authentication realm" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Domaine d'authentification" + } + } + } + }, + "settings_contacts_carddav_server_url_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server URL" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL du serveur" + } + } + } + }, + "settings_contacts_carddav_sync_error_toast" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronization error!" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur de synchronization !" + } + } + } + }, + "settings_contacts_carddav_sync_successful_toast" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronization successful" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronization réussie" + } + } + } + }, + "settings_contacts_carddav_use_as_default_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Store newly created contacts here" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stocker ici les contacts nouvellement crées" + } + } + } + }, + "settings_contacts_carddav_username_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom d'utilisateur" + } + } + } + }, + "settings_contacts_edit_carddav_server_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit CardDAV address book" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editer le carnet d'adresse CardDAV" + } + } + } + }, + "settings_contacts_edit_ldap_server_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit LDAP server" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editer le serveur LDAP" + } + } + } + }, + "settings_contacts_ldap_bind_dn_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bind DN" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bind DN" + } + } + } + }, + "settings_contacts_ldap_bind_user_password_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bind user password" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mot de passe de l'utilisateur Bind" + } + } + } + }, + "settings_contacts_ldap_max_results_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Maximum results" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre de résultats maximum" + } + } + } + }, + "settings_contacts_ldap_password_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mot de passe" + } + } + } + }, + "settings_contacts_ldap_request_timeout_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Request timeout" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Délai d'attente de la requête" + } + } + } + }, + "settings_contacts_ldap_search_base_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search base (can't be empty)" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Base de recherche (ne peut être vide)" + } + } + } + }, + "settings_contacts_ldap_search_filter_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filter" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filtre" + } + } + } + }, + "settings_contacts_ldap_server_url_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server URL (can't be empty)" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL du serveur (ne peut être vide)" + } + } + } + }, + "settings_contacts_ldap_use_tls_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Use TLS" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utiliser TLS" + } + } + } + }, + "settings_contacts_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contacts" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contacts" + } + } + } + }, + "settings_conversations_auto_download_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auto-download files" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Télécharger automatiquement les fichiers" + } + } + } + }, + "settings_conversations_mark_as_read_when_dismissing_notif_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mark conversation as read when dismissing message notification" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Marquer la conversation comme lue lorsqu'une notification de message est supprimée" + } + } + } + }, + "settings_conversations_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conversations" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conversations" + } + } + } + }, + "settings_meetings_default_layout_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Default layout" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disposition par défaut" + } + } + } + }, + "settings_meetings_layout_active_speaker_label" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Active speaker" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervenant actif" + } + } + } + }, + "settings_meetings_layout_mosaic_label" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mosaic" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mosaïque" + } + } + } + }, + "settings_meetings_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meetings" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réunions" + } + } + } + }, + "settings_network_allow_ipv6" : { + "comment" : "Autoriser l'IPv6", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allow IPv6" + } + } + } + }, + "settings_network_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Network" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réseau" + } + } + } + }, + "settings_network_use_wifi_only" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Use only Wi-Fi networks" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se connecter uniquement via le Wi-Fi" + } + } + } + }, + "settings_security_enable_vfs_subtitle" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warning: once enabled it can't be disabled!" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attention, vous ne pourrez pas revenir en arrière !" + } + } + } + }, + "settings_security_enable_vfs_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encrypt everything" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chiffrer tous les fichiers" + } + } + } + }, + "settings_security_prevent_screenshots_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prevent interface from being recorded" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Empêcher l'interface d'être enregistrée" + } + } + } + }, + "settings_security_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Security" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Securité" + } + } + } + }, "settings_title" : { "extractionState" : "manual", "localizations" : { diff --git a/Linphone/UI/Main/ContentView.swift b/Linphone/UI/Main/ContentView.swift index 0278368ee..b22ee302f 100644 --- a/Linphone/UI/Main/ContentView.swift +++ b/Linphone/UI/Main/ContentView.swift @@ -72,6 +72,7 @@ struct ContentView: View { @State var isShowSipAddressesPopupType = 0 // 0 to call, 1 to message, 2 to video call @State var isShowConversationFragment = false @State var isShowAccountProfileFragment = false + @State var isShowSettingsFragment = false @State var fullscreenVideo = false @@ -949,7 +950,8 @@ struct ContentView: View { menuClose: self.openMenu, safeAreaInsets: geometry.safeAreaInsets, isShowLoginFragment: $isShowLoginFragment, - isShowAccountProfileFragment: $isShowAccountProfileFragment + isShowAccountProfileFragment: $isShowAccountProfileFragment, + isShowSettingsFragment: $isShowSettingsFragment ) .ignoresSafeArea(.all) .zIndex(2) @@ -1188,6 +1190,15 @@ struct ContentView: View { .transition(.move(edge: .trailing)) } + if isShowSettingsFragment { + SettingsFragment( + settingsViewModel: SettingsViewModel(), + isShowSettingsFragment: $isShowSettingsFragment + ) + .zIndex(3) + .transition(.move(edge: .trailing)) + } + if isShowSendCancelMeetingNotificationPopup { PopupView(isShowPopup: $isShowSendCancelMeetingNotificationPopup, title: Text("meeting_schedule_cancel_dialog_title"), diff --git a/Linphone/UI/Main/Fragments/SideMenu.swift b/Linphone/UI/Main/Fragments/SideMenu.swift index bb5c3a1a6..bbad9bbea 100644 --- a/Linphone/UI/Main/Fragments/SideMenu.swift +++ b/Linphone/UI/Main/Fragments/SideMenu.swift @@ -31,6 +31,7 @@ struct SideMenu: View { let safeAreaInsets: EdgeInsets @Binding var isShowLoginFragment: Bool @Binding var isShowAccountProfileFragment: Bool + @Binding var isShowSettingsFragment: Bool @State private var showHelp = false var body: some View { @@ -124,14 +125,21 @@ struct SideMenu: View { ForEach(0... + */ + +import SwiftUI +import UniformTypeIdentifiers + +struct SettingsFragment: View { + @ObservedObject var settingsViewModel: SettingsViewModel + + @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 + @State var contactsIsOpen: Bool = false + @State var meetingsIsOpen: Bool = false + @State var networkIsOpen: Bool = false + @State var userInterfaceIsOpen: Bool = false + + var body: some View { + NavigationView { + 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 { + settingsViewModel.saveChangesWhenLeaving() + withAnimation { + if isShowSettingsFragment { + isShowSettingsFragment = false + } + } + } + + 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) { + HStack(alignment: .center) { + Text("settings_security_title") + .default_text_style_800(styleSize: 18) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + + Image(securityIsOpen ? "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 { + securityIsOpen.toggle() + } + } + + if securityIsOpen { + VStack(spacing: 0) { + VStack(spacing: 30) { + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("settings_security_enable_vfs_title") + .default_text_style_700(styleSize: 15) + Text("settings_security_enable_vfs_subtitle") + .foregroundColor(Color.grayMain2c500) + .default_text_style(styleSize: 14) + } + .layoutPriority(1) + + Toggle(isOn: $settingsViewModel.enableVfs) { + EmptyView() + } + .toggleStyle(SwitchToggleStyle()) + .disabled(settingsViewModel.enableVfs) + } + + /* + Toggle("settings_security_prevent_screenshots_title", isOn: $isOn) + .default_text_style_700(styleSize: 15) + */ + } + .padding(.vertical, 30) + .padding(.horizontal, 20) + } + .background(.white) + .cornerRadius(15) + .padding(.horizontal) + .zIndex(-1) + .transition(.move(edge: .top)) + } + + HStack(alignment: .center) { + Text("settings_calls_title") + .default_text_style_800(styleSize: 18) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + + Image(callsIsOpen ? "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 { + callsIsOpen.toggle() + } + } + + if callsIsOpen { + 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_vibrate_while_ringing_title", isOn: $isOn) + .default_text_style_700(styleSize: 15) + */ + + Toggle("settings_calls_auto_record_title", isOn: $settingsViewModel.autoRecord) + .default_text_style_700(styleSize: 15) + + /* + Button { + } label: { + HStack { + Text("settings_calls_change_ringtone_title") + .default_text_style_700(styleSize: 15) + .frame(maxWidth: .infinity, alignment: .leading) + + Image("arrow-square-out") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c600) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.trailing, 10) + } + } + */ + } + .padding(.vertical, 30) + .padding(.horizontal, 20) + } + .background(.white) + .cornerRadius(15) + .padding(.horizontal) + .zIndex(-2) + .transition(.move(edge: .top)) + } + + HStack(alignment: .center) { + Text("settings_conversations_title") + .default_text_style_800(styleSize: 18) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + + Image(conversationsIsOpen ? "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 { + conversationsIsOpen.toggle() + } + } + + if conversationsIsOpen { + 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)) + } + + /* + // Hide Contacts + + HStack(alignment: .center) { + Text("settings_contacts_title") + .default_text_style_800(styleSize: 18) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + + Image(contactsIsOpen ? "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 { + contactsIsOpen.toggle() + } + } + + if contactsIsOpen { + VStack(spacing: 0) { + VStack(spacing: 30) { + Toggle("account_settings_avpf_title", isOn: $isOn) + .default_text_style_700(styleSize: 15) + + Toggle("account_settings_avpf_title", isOn: $isOn) + .default_text_style_700(styleSize: 15) + } + .padding(.vertical, 30) + .padding(.horizontal, 20) + } + .background(.white) + .cornerRadius(15) + .padding(.horizontal) + .zIndex(-4) + .transition(.move(edge: .top)) + } + */ + + HStack(alignment: .center) { + Text("settings_meetings_title") + .default_text_style_800(styleSize: 18) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + + Image(meetingsIsOpen ? "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 { + meetingsIsOpen.toggle() + } + } + + if meetingsIsOpen { + VStack(spacing: 0) { + VStack(spacing: 30) { + VStack(alignment: .leading) { + Text("settings_meetings_default_layout_title") + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + Menu { + Button("settings_meetings_layout_active_speaker_label") { settingsViewModel.defaultLayout = String(localized: "settings_meetings_layout_active_speaker_label") } + Button("settings_meetings_layout_mosaic_label") { settingsViewModel.defaultLayout = String(localized: "settings_meetings_layout_mosaic_label") } + } label: { + Text(settingsViewModel.defaultLayout) + .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(-5) + .transition(.move(edge: .top)) + } + + HStack(alignment: .center) { + Text("settings_network_title") + .default_text_style_800(styleSize: 18) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + + Image(networkIsOpen ? "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 { + networkIsOpen.toggle() + } + } + + if networkIsOpen { + VStack(spacing: 0) { + VStack(spacing: 30) { + Toggle("settings_network_use_wifi_only", isOn: $settingsViewModel.useWifiOnly) + .default_text_style_700(styleSize: 15) + + Toggle("settings_network_allow_ipv6", isOn: $settingsViewModel.allowIpv6) + .default_text_style_700(styleSize: 15) + } + .padding(.vertical, 30) + .padding(.horizontal, 20) + } + .background(.white) + .cornerRadius(15) + .padding(.horizontal) + .zIndex(-6) + .transition(.move(edge: .top)) + } + + /* + // Hide User interface (Dark mode) + + HStack(alignment: .center) { + Text("manage_account_details_title") + .default_text_style_800(styleSize: 18) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + + Image(userInterfaceIsOpen ? "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 { + userInterfaceIsOpen.toggle() + } + } + + if userInterfaceIsOpen { + VStack(spacing: 0) { + VStack(spacing: 30) { + Toggle("account_settings_avpf_title", isOn: $isOn) + .default_text_style_700(styleSize: 15) + + Toggle("account_settings_avpf_title", isOn: $isOn) + .default_text_style_700(styleSize: 15) + } + .padding(.vertical, 30) + .padding(.horizontal, 20) + } + .background(.white) + .cornerRadius(15) + .padding(.horizontal) + .zIndex(-7) + .transition(.move(edge: .top)) + } + */ + 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) + } + .padding(.vertical, 10) + .padding(.horizontal, 20) + .background(Color.gray100) + .onTapGesture { + withAnimation { + //networkIsOpen.toggle() + } + } + } + } + .background(Color.gray100) + } + .background(Color.gray100) + } + .navigationTitle("") + .navigationBarHidden(true) + } + .navigationViewStyle(StackNavigationViewStyle()) + } +} diff --git a/Linphone/UI/Main/Settings/ViewModel/SettingsViewModel.swift b/Linphone/UI/Main/Settings/ViewModel/SettingsViewModel.swift new file mode 100644 index 000000000..b32962b16 --- /dev/null +++ b/Linphone/UI/Main/Settings/ViewModel/SettingsViewModel.swift @@ -0,0 +1,134 @@ +/* + * 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 + +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 + + 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 + + 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 + + self.coreDelegate = CoreDelegateStub( + onAudioDevicesListUpdated: { (_: Core) in + Log.info( + "\(SettingsViewModel.TAG) Audio devices list has changed, update available input/output audio devices list" + ) + // setupAudioDevices() + } + ) + core.addDelegate(delegate: self.coreDelegate!) + } + } + } + + deinit { + if let delegate = coreDelegate { + CoreContext.shared.doOnCoreQueue { core in + core.removeDelegate(delegate: delegate) + } + } + } + + 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 + } + } + } +}