diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 000000000..0133ab567 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,32 @@ +disabled_rules: +- trailing_whitespace +opt_in_rules: +- empty_count +- empty_string +excluded: +- Carthage +- Pods +- SwiftLint/Common/3rdPartyLib +line_length: + warning: 150 + error: 200 + ignores_function_declarations: true + ignores_comments: true + ignores_urls: true +function_body_length: + warning: 300 + error: 500 +function_parameter_count: + warning: 6 + error: 8 +type_body_length: + warning: 300 + error: 500 +file_length: + warning: 1000 + error: 1500 + ignore_comment_only_lines: true +cyclomatic_complexity: + warning: 15 + error: 25 +reporter: "xcode" diff --git a/Linphone.xcodeproj/project.pbxproj b/Linphone.xcodeproj/project.pbxproj index 439addb8d..c8f9d54ca 100644 --- a/Linphone.xcodeproj/project.pbxproj +++ b/Linphone.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + D70C93DE2AC2D0F60063CA3B /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = D70C93DD2AC2D0F60063CA3B /* Localizable.xcstrings */; }; + D717071E2AC5922E0037746F /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D717071D2AC5922E0037746F /* ColorExtension.swift */; }; + D71707202AC5989C0037746F /* TextExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D717071F2AC5989C0037746F /* TextExtension.swift */; }; D719ABB72ABC67BF00B41C10 /* LinphoneApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABB62ABC67BF00B41C10 /* LinphoneApp.swift */; }; D719ABB92ABC67BF00B41C10 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABB82ABC67BF00B41C10 /* ContentView.swift */; }; D719ABBB2ABC67BF00B41C10 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D719ABBA2ABC67BF00B41C10 /* Assets.xcassets */; }; @@ -14,9 +17,42 @@ D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABC82ABC6FD700B41C10 /* CoreContext.swift */; }; D719ABCC2ABC769C00B41C10 /* AssistantView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABCB2ABC769C00B41C10 /* AssistantView.swift */; }; D719ABCF2ABC779A00B41C10 /* AccountLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABCE2ABC779A00B41C10 /* AccountLoginViewModel.swift */; }; + D72343302ACEFEF8009AA24E /* QrCodeScannerFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D723432F2ACEFEF8009AA24E /* QrCodeScannerFragment.swift */; }; + D72343322ACEFF58009AA24E /* QRScannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72343312ACEFF58009AA24E /* QRScannerController.swift */; }; + D72343342ACEFFC3009AA24E /* QRScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72343332ACEFFC3009AA24E /* QRScanner.swift */; }; + D72343362AD037AF009AA24E /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72343352AD037AF009AA24E /* ToastView.swift */; }; + D748BF2C2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */; }; + D748BF2E2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D748BF2D2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift */; }; + D74C9CF82ACACECE0021626A /* WelcomePage1Fragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74C9CF72ACACECE0021626A /* WelcomePage1Fragment.swift */; }; + D74C9CFA2ACACF2D0021626A /* WelcomePage2Fragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74C9CF92ACACF2D0021626A /* WelcomePage2Fragment.swift */; }; + D74C9CFC2ACACF370021626A /* WelcomePage3Fragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74C9CFB2ACACF370021626A /* WelcomePage3Fragment.swift */; }; + D74C9CFF2ACAEC5E0021626A /* PopupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74C9CFE2ACAEC5E0021626A /* PopupView.swift */; }; + D74C9D012ACB098C0021626A /* PermissionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74C9D002ACB098C0021626A /* PermissionManager.swift */; }; + D750D3392AD3E6EE00EC99C5 /* PopupLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D750D3382AD3E6EE00EC99C5 /* PopupLoadingView.swift */; }; + D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7702EF12AC7205000557C00 /* WelcomeView.swift */; }; + D7A03FBD2ACC2DB60081A588 /* ContactsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A03FBC2ACC2DB60081A588 /* ContactsView.swift */; }; + D7A03FC02ACC2E390081A588 /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A03FBF2ACC2E390081A588 /* HistoryView.swift */; }; + D7A03FC62ACC458A0081A588 /* SplashScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A03FC52ACC458A0081A588 /* SplashScreen.swift */; }; + D7A2EDD62AC18115005D90FC /* SharedMainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A2EDD52AC18115005D90FC /* SharedMainViewModel.swift */; }; + D7D24D132AC1B4E800C6F35B /* NotoSans-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0D2AC1B4E800C6F35B /* NotoSans-Medium.ttf */; }; + D7D24D142AC1B4E800C6F35B /* NotoSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0E2AC1B4E800C6F35B /* NotoSans-Regular.ttf */; }; + D7D24D152AC1B4E800C6F35B /* NotoSans-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0F2AC1B4E800C6F35B /* NotoSans-Light.ttf */; }; + D7D24D162AC1B4E800C6F35B /* NotoSans-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D102AC1B4E800C6F35B /* NotoSans-SemiBold.ttf */; }; + D7D24D172AC1B4E800C6F35B /* NotoSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D112AC1B4E800C6F35B /* NotoSans-Bold.ttf */; }; + D7D24D182AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D122AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf */; }; + D7DA67622ACCB2FA00E95002 /* LoginFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA67612ACCB2FA00E95002 /* LoginFragment.swift */; }; + D7DA67642ACCB31700E95002 /* ProfileModeFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */; }; + D7EAACCF2AD6ED8000AA6A8A /* PermissionsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EAACCE2AD6ED8000AA6A8A /* PermissionsFragment.swift */; }; + D7FB55112AD447FD00A5AB15 /* RegisterFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FB55102AD447FD00A5AB15 /* RegisterFragment.swift */; }; + F4BB8DFBA0FF08430EBA9351 /* Pods_Linphone.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F5B27C5576B1EAED2F205EB /* Pods_Linphone.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 377E0B5C2B1F38192E694334 /* Pods-Linphone.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Linphone.debug.xcconfig"; path = "Target Support Files/Pods-Linphone/Pods-Linphone.debug.xcconfig"; sourceTree = ""; }; + 6F5B27C5576B1EAED2F205EB /* Pods_Linphone.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Linphone.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D70C93DD2AC2D0F60063CA3B /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; + D717071D2AC5922E0037746F /* ColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorExtension.swift; sourceTree = ""; }; + D717071F2AC5989C0037746F /* TextExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextExtension.swift; sourceTree = ""; }; D719ABB32ABC67BF00B41C10 /* Linphone.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Linphone.app; sourceTree = BUILT_PRODUCTS_DIR; }; D719ABB62ABC67BF00B41C10 /* LinphoneApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinphoneApp.swift; sourceTree = ""; }; D719ABB82ABC67BF00B41C10 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -26,6 +62,35 @@ D719ABC82ABC6FD700B41C10 /* CoreContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreContext.swift; sourceTree = ""; }; D719ABCB2ABC769C00B41C10 /* AssistantView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssistantView.swift; sourceTree = ""; }; D719ABCE2ABC779A00B41C10 /* AccountLoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountLoginViewModel.swift; sourceTree = ""; }; + D723432F2ACEFEF8009AA24E /* QrCodeScannerFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrCodeScannerFragment.swift; sourceTree = ""; }; + D72343312ACEFF58009AA24E /* QRScannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerController.swift; sourceTree = ""; }; + D72343332ACEFFC3009AA24E /* QRScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScanner.swift; sourceTree = ""; }; + D72343352AD037AF009AA24E /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = ""; }; + D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdPartySipAccountLoginFragment.swift; sourceTree = ""; }; + D748BF2D2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdPartySipAccountWarningFragment.swift; sourceTree = ""; }; + D74C9CF72ACACECE0021626A /* WelcomePage1Fragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomePage1Fragment.swift; sourceTree = ""; }; + D74C9CF92ACACF2D0021626A /* WelcomePage2Fragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomePage2Fragment.swift; sourceTree = ""; }; + D74C9CFB2ACACF370021626A /* WelcomePage3Fragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomePage3Fragment.swift; sourceTree = ""; }; + D74C9CFE2ACAEC5E0021626A /* PopupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupView.swift; sourceTree = ""; }; + D74C9D002ACB098C0021626A /* PermissionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionManager.swift; sourceTree = ""; }; + D750D3382AD3E6EE00EC99C5 /* PopupLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupLoadingView.swift; sourceTree = ""; }; + D7702EF12AC7205000557C00 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; + D7A03FBC2ACC2DB60081A588 /* ContactsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsView.swift; sourceTree = ""; }; + D7A03FBF2ACC2E390081A588 /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = ""; }; + D7A03FC52ACC458A0081A588 /* SplashScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashScreen.swift; sourceTree = ""; }; + D7A2EDD52AC18115005D90FC /* SharedMainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedMainViewModel.swift; sourceTree = ""; }; + D7A2EDDA2AC19EEC005D90FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + D7D24D0D2AC1B4E800C6F35B /* NotoSans-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Medium.ttf"; sourceTree = ""; }; + D7D24D0E2AC1B4E800C6F35B /* NotoSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Regular.ttf"; sourceTree = ""; }; + D7D24D0F2AC1B4E800C6F35B /* NotoSans-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Light.ttf"; sourceTree = ""; }; + D7D24D102AC1B4E800C6F35B /* NotoSans-SemiBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-SemiBold.ttf"; sourceTree = ""; }; + D7D24D112AC1B4E800C6F35B /* NotoSans-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Bold.ttf"; sourceTree = ""; }; + D7D24D122AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-ExtraBold.ttf"; sourceTree = ""; }; + D7DA67612ACCB2FA00E95002 /* LoginFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginFragment.swift; sourceTree = ""; }; + D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModeFragment.swift; sourceTree = ""; }; + D7EAACCE2AD6ED8000AA6A8A /* PermissionsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionsFragment.swift; sourceTree = ""; }; + D7FB55102AD447FD00A5AB15 /* RegisterFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterFragment.swift; sourceTree = ""; }; + F76FB87556A3109F61F9E2D5 /* Pods-Linphone.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Linphone.release.xcconfig"; path = "Target Support Files/Pods-Linphone/Pods-Linphone.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -33,17 +98,47 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + F4BB8DFBA0FF08430EBA9351 /* Pods_Linphone.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 52EFCC310713B3CA01062945 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6F5B27C5576B1EAED2F205EB /* Pods_Linphone.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + A31AF2AB8C6A3D7B7EA3B424 /* Pods */ = { + isa = PBXGroup; + children = ( + 377E0B5C2B1F38192E694334 /* Pods-Linphone.debug.xcconfig */, + F76FB87556A3109F61F9E2D5 /* Pods-Linphone.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + D717071C2AC591EF0037746F /* Utils */ = { + isa = PBXGroup; + children = ( + D717071D2AC5922E0037746F /* ColorExtension.swift */, + D717071F2AC5989C0037746F /* TextExtension.swift */, + D74C9D002ACB098C0021626A /* PermissionManager.swift */, + ); + path = Utils; + sourceTree = ""; + }; D719ABAA2ABC67BF00B41C10 = { isa = PBXGroup; children = ( D719ABB52ABC67BF00B41C10 /* Linphone */, D719ABB42ABC67BF00B41C10 /* Products */, + A31AF2AB8C6A3D7B7EA3B424 /* Pods */, + 52EFCC310713B3CA01062945 /* Frameworks */, ); sourceTree = ""; }; @@ -58,12 +153,17 @@ D719ABB52ABC67BF00B41C10 /* Linphone */ = { isa = PBXGroup; children = ( - D719ABC72ABC6FB200B41C10 /* core */, - D719ABC52ABC6EE800B41C10 /* ui */, + D7A03FC52ACC458A0081A588 /* SplashScreen.swift */, D719ABB62ABC67BF00B41C10 /* LinphoneApp.swift */, + D719ABC72ABC6FB200B41C10 /* Core */, + D719ABC52ABC6EE800B41C10 /* UI */, + D717071C2AC591EF0037746F /* Utils */, D719ABBA2ABC67BF00B41C10 /* Assets.xcassets */, D719ABBC2ABC67BF00B41C10 /* Linphone.entitlements */, + D7A2EDDA2AC19EEC005D90FC /* Info.plist */, + D70C93DD2AC2D0F60063CA3B /* Localizable.xcstrings */, D719ABBD2ABC67BF00B41C10 /* Preview Content */, + D7D24D0C2AC1B4C700C6F35B /* Fonts */, ); path = Linphone; sourceTree = ""; @@ -76,54 +176,134 @@ path = "Preview Content"; sourceTree = ""; }; - D719ABC52ABC6EE800B41C10 /* ui */ = { + D719ABC52ABC6EE800B41C10 /* UI */ = { isa = PBXGroup; children = ( - D719ABCA2ABC761800B41C10 /* assistant */, - D719ABC62ABC6F0200B41C10 /* main */, + D719ABCA2ABC761800B41C10 /* Assistant */, + D719ABC62ABC6F0200B41C10 /* Main */, + D7702EF02AC7200600557C00 /* Welcome */, ); - path = ui; + path = UI; sourceTree = ""; }; - D719ABC62ABC6F0200B41C10 /* main */ = { + D719ABC62ABC6F0200B41C10 /* Main */ = { isa = PBXGroup; children = ( - D719ABD02ABC7C4F00B41C10 /* viewmodel */, + D7A03FBB2ACC2D850081A588 /* Contacts */, + D74C9CFD2ACAEC150021626A /* Fragments */, + D7A03FBE2ACC2E010081A588 /* History */, + D7A2EDD42AC180FE005D90FC /* Viewmodel */, D719ABB82ABC67BF00B41C10 /* ContentView.swift */, ); - path = main; + path = Main; sourceTree = ""; }; - D719ABC72ABC6FB200B41C10 /* core */ = { + D719ABC72ABC6FB200B41C10 /* Core */ = { isa = PBXGroup; children = ( D719ABC82ABC6FD700B41C10 /* CoreContext.swift */, ); - path = core; + path = Core; sourceTree = ""; }; - D719ABCA2ABC761800B41C10 /* assistant */ = { + D719ABCA2ABC761800B41C10 /* Assistant */ = { isa = PBXGroup; children = ( - D719ABCD2ABC777600B41C10 /* viewmodel */, + D7DA67602ACCB2D700E95002 /* Fragments */, + D719ABCD2ABC777600B41C10 /* Viewmodel */, D719ABCB2ABC769C00B41C10 /* AssistantView.swift */, ); - path = assistant; + path = Assistant; sourceTree = ""; }; - D719ABCD2ABC777600B41C10 /* viewmodel */ = { + D719ABCD2ABC777600B41C10 /* Viewmodel */ = { isa = PBXGroup; children = ( D719ABCE2ABC779A00B41C10 /* AccountLoginViewModel.swift */, + D72343332ACEFFC3009AA24E /* QRScanner.swift */, + D72343312ACEFF58009AA24E /* QRScannerController.swift */, ); - path = viewmodel; + path = Viewmodel; sourceTree = ""; }; - D719ABD02ABC7C4F00B41C10 /* viewmodel */ = { + D74C9CF62ACACEB70021626A /* Fragments */ = { isa = PBXGroup; children = ( + D74C9CF72ACACECE0021626A /* WelcomePage1Fragment.swift */, + D74C9CF92ACACF2D0021626A /* WelcomePage2Fragment.swift */, + D74C9CFB2ACACF370021626A /* WelcomePage3Fragment.swift */, ); - path = viewmodel; + path = Fragments; + sourceTree = ""; + }; + D74C9CFD2ACAEC150021626A /* Fragments */ = { + isa = PBXGroup; + children = ( + D74C9CFE2ACAEC5E0021626A /* PopupView.swift */, + D72343352AD037AF009AA24E /* ToastView.swift */, + D750D3382AD3E6EE00EC99C5 /* PopupLoadingView.swift */, + ); + path = Fragments; + sourceTree = ""; + }; + D7702EF02AC7200600557C00 /* Welcome */ = { + isa = PBXGroup; + children = ( + D74C9CF62ACACEB70021626A /* Fragments */, + D7702EF12AC7205000557C00 /* WelcomeView.swift */, + ); + path = Welcome; + sourceTree = ""; + }; + D7A03FBB2ACC2D850081A588 /* Contacts */ = { + isa = PBXGroup; + children = ( + D7A03FBC2ACC2DB60081A588 /* ContactsView.swift */, + ); + path = Contacts; + sourceTree = ""; + }; + D7A03FBE2ACC2E010081A588 /* History */ = { + isa = PBXGroup; + children = ( + D7A03FBF2ACC2E390081A588 /* HistoryView.swift */, + ); + path = History; + sourceTree = ""; + }; + D7A2EDD42AC180FE005D90FC /* Viewmodel */ = { + isa = PBXGroup; + children = ( + D7A2EDD52AC18115005D90FC /* SharedMainViewModel.swift */, + ); + path = Viewmodel; + sourceTree = ""; + }; + D7D24D0C2AC1B4C700C6F35B /* Fonts */ = { + isa = PBXGroup; + children = ( + D7D24D0F2AC1B4E800C6F35B /* NotoSans-Light.ttf */, + D7D24D0E2AC1B4E800C6F35B /* NotoSans-Regular.ttf */, + D7D24D0D2AC1B4E800C6F35B /* NotoSans-Medium.ttf */, + D7D24D102AC1B4E800C6F35B /* NotoSans-SemiBold.ttf */, + D7D24D112AC1B4E800C6F35B /* NotoSans-Bold.ttf */, + D7D24D122AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf */, + ); + path = Fonts; + sourceTree = ""; + }; + D7DA67602ACCB2D700E95002 /* Fragments */ = { + isa = PBXGroup; + children = ( + D7DA67612ACCB2FA00E95002 /* LoginFragment.swift */, + D7EAACCE2AD6ED8000AA6A8A /* PermissionsFragment.swift */, + D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */, + D723432F2ACEFEF8009AA24E /* QrCodeScannerFragment.swift */, + D7FB55102AD447FD00A5AB15 /* RegisterFragment.swift */, + D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */, + D748BF2D2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift */, + ); + path = Fragments; sourceTree = ""; }; /* End PBXGroup section */ @@ -133,9 +313,12 @@ isa = PBXNativeTarget; buildConfigurationList = D719ABC22ABC67BF00B41C10 /* Build configuration list for PBXNativeTarget "Linphone" */; buildPhases = ( + 6FE8573A5CFC1DA89D3172B5 /* [CP] Check Pods Manifest.lock */, D719ABAF2ABC67BF00B41C10 /* Sources */, D719ABB02ABC67BF00B41C10 /* Frameworks */, D719ABB12ABC67BF00B41C10 /* Resources */, + D7FB55122AD53FE200A5AB15 /* Run Script */, + 230129DD87A6EBB04DF458AD /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -184,23 +367,114 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + D7D24D142AC1B4E800C6F35B /* NotoSans-Regular.ttf in Resources */, + D7D24D182AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf in Resources */, + D7D24D152AC1B4E800C6F35B /* NotoSans-Light.ttf in Resources */, + D7D24D162AC1B4E800C6F35B /* NotoSans-SemiBold.ttf in Resources */, + D7D24D172AC1B4E800C6F35B /* NotoSans-Bold.ttf in Resources */, D719ABBF2ABC67BF00B41C10 /* Preview Assets.xcassets in Resources */, D719ABBB2ABC67BF00B41C10 /* Assets.xcassets in Resources */, + D7D24D132AC1B4E800C6F35B /* NotoSans-Medium.ttf in Resources */, + D70C93DE2AC2D0F60063CA3B /* Localizable.xcstrings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 230129DD87A6EBB04DF458AD /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Linphone/Pods-Linphone-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Linphone/Pods-Linphone-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Linphone/Pods-Linphone-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 6FE8573A5CFC1DA89D3172B5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Linphone-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + D7FB55122AD53FE200A5AB15 /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 12; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\nif which swiftlint > /dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ D719ABAF2ABC67BF00B41C10 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D71707202AC5989C0037746F /* TextExtension.swift in Sources */, D719ABB92ABC67BF00B41C10 /* ContentView.swift in Sources */, + D750D3392AD3E6EE00EC99C5 /* PopupLoadingView.swift in Sources */, D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */, + D7EAACCF2AD6ED8000AA6A8A /* PermissionsFragment.swift in Sources */, + D7A03FBD2ACC2DB60081A588 /* ContactsView.swift in Sources */, D719ABCF2ABC779A00B41C10 /* AccountLoginViewModel.swift in Sources */, + D74C9D012ACB098C0021626A /* PermissionManager.swift in Sources */, + D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */, D719ABB72ABC67BF00B41C10 /* LinphoneApp.swift in Sources */, + D7A2EDD62AC18115005D90FC /* SharedMainViewModel.swift in Sources */, + D7A03FC62ACC458A0081A588 /* SplashScreen.swift in Sources */, + D7A03FC02ACC2E390081A588 /* HistoryView.swift in Sources */, + D748BF2E2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift in Sources */, + D748BF2C2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift in Sources */, + D74C9CF82ACACECE0021626A /* WelcomePage1Fragment.swift in Sources */, + D72343362AD037AF009AA24E /* ToastView.swift in Sources */, + D7FB55112AD447FD00A5AB15 /* RegisterFragment.swift in Sources */, + D72343322ACEFF58009AA24E /* QRScannerController.swift in Sources */, + D72343342ACEFFC3009AA24E /* QRScanner.swift in Sources */, + D72343302ACEFEF8009AA24E /* QrCodeScannerFragment.swift in Sources */, + D717071E2AC5922E0037746F /* ColorExtension.swift in Sources */, + D7DA67642ACCB31700E95002 /* ProfileModeFragment.swift in Sources */, + D74C9CFC2ACACF370021626A /* WelcomePage3Fragment.swift in Sources */, D719ABCC2ABC769C00B41C10 /* AssistantView.swift in Sources */, + D74C9CFA2ACACF2D0021626A /* WelcomePage2Fragment.swift in Sources */, + D74C9CFF2ACAEC5E0021626A /* PopupView.swift in Sources */, + D7DA67622ACCB2FA00E95002 /* LoginFragment.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -322,9 +596,11 @@ }; D719ABC32ABC67BF00B41C10 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 377E0B5C2B1F38192E694334 /* Pods-Linphone.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGN_ENTITLEMENTS = Linphone/Linphone.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -334,6 +610,9 @@ ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Linphone/Info.plist; + INFOPLIST_KEY_NSCameraUsageDescription = "Share photos with your friends and customize avatars"; + INFOPLIST_KEY_NSPhotoLibraryUsageDescription = ""; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; @@ -361,9 +640,11 @@ }; D719ABC42ABC67BF00B41C10 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F76FB87556A3109F61F9E2D5 /* Pods-Linphone.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGN_ENTITLEMENTS = Linphone/Linphone.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -373,6 +654,9 @@ ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Linphone/Info.plist; + INFOPLIST_KEY_NSCameraUsageDescription = "Share photos with your friends and customize avatars"; + INFOPLIST_KEY_NSPhotoLibraryUsageDescription = ""; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; diff --git a/Linphone/.DS_Store b/Linphone/.DS_Store index ec9d1dc22..ee98363d4 100644 Binary files a/Linphone/.DS_Store and b/Linphone/.DS_Store differ diff --git a/Linphone/Assets.xcassets/address-book.imageset/Contents.json b/Linphone/Assets.xcassets/address-book.imageset/Contents.json new file mode 100644 index 000000000..7e7aa5f15 --- /dev/null +++ b/Linphone/Assets.xcassets/address-book.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "address-book.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/address-book.imageset/address-book.svg b/Linphone/Assets.xcassets/address-book.imageset/address-book.svg new file mode 100644 index 000000000..9dc0b9ec9 --- /dev/null +++ b/Linphone/Assets.xcassets/address-book.imageset/address-book.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/bell-ringing.imageset/Contents.json b/Linphone/Assets.xcassets/bell-ringing.imageset/Contents.json new file mode 100644 index 000000000..d406bf8d4 --- /dev/null +++ b/Linphone/Assets.xcassets/bell-ringing.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bell-ringing.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/bell-ringing.imageset/bell-ringing.svg b/Linphone/Assets.xcassets/bell-ringing.imageset/bell-ringing.svg new file mode 100644 index 000000000..247d2a164 --- /dev/null +++ b/Linphone/Assets.xcassets/bell-ringing.imageset/bell-ringing.svg @@ -0,0 +1,3 @@ + + + diff --git a/Linphone/Assets.xcassets/caret-down.imageset/Contents.json b/Linphone/Assets.xcassets/caret-down.imageset/Contents.json new file mode 100644 index 000000000..cc33c146a --- /dev/null +++ b/Linphone/Assets.xcassets/caret-down.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "caret-down.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/caret-down.imageset/caret-down.svg b/Linphone/Assets.xcassets/caret-down.imageset/caret-down.svg new file mode 100644 index 000000000..42f37b716 --- /dev/null +++ b/Linphone/Assets.xcassets/caret-down.imageset/caret-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/caret-left.imageset/Contents.json b/Linphone/Assets.xcassets/caret-left.imageset/Contents.json new file mode 100644 index 000000000..a5f91ffff --- /dev/null +++ b/Linphone/Assets.xcassets/caret-left.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "caret-left.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/caret-left.imageset/caret-left.svg b/Linphone/Assets.xcassets/caret-left.imageset/caret-left.svg new file mode 100644 index 000000000..a3a1e39a6 --- /dev/null +++ b/Linphone/Assets.xcassets/caret-left.imageset/caret-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/conversation.imageset/Contents.json b/Linphone/Assets.xcassets/conversation.imageset/Contents.json new file mode 100644 index 000000000..949fb1205 --- /dev/null +++ b/Linphone/Assets.xcassets/conversation.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "conversation.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/conversation.imageset/conversation.svg b/Linphone/Assets.xcassets/conversation.imageset/conversation.svg new file mode 100644 index 000000000..27d2bfb1b --- /dev/null +++ b/Linphone/Assets.xcassets/conversation.imageset/conversation.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Linphone/Assets.xcassets/current-dot.imageset/Contents.json b/Linphone/Assets.xcassets/current-dot.imageset/Contents.json new file mode 100644 index 000000000..dafa79dae --- /dev/null +++ b/Linphone/Assets.xcassets/current-dot.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "current-dot.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/current-dot.imageset/current-dot.svg b/Linphone/Assets.xcassets/current-dot.imageset/current-dot.svg new file mode 100644 index 000000000..e28783f66 --- /dev/null +++ b/Linphone/Assets.xcassets/current-dot.imageset/current-dot.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/danger.imageset/Contents.json b/Linphone/Assets.xcassets/danger.imageset/Contents.json new file mode 100644 index 000000000..63a1157a7 --- /dev/null +++ b/Linphone/Assets.xcassets/danger.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "danger.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/danger.imageset/danger.svg b/Linphone/Assets.xcassets/danger.imageset/danger.svg new file mode 100644 index 000000000..7f47d7312 --- /dev/null +++ b/Linphone/Assets.xcassets/danger.imageset/danger.svg @@ -0,0 +1,3 @@ + + + diff --git a/Linphone/Assets.xcassets/dot.imageset/Contents.json b/Linphone/Assets.xcassets/dot.imageset/Contents.json new file mode 100644 index 000000000..ad32b6795 --- /dev/null +++ b/Linphone/Assets.xcassets/dot.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "dot.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/dot.imageset/dot.svg b/Linphone/Assets.xcassets/dot.imageset/dot.svg new file mode 100644 index 000000000..fe8bdc248 --- /dev/null +++ b/Linphone/Assets.xcassets/dot.imageset/dot.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/eye-slash.imageset/Contents.json b/Linphone/Assets.xcassets/eye-slash.imageset/Contents.json new file mode 100644 index 000000000..ea1110e56 --- /dev/null +++ b/Linphone/Assets.xcassets/eye-slash.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "eye-slash.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/eye-slash.imageset/eye-slash.svg b/Linphone/Assets.xcassets/eye-slash.imageset/eye-slash.svg new file mode 100644 index 000000000..6dc0e47a4 --- /dev/null +++ b/Linphone/Assets.xcassets/eye-slash.imageset/eye-slash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/eye.imageset/Contents.json b/Linphone/Assets.xcassets/eye.imageset/Contents.json new file mode 100644 index 000000000..d5696cab8 --- /dev/null +++ b/Linphone/Assets.xcassets/eye.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "eye.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/eye.imageset/eye.svg b/Linphone/Assets.xcassets/eye.imageset/eye.svg new file mode 100644 index 000000000..36ed4da10 --- /dev/null +++ b/Linphone/Assets.xcassets/eye.imageset/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/info.imageset/Contents.json b/Linphone/Assets.xcassets/info.imageset/Contents.json new file mode 100644 index 000000000..b5faab124 --- /dev/null +++ b/Linphone/Assets.xcassets/info.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "info.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/info.imageset/info.svg b/Linphone/Assets.xcassets/info.imageset/info.svg new file mode 100644 index 000000000..40cce74f7 --- /dev/null +++ b/Linphone/Assets.xcassets/info.imageset/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/linphone.imageset/Contents.json b/Linphone/Assets.xcassets/linphone.imageset/Contents.json new file mode 100644 index 000000000..ff043ddb2 --- /dev/null +++ b/Linphone/Assets.xcassets/linphone.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "linphone.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/linphone.imageset/linphone.svg b/Linphone/Assets.xcassets/linphone.imageset/linphone.svg new file mode 100644 index 000000000..44f54349b --- /dev/null +++ b/Linphone/Assets.xcassets/linphone.imageset/linphone.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Linphone/Assets.xcassets/microphone.imageset/Contents.json b/Linphone/Assets.xcassets/microphone.imageset/Contents.json new file mode 100644 index 000000000..49b7a33fb --- /dev/null +++ b/Linphone/Assets.xcassets/microphone.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "microphone.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/microphone.imageset/microphone.svg b/Linphone/Assets.xcassets/microphone.imageset/microphone.svg new file mode 100644 index 000000000..c4aeceaeb --- /dev/null +++ b/Linphone/Assets.xcassets/microphone.imageset/microphone.svg @@ -0,0 +1,3 @@ + + + diff --git a/Linphone/Assets.xcassets/mountain.imageset/Contents.json b/Linphone/Assets.xcassets/mountain.imageset/Contents.json new file mode 100644 index 000000000..101c38e7e --- /dev/null +++ b/Linphone/Assets.xcassets/mountain.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "mountain.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/mountain.imageset/mountain.svg b/Linphone/Assets.xcassets/mountain.imageset/mountain.svg new file mode 100644 index 000000000..fdb0ecf8d --- /dev/null +++ b/Linphone/Assets.xcassets/mountain.imageset/mountain.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/Linphone/Assets.xcassets/open-source.imageset/Contents.json b/Linphone/Assets.xcassets/open-source.imageset/Contents.json new file mode 100644 index 000000000..f63666669 --- /dev/null +++ b/Linphone/Assets.xcassets/open-source.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "open-source.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/open-source.imageset/open-source.svg b/Linphone/Assets.xcassets/open-source.imageset/open-source.svg new file mode 100644 index 000000000..9bbb7658b --- /dev/null +++ b/Linphone/Assets.xcassets/open-source.imageset/open-source.svg @@ -0,0 +1,3 @@ + + + diff --git a/Linphone/Assets.xcassets/phone.imageset/Contents.json b/Linphone/Assets.xcassets/phone.imageset/Contents.json new file mode 100644 index 000000000..1c2ef1a6a --- /dev/null +++ b/Linphone/Assets.xcassets/phone.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "phone.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/phone.imageset/phone.svg b/Linphone/Assets.xcassets/phone.imageset/phone.svg new file mode 100644 index 000000000..6eb862926 --- /dev/null +++ b/Linphone/Assets.xcassets/phone.imageset/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/profile-mode.imageset/Contents.json b/Linphone/Assets.xcassets/profile-mode.imageset/Contents.json new file mode 100644 index 000000000..528acc575 --- /dev/null +++ b/Linphone/Assets.xcassets/profile-mode.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "profile-mode.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/profile-mode.imageset/profile-mode.png b/Linphone/Assets.xcassets/profile-mode.imageset/profile-mode.png new file mode 100644 index 000000000..740a909fb Binary files /dev/null and b/Linphone/Assets.xcassets/profile-mode.imageset/profile-mode.png differ diff --git a/Linphone/Assets.xcassets/qr-code.imageset/Contents.json b/Linphone/Assets.xcassets/qr-code.imageset/Contents.json new file mode 100644 index 000000000..12ceab544 --- /dev/null +++ b/Linphone/Assets.xcassets/qr-code.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "qr-code.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/qr-code.imageset/qr-code.svg b/Linphone/Assets.xcassets/qr-code.imageset/qr-code.svg new file mode 100644 index 000000000..d5cd44274 --- /dev/null +++ b/Linphone/Assets.xcassets/qr-code.imageset/qr-code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/radio-button-fill.imageset/Contents.json b/Linphone/Assets.xcassets/radio-button-fill.imageset/Contents.json new file mode 100644 index 000000000..7de01f0e6 --- /dev/null +++ b/Linphone/Assets.xcassets/radio-button-fill.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "radio-button-fill.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/radio-button-fill.imageset/radio-button-fill.svg b/Linphone/Assets.xcassets/radio-button-fill.imageset/radio-button-fill.svg new file mode 100644 index 000000000..1bc27f1d2 --- /dev/null +++ b/Linphone/Assets.xcassets/radio-button-fill.imageset/radio-button-fill.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Linphone/Assets.xcassets/radio-button.imageset/Contents.json b/Linphone/Assets.xcassets/radio-button.imageset/Contents.json new file mode 100644 index 000000000..61335ddfb --- /dev/null +++ b/Linphone/Assets.xcassets/radio-button.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "radio-button.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/radio-button.imageset/radio-button.svg b/Linphone/Assets.xcassets/radio-button.imageset/radio-button.svg new file mode 100644 index 000000000..d9001baba --- /dev/null +++ b/Linphone/Assets.xcassets/radio-button.imageset/radio-button.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Linphone/Assets.xcassets/secure-image.imageset/Contents.json b/Linphone/Assets.xcassets/secure-image.imageset/Contents.json new file mode 100644 index 000000000..ade67196d --- /dev/null +++ b/Linphone/Assets.xcassets/secure-image.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "secure-image.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/secure-image.imageset/secure-image.svg b/Linphone/Assets.xcassets/secure-image.imageset/secure-image.svg new file mode 100644 index 000000000..7c8d6ee3a --- /dev/null +++ b/Linphone/Assets.xcassets/secure-image.imageset/secure-image.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Linphone/Assets.xcassets/success.imageset/Contents.json b/Linphone/Assets.xcassets/success.imageset/Contents.json new file mode 100644 index 000000000..a4aa98205 --- /dev/null +++ b/Linphone/Assets.xcassets/success.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "success.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/success.imageset/success.svg b/Linphone/Assets.xcassets/success.imageset/success.svg new file mode 100644 index 000000000..633f0687c --- /dev/null +++ b/Linphone/Assets.xcassets/success.imageset/success.svg @@ -0,0 +1,3 @@ + + + diff --git a/Linphone/Assets.xcassets/video-call.imageset/Contents.json b/Linphone/Assets.xcassets/video-call.imageset/Contents.json new file mode 100644 index 000000000..0617b9467 --- /dev/null +++ b/Linphone/Assets.xcassets/video-call.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "VideoCall.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/video-call.imageset/VideoCall.svg b/Linphone/Assets.xcassets/video-call.imageset/VideoCall.svg new file mode 100644 index 000000000..5bfd91cd2 --- /dev/null +++ b/Linphone/Assets.xcassets/video-call.imageset/VideoCall.svg @@ -0,0 +1,3 @@ + + + diff --git a/Linphone/Assets.xcassets/video-camera.imageset/Contents.json b/Linphone/Assets.xcassets/video-camera.imageset/Contents.json new file mode 100644 index 000000000..e7745ecc4 --- /dev/null +++ b/Linphone/Assets.xcassets/video-camera.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "video-camera.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/video-camera.imageset/video-camera.svg b/Linphone/Assets.xcassets/video-camera.imageset/video-camera.svg new file mode 100644 index 000000000..1f42d14cc --- /dev/null +++ b/Linphone/Assets.xcassets/video-camera.imageset/video-camera.svg @@ -0,0 +1,3 @@ + + + diff --git a/Linphone/Core/CoreContext.swift b/Linphone/Core/CoreContext.swift new file mode 100644 index 000000000..f20316b7f --- /dev/null +++ b/Linphone/Core/CoreContext.swift @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 + +final class CoreContext: ObservableObject { + + static let shared = CoreContext() + + var mCore: Core! + var mRegistrationDelegate: CoreDelegate! + var mConfigurationDelegate: CoreDelegate! + + var coreVersion: String = Core.getVersion + @Published var loggedIn: Bool = false + @Published var loggingInProgress: Bool = false + @Published var toastMessage: String = "" + + private init() {} + + func initialiseCore() async throws { + LoggingService.Instance.logLevel = LogLevel.Debug + + let factory = Factory.Instance + let configDir = factory.getConfigDir(context: nil) + try? mCore = Factory.Instance.createCore(configPath: "\(configDir)/MyConfig", factoryConfigPath: "", systemContext: nil) + try? mCore.start() + + // Create a Core listener to listen for the callback we need + // In this case, we want to know about the account registration status + + mRegistrationDelegate = + CoreDelegateStub( + onConfiguringStatus: {(_: Core, state: Config.ConfiguringState, message: String) in + NSLog("New configuration state is \(state) = \(message)\n") + if state == .Successful { + self.toastMessage = "Successful" + } else { + self.toastMessage = "Failed" + } + }, + + onAccountRegistrationStateChanged: {(_: Core, account: Account, state: RegistrationState, message: String) in + // If account has been configured correctly, we will go through Progress and Ok states + // Otherwise, we will be Failed. + NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString())) = \(message)\n") + if state == .Ok { + self.loggingInProgress = false + self.loggedIn = true + } else if state == .Progress { + self.loggingInProgress = true + } else { + self.toastMessage = "Registration failed" + self.loggingInProgress = false + self.loggedIn = false + } + } + ) + + mCore.addDelegate(delegate: mRegistrationDelegate) + } +} diff --git a/Linphone/Fonts/NotoSans-Bold.ttf b/Linphone/Fonts/NotoSans-Bold.ttf new file mode 100644 index 000000000..0d1906839 Binary files /dev/null and b/Linphone/Fonts/NotoSans-Bold.ttf differ diff --git a/Linphone/Fonts/NotoSans-ExtraBold.ttf b/Linphone/Fonts/NotoSans-ExtraBold.ttf new file mode 100644 index 000000000..a74c69ab1 Binary files /dev/null and b/Linphone/Fonts/NotoSans-ExtraBold.ttf differ diff --git a/Linphone/Fonts/NotoSans-Light.ttf b/Linphone/Fonts/NotoSans-Light.ttf new file mode 100644 index 000000000..8fde66280 Binary files /dev/null and b/Linphone/Fonts/NotoSans-Light.ttf differ diff --git a/Linphone/Fonts/NotoSans-Medium.ttf b/Linphone/Fonts/NotoSans-Medium.ttf new file mode 100644 index 000000000..faf167c91 Binary files /dev/null and b/Linphone/Fonts/NotoSans-Medium.ttf differ diff --git a/Linphone/Fonts/NotoSans-Regular.ttf b/Linphone/Fonts/NotoSans-Regular.ttf new file mode 100644 index 000000000..7552fbe80 Binary files /dev/null and b/Linphone/Fonts/NotoSans-Regular.ttf differ diff --git a/Linphone/Fonts/NotoSans-SemiBold.ttf b/Linphone/Fonts/NotoSans-SemiBold.ttf new file mode 100644 index 000000000..b460754c5 Binary files /dev/null and b/Linphone/Fonts/NotoSans-SemiBold.ttf differ diff --git a/Linphone/Info.plist b/Linphone/Info.plist new file mode 100644 index 000000000..c3b52b04f --- /dev/null +++ b/Linphone/Info.plist @@ -0,0 +1,20 @@ + + + + + UIAppFonts + + NotoSans-Light.ttf + NotoSans-Regular.ttf + NotoSans-Medium.ttf + NotoSans-SemiBold.ttf + NotoSans-Bold.ttf + NotoSans-ExtraBold.ttf + + UILaunchScreen + + UIImageName + linphone + + + diff --git a/Linphone/LinphoneApp.swift b/Linphone/LinphoneApp.swift index 6536d8c3d..87b0a4007 100644 --- a/Linphone/LinphoneApp.swift +++ b/Linphone/LinphoneApp.swift @@ -1,29 +1,38 @@ /* -* 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 . -*/ + * 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 @main struct LinphoneApp: App { - var body: some Scene { - WindowGroup { - ContentView() - } - } + + @ObservedObject private var coreContext = CoreContext.shared + @State private var isActive = false + + var body: some Scene { + WindowGroup { + if isActive { + ContentView(sharedMainViewModel: SharedMainViewModel()) + .toast(isShowing: $coreContext.toastMessage) + } else { + SplashScreen(isActive: $isActive) + } + } + } } diff --git a/Linphone/Localizable.xcstrings b/Linphone/Localizable.xcstrings new file mode 100644 index 000000000..220ead687 --- /dev/null +++ b/Linphone/Localizable.xcstrings @@ -0,0 +1,308 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "" : { + + }, + " et " : { + + }, + " or " : { + + }, + "." : { + + }, + "[Forgotten password?](https://subscribe.linphone.org/)" : { + + }, + "[linphone.org/contact](https://linphone.org/contact)" : { + + }, + "[nos conditions d’utilisation](https://linphone.org/general-terms)" : { + + }, + "[notre politique de confidentialité](https://linphone.org/privacy-policy)" : { + + }, + "**Camera** : Pour capturer votre vidéo lors des appels vidéo et conférence." : { + + }, + "**Contacts** : Pour vous afficher vos contacts et retrouver qui utilise Linphone." : { + + }, + "**Micro** : Pour permettre à vos correspondants de vous entendre." : { + + }, + "**Notifications** : Pour vous informé quand vous recevez un message ou un appel." : { + + }, + "%lld Book (Example)" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "%lld Book" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Books" + } + }, + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "No Book" + } + } + } + } + }, + "fr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Livre" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Livres" + } + }, + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pas de Livre" + } + } + } + } + } + } + }, + "Accept all" : { + + }, + "assistant_account_login" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Login" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Login en FR" + } + } + } + }, + "Calls" : { + + }, + "Ce mode vous permet d’être interopérable avec d’autres services SIP.\nVos communications seront chiffrées de point à point. " : { + + }, + "Chiffrement de bout en bout de tous vos échanges, grâce au mode default vos communications sont à l’abri des regards." : { + + }, + "Close" : { + + }, + "Conditions de service" : { + + }, + "Contacts" : { + + }, + "Contacts View" : { + + }, + "Continue" : { + + }, + "D'accord" : { + + }, + "Default" : { + + }, + "Default mode" : { + + }, + "Demande d’autorisations" : { + + }, + "Deny all" : { + + }, + "Display Name" : { + + }, + "Domain" : { + + }, + "En continuant, vous acceptez ces conditions, " : { + + }, + "Error" : { + + }, + "History View" : { + + }, + "I prefere create an account" : { + + }, + "I understand" : { + + }, + "Interoperable" : { + + }, + "Interoperable mode" : { + + }, + "Invalid QR code!" : { + + }, + "Invalide URI" : { + + }, + "Linphone" : { + + }, + "Log out" : { + + }, + "Next" : { + + }, + "Not account yet?" : { + + }, + "Open source" : { + + }, + "Opération en cours..." : { + + }, + "password" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password en FR" + } + } + } + }, + "Personnalize your profil mode" : { + + }, + "Plus tard" : { + + }, + "Pour vous permettre de vous profitez pleinement de Linphone nous avons besoin des autorisations suivantes :" : { + + }, + "QR code validated!" : { + + }, + "Register" : { + + }, + "Scan QR code" : { + + }, + "Sécurisé" : { + + }, + "sip.linphone.org" : { + + }, + "Skip" : { + + }, + "Start" : { + + }, + "TCP" : { + + }, + "The user name or password is incorrects" : { + + }, + "TLS" : { + + }, + "to Linphone" : { + + }, + "Transport" : { + + }, + "UDP" : { + + }, + "Une application de communication **sécurisée**, **open source** et **française**." : { + + }, + "Une application open source et un **service gratuit** depuis **2001**." : { + + }, + "Use a SIP account" : { + + }, + "Use SIP Account" : { + + }, + "username" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username en FR" + } + } + } + }, + "Vos communications sont en sécurité grâce aux **Chiffrement de bout en bout**." : { + + }, + "Welcome" : { + + }, + "You will change this mode later" : { + + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Linphone/SplashScreen.swift b/Linphone/SplashScreen.swift new file mode 100644 index 000000000..64d59e258 --- /dev/null +++ b/Linphone/SplashScreen.swift @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 + +struct SplashScreen: View { + + @ObservedObject private var coreContext = CoreContext.shared + @Binding var isActive: Bool + + var body: some View { + GeometryReader { _ in + VStack { + Spacer() + HStack { + Spacer() + Image("linphone") + Spacer() + } + Spacer() + } + + } + .ignoresSafeArea(.all) + .onAppear { + Task { + try await coreContext.initialiseCore() + withAnimation { + self.isActive = true + } + } + } + } +} + +#Preview { + SplashScreen(isActive: .constant(true)) +} diff --git a/Linphone/UI/Assistant/AssistantView.swift b/Linphone/UI/Assistant/AssistantView.swift new file mode 100644 index 000000000..c3cc5e2bd --- /dev/null +++ b/Linphone/UI/Assistant/AssistantView.swift @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 + +struct AssistantView: View { + + @ObservedObject var sharedMainViewModel: SharedMainViewModel + @ObservedObject private var coreContext = CoreContext.shared + + var body: some View { + if sharedMainViewModel.displayProfileMode && coreContext.loggedIn { + ProfileModeFragment(sharedMainViewModel: sharedMainViewModel) + } else { + LoginFragment(accountLoginViewModel: AccountLoginViewModel(), sharedMainViewModel: sharedMainViewModel) + } + } +} + +#Preview { + AssistantView(sharedMainViewModel: SharedMainViewModel()) +} diff --git a/Linphone/UI/Assistant/Fragments/LoginFragment.swift b/Linphone/UI/Assistant/Fragments/LoginFragment.swift new file mode 100644 index 000000000..0d5cac6b9 --- /dev/null +++ b/Linphone/UI/Assistant/Fragments/LoginFragment.swift @@ -0,0 +1,321 @@ +/* + * 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 + +struct LoginFragment: View { + + @ObservedObject private var coreContext = CoreContext.shared + @ObservedObject var accountLoginViewModel: AccountLoginViewModel + @ObservedObject var sharedMainViewModel: SharedMainViewModel + + @State private var isSecured: Bool = true + + @FocusState var isNameFocused: Bool + @FocusState var isPasswordFocused: Bool + + @State private var isShowPopup = false + + @State private var linkActive = "" + + @State private var isLinkSIPActive = false + @State private var isLinkREGActive = false + + var body: some View { + NavigationView { + ZStack { + GeometryReader { geometry in + ScrollView(.vertical) { + VStack { + ZStack { + Image("mountain") + .resizable() + .scaledToFill() + .frame(width: geometry.size.width, height: 100) + .clipped() + Text("assistant_account_login") + .default_text_style_white_800(styleSize: 20) + .padding(.top, 20) + } + .padding(.top, 35) + .padding(.bottom, 10) + + VStack(alignment: .leading) { + Text(String(localized: "username")+"*") + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + TextField("username", text: $accountLoginViewModel.username) + .default_text_style(styleSize: 15) + .disabled(coreContext.loggedIn) + .frame(height: 25) + .padding(.horizontal, 20) + .padding(.vertical, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(isNameFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1) + ) + .padding(.bottom) + .focused($isNameFocused) + + Text(String(localized: "password")+"*") + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + ZStack(alignment: .trailing) { + Group { + if isSecured { + SecureField("password", text: $accountLoginViewModel.passwd) + .default_text_style(styleSize: 15) + .frame(height: 25) + .focused($isPasswordFocused) + } else { + TextField("password", text: $accountLoginViewModel.passwd) + .default_text_style(styleSize: 15) + .frame(height: 25) + .focused($isPasswordFocused) + } + } + + Button(action: { + isSecured.toggle() + }, label: { + Image(self.isSecured ? "eye-slash" : "eye") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 20, height: 20) + }) + } + .disabled(coreContext.loggedIn) + .padding(.horizontal, 20) + .padding(.vertical, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(isPasswordFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1) + ) + .padding(.bottom) + + Button(action: { + self.accountLoginViewModel.login() + }, label: { + Text(coreContext.loggedIn ? "Log out" : "assistant_account_login") + .default_text_style_white_600(styleSize: 20) + .frame(height: 35) + .frame(maxWidth: .infinity) + }) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background((accountLoginViewModel.username.isEmpty || accountLoginViewModel.passwd.isEmpty) ? Color.orangeMain100 : Color.orangeMain500) + .cornerRadius(60) + .disabled(accountLoginViewModel.username.isEmpty || accountLoginViewModel.passwd.isEmpty) + .padding(.bottom) + + HStack { + Text("[Forgotten password?](https://subscribe.linphone.org/)") + .underline() + .tint(Color.grayMain2c600) + .default_text_style_600(styleSize: 15) + .foregroundStyle(Color.grayMain2c500) + } + .frame(maxWidth: .infinity) + .padding(.bottom, 30) + + HStack { + VStack { + Divider() + } + Text(" or ") + .default_text_style(styleSize: 15) + .foregroundStyle(Color.grayMain2c500) + VStack { + Divider() + } + } + .padding(.bottom, 10) + + NavigationLink(destination: { + QrCodeScannerFragment() + }, label: { + HStack { + Image("qr-code") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.orangeMain500) + .frame(width: 20, height: 20) + + Text("Scan QR code") + .default_text_style_orange_600(styleSize: 20) + .frame(height: 35) + } + .frame(maxWidth: .infinity) + + }) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(Color.orangeMain500, lineWidth: 1) + ) + .padding(.bottom) + + NavigationLink(isActive: $isLinkSIPActive, destination: { + ThirdPartySipAccountWarningFragment(sharedMainViewModel: sharedMainViewModel, accountLoginViewModel: accountLoginViewModel) + }, label: { + Text("Use SIP Account") + .default_text_style_orange_600(styleSize: 20) + .frame(height: 35) + .frame(maxWidth: .infinity) + + }) + .disabled(!sharedMainViewModel.generalTermsAccepted) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(Color.orangeMain500, lineWidth: 1) + ) + .padding(.bottom) + .simultaneousGesture( + TapGesture().onEnded { + self.linkActive = "SIP" + if !sharedMainViewModel.generalTermsAccepted { + withAnimation { + self.isShowPopup.toggle() + } + } else { + self.isLinkSIPActive = true + } + } + ) + + Spacer() + + HStack(alignment: .center) { + + Spacer() + + Text("Not account yet?") + .default_text_style(styleSize: 15) + .foregroundStyle(Color.grayMain2c700) + .padding(.horizontal, 10) + + NavigationLink(destination: RegisterFragment(), isActive: $isLinkREGActive, label: {Text("Register") + .default_text_style_orange_600(styleSize: 20) + .frame(height: 35) + }) + .disabled(!sharedMainViewModel.generalTermsAccepted) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(Color.orangeMain500, lineWidth: 1) + ) + .padding(.horizontal, 10) + .simultaneousGesture( + TapGesture().onEnded { + self.linkActive = "REG" + if !sharedMainViewModel.generalTermsAccepted { + withAnimation { + self.isShowPopup.toggle() + } + } else { + self.isLinkREGActive = true + } + } + ) + + Spacer() + } + .padding(.bottom) + } + .frame(maxWidth: sharedMainViewModel.maxWidth) + .padding(.horizontal, 20) + } + .frame(minHeight: geometry.size.height) + } + + if self.isShowPopup { + let contentPopup1 = Text("En continuant, vous acceptez ces conditions, ") + let contentPopup2 = Text("[notre politique de confidentialité](https://linphone.org/privacy-policy)").underline() + let contentPopup3 = Text(" et ") + let contentPopup4 = Text("[nos conditions d’utilisation](https://linphone.org/general-terms)").underline() + let contentPopup5 = Text(".") + PopupView(sharedMainViewModel: sharedMainViewModel, isShowPopup: $isShowPopup, + title: Text("Conditions de service"), + content: contentPopup1 + contentPopup2 + contentPopup3 + contentPopup4 + contentPopup5, + titleFirstButton: Text("Deny all"), + actionFirstButton: {self.isShowPopup.toggle()}, + titleSecondButton: Text("Accept all"), + actionSecondButton: {acceptGeneralTerms()}) + .background(.black.opacity(0.65)) + .onTapGesture { + self.isShowPopup.toggle() + } + } + } + .onAppear { + sharedMainViewModel.changeDisplayProfileMode() + } + + if coreContext.loggingInProgress { + PopupLoadingView(sharedMainViewModel: sharedMainViewModel) + .background(.black.opacity(0.65)) + } + + if !coreContext.loggingInProgress && !coreContext.loggedIn { + ZStack { + + }.onAppear { + self.accountLoginViewModel.unregister() + self.accountLoginViewModel.delete() + } + } + } + } + .navigationViewStyle(StackNavigationViewStyle()) + } + + func acceptGeneralTerms() { + sharedMainViewModel.changeGeneralTerms() + self.isShowPopup.toggle() + switch linkActive { + case "SIP": + self.isLinkSIPActive = true + case "REG": + self.isLinkREGActive = true + default: + print("Link Not Active") + } + } +} + +#Preview { + LoginFragment(accountLoginViewModel: AccountLoginViewModel(), sharedMainViewModel: SharedMainViewModel()) +} diff --git a/Linphone/UI/Assistant/Fragments/PermissionsFragment.swift b/Linphone/UI/Assistant/Fragments/PermissionsFragment.swift new file mode 100644 index 000000000..cbb56aa76 --- /dev/null +++ b/Linphone/UI/Assistant/Fragments/PermissionsFragment.swift @@ -0,0 +1,227 @@ +/* + * 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 + +struct PermissionsFragment: View { + + @ObservedObject var sharedMainViewModel: SharedMainViewModel + + var permissionManager = PermissionManager.shared + + @Environment(\.dismiss) var dismiss + + var body: some View { + GeometryReader { geometry in + ScrollView(.vertical) { + VStack { + ZStack { + Image("mountain") + .resizable() + .scaledToFill() + .frame(width: geometry.size.width, height: 100) + .clipped() + + VStack(alignment: .leading) { + HStack { + Image("caret-left") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.top, -65) + .onTapGesture { + withAnimation { + dismiss() + } + } + + Spacer() + } + .padding(.leading) + } + .frame(width: geometry.size.width) + + Text("Demande d’autorisations") + .default_text_style_white_800(styleSize: 20) + .padding(.top, 20) + } + .padding(.top, 35) + .padding(.bottom, 10) + + Text("Pour vous permettre de vous profitez pleinement de Linphone nous avons besoin des autorisations suivantes :") + .default_text_style(styleSize: 15) + .multilineTextAlignment(.center) + + Spacer() + + VStack(alignment: .leading) { + HStack { + HStack(alignment: .center) { + Image("bell-ringing") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 20, height: 20, alignment: .leading) + .onTapGesture { + withAnimation { + dismiss() + } + } + } + .padding(16) + .background(Color.grayMain2c200) + .cornerRadius(40) + + Text("**Notifications** : Pour vous informé quand vous recevez un message ou un appel.") + .default_text_style(styleSize: 15) + .padding(.leading, 10) + } + .padding(.bottom) + + HStack { + HStack(alignment: .center) { + Image("address-book") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 20, height: 20, alignment: .leading) + .onTapGesture { + withAnimation { + dismiss() + } + } + } + .padding(16) + .background(Color.grayMain2c200) + .cornerRadius(40) + + Text("**Contacts** : Pour vous afficher vos contacts et retrouver qui utilise Linphone.") + .default_text_style(styleSize: 15) + .padding(.leading, 10) + } + .padding(.bottom) + + HStack { + HStack(alignment: .center) { + Image("microphone") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 20, height: 20, alignment: .leading) + .onTapGesture { + withAnimation { + dismiss() + } + } + } + .padding(16) + .background(Color.grayMain2c200) + .cornerRadius(40) + + Text("**Micro** : Pour permettre à vos correspondants de vous entendre.") + .default_text_style(styleSize: 15) + .padding(.leading, 10) + } + .padding(.bottom) + + HStack { + HStack(alignment: .center) { + Image("video-camera") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 20, height: 20, alignment: .leading) + .onTapGesture { + withAnimation { + dismiss() + } + } + } + .padding(16) + .background(Color.grayMain2c200) + .cornerRadius(40) + + Text("**Camera** : Pour capturer votre vidéo lors des appels vidéo et conférence.") + .default_text_style(styleSize: 15) + .padding(.leading, 10) + } + .padding(.bottom) + } + .frame(maxWidth: sharedMainViewModel.maxWidth) + .frame(maxHeight: .infinity) + .padding(.horizontal, 20) + + Spacer() + + Button(action: { + withAnimation { + sharedMainViewModel.changeWelcomeView() + } + }, label: { + Text("Plus tard") + .default_text_style_orange_600(styleSize: 20) + .frame(height: 35) + .frame(maxWidth: .infinity) + }) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(Color.orangeMain500, lineWidth: 1) + ) + .frame(maxWidth: sharedMainViewModel.maxWidth) + .padding(.horizontal) + + Button { + permissionManager.cameraRequestPermission() + } label: { + Text("D'accord") + .default_text_style_white_600(styleSize: 20) + .frame(height: 35) + .frame(maxWidth: .infinity) + } + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background(Color.orangeMain500) + .cornerRadius(60) + .frame(maxWidth: sharedMainViewModel.maxWidth) + .padding(.horizontal) + .padding(.bottom, geometry.safeAreaInsets.bottom.isEqual(to: 0.0) ? 20 : 0) + } + .frame(minHeight: geometry.size.height) + } + } + .navigationViewStyle(StackNavigationViewStyle()) + .navigationBarHidden(true) + .onReceive(permissionManager.$cameraPermissionGranted, perform: { (granted) in + if granted { + withAnimation { + sharedMainViewModel.changeWelcomeView() + } + } + }) + } +} + +#Preview { + PermissionsFragment(sharedMainViewModel: SharedMainViewModel()) +} diff --git a/Linphone/UI/Assistant/Fragments/ProfileModeFragment.swift b/Linphone/UI/Assistant/Fragments/ProfileModeFragment.swift new file mode 100644 index 000000000..0eec8a5e0 --- /dev/null +++ b/Linphone/UI/Assistant/Fragments/ProfileModeFragment.swift @@ -0,0 +1,171 @@ +/* + * 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 + +struct ProfileModeFragment: View { + + @ObservedObject var sharedMainViewModel: SharedMainViewModel + + @State var options: Int = 1 + @State private var isShowPopup = false + @State private var isShowPopupForDefault = true + + var body: some View { + GeometryReader { geometry in + ScrollView(.vertical) { + VStack { + ZStack { + Image("mountain") + .resizable() + .scaledToFill() + .frame(width: geometry.size.width, height: 100) + .clipped() + Text("Personnalize your profil mode") + .default_text_style_white_800(styleSize: 20) + .padding(.top, -10) + Text("You will change this mode later") + .default_text_style_white(styleSize: 15) + .padding(.top, 40) + } + .padding(.top, 35) + .padding(.bottom, 10) + + VStack(spacing: 10) { + Button(action: { + options = 1 + }, label: { + HStack { + Image(options == 1 ? "radio-button-fill" : "radio-button") + Text("Default") + .profile_mode_text_style_gray_800(styleSize: 16) + Image("info") + .resizable() + .frame(width: 25, height: 25) + .onTapGesture { + withAnimation { + self.isShowPopupForDefault = true + self.isShowPopup.toggle() + } + } + Spacer() + } + }) + + HStack { + Text("Chiffrement de bout en bout de tous vos échanges, grâce au mode default vos communications sont à l’abri des regards.") + .profile_mode_text_style_gray(styleSize: 15) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 16) + .padding(.vertical, 20) + .background(Color.gray100) + .cornerRadius(15) + .padding(.bottom, 5) + + Image("profile-mode") + .resizable() + .frame(width: 150, height: 60) + .padding() + + Button(action: { + options = 2 + }, label: { + HStack { + Image(options == 2 ? "radio-button-fill" : "radio-button") + Text("Interoperable") + .profile_mode_text_style_gray_800(styleSize: 16) + Image("info") + .resizable() + .frame(width: 25, height: 25) + .onTapGesture { + withAnimation { + self.isShowPopupForDefault = false + self.isShowPopup.toggle() + } + } + Spacer() + } + }) + + HStack { + Text("Ce mode vous permet d’être interopérable avec d’autres services SIP.\nVos communications seront chiffrées de point à point. ") + .profile_mode_text_style_gray(styleSize: 15) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 16) + .padding(.vertical, 20) + .background(Color.gray100) + .cornerRadius(15) + } + .frame(maxWidth: sharedMainViewModel.maxWidth) + .padding() + + Spacer() + + Button(action: { + sharedMainViewModel.changeHideProfileMode() + }, label: { + Text("Continue") + .default_text_style_white_600(styleSize: 20) + .frame(height: 35) + .frame(maxWidth: .infinity) + }) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background(Color.orangeMain500) + .cornerRadius(60) + .padding(.horizontal) + .padding(.bottom, geometry.safeAreaInsets.bottom.isEqual(to: 0.0) ? 20 : 0) + .frame(maxWidth: sharedMainViewModel.maxWidth) + } + .frame(minHeight: geometry.size.height) + } + .onAppear { + UserDefaults.standard.set(false, forKey: "display_profile_mode") + } + + if self.isShowPopup { + PopupView(sharedMainViewModel: sharedMainViewModel, isShowPopup: $isShowPopup, + title: Text(isShowPopupForDefault ? "Default mode" : "Interoperable mode"), + content: Text( + isShowPopupForDefault + ? "Texte explicatif du default mode : lorem ipsum dolor sit amet, consectetur adipiscing elit." + + "Etiam velit sapien, egestas sit amet dictum eget, condimentum a ligula." + : "Texte explicatif du interoperable mode : lorem ipsum dolor sit amet, consectetur adipiscing elit." + + " Etiam velit sapien, egestas sit amet dictum eget, condimentum a ligula."), + titleFirstButton: nil, + actionFirstButton: {}, + titleSecondButton: Text("Close"), + actionSecondButton: { + self.isShowPopup.toggle() + } + ) + .background(.black.opacity(0.65)) + .onTapGesture { + self.isShowPopup.toggle() + } + } + } + } +} + +#Preview { + ProfileModeFragment(sharedMainViewModel: SharedMainViewModel()) +} diff --git a/Linphone/UI/Assistant/Fragments/QrCodeScannerFragment.swift b/Linphone/UI/Assistant/Fragments/QrCodeScannerFragment.swift new file mode 100644 index 000000000..d5c9ad36e --- /dev/null +++ b/Linphone/UI/Assistant/Fragments/QrCodeScannerFragment.swift @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 + +struct QrCodeScannerFragment: View { + + @ObservedObject private var coreContext = CoreContext.shared + + @Environment(\.dismiss) var dismiss + + @State var scanResult = "Scan a QR code" + + var body: some View { + ZStack(alignment: .top) { + QRScanner(result: $scanResult) + + Text(scanResult) + .default_text_style_white_800(styleSize: 20) + .padding(.top, 175) + + HStack { + Button { + dismiss() + } label: { + Image("caret-left") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.white) + .frame(width: 25, height: 25, alignment: .leading) + } + .padding() + .padding(.top, 50) + + Spacer() + } + } + .edgesIgnoringSafeArea(.all) + .navigationBarHidden(true) + + if coreContext.toastMessage == "Successful" { + ZStack { + + }.onAppear { + dismiss() + } + } + } +} + +#Preview { + QrCodeScannerFragment() +} diff --git a/Linphone/UI/Assistant/Fragments/RegisterFragment.swift b/Linphone/UI/Assistant/Fragments/RegisterFragment.swift new file mode 100644 index 000000000..194bd5b5a --- /dev/null +++ b/Linphone/UI/Assistant/Fragments/RegisterFragment.swift @@ -0,0 +1,76 @@ +/* + * 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 + +struct RegisterFragment: View { + + @Environment(\.dismiss) var dismiss + + var body: some View { + NavigationView { + GeometryReader { geometry in + ScrollView(.vertical) { + VStack { + ZStack { + Image("mountain") + .resizable() + .scaledToFill() + .frame(width: geometry.size.width, height: 100) + .clipped() + + VStack(alignment: .leading) { + HStack { + Image("caret-left") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.top, -65) + .onTapGesture { + withAnimation { + dismiss() + } + } + + Spacer() + } + .padding(.leading) + } + .frame(width: geometry.size.width) + + Text("Register") + .default_text_style_white_800(styleSize: 20) + .padding(.top, 20) + } + .padding(.top, 35) + .padding(.bottom, 10) + + } + } + } + } + .navigationViewStyle(StackNavigationViewStyle()) + .navigationBarHidden(true) + } +} + +#Preview { + RegisterFragment() +} diff --git a/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountLoginFragment.swift b/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountLoginFragment.swift new file mode 100644 index 000000000..b8be41481 --- /dev/null +++ b/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountLoginFragment.swift @@ -0,0 +1,237 @@ +/* + * 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 + +struct ThirdPartySipAccountLoginFragment: View { + + @ObservedObject var sharedMainViewModel: SharedMainViewModel + @ObservedObject private var coreContext = CoreContext.shared + @ObservedObject var accountLoginViewModel: AccountLoginViewModel + + @Environment(\.dismiss) var dismiss + + @State private var isSecured: Bool = true + + @FocusState var isNameFocused: Bool + @FocusState var isPasswordFocused: Bool + @FocusState var isDomainFocused: Bool + @FocusState var isDisplayNameFocused: Bool + + var body: some View { + GeometryReader { geometry in + ScrollView(.vertical) { + VStack { + ZStack { + Image("mountain") + .resizable() + .scaledToFill() + .frame(width: geometry.size.width, height: 100) + .clipped() + + VStack(alignment: .leading) { + HStack { + Image("caret-left") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.top, -65) + .onTapGesture { + withAnimation { + accountLoginViewModel.domain = "sip.linphone.org" + accountLoginViewModel.transportType = "TLS" + dismiss() + } + } + + Spacer() + } + .padding(.leading) + } + .frame(width: geometry.size.width) + + Text("Use a SIP account") + .default_text_style_white_800(styleSize: 20) + .padding(.top, 20) + } + .padding(.top, 35) + .padding(.bottom, 10) + + VStack(alignment: .leading) { + Text(String(localized: "username")+"*") + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + TextField("username", text: $accountLoginViewModel.username) + .default_text_style(styleSize: 15) + .disabled(coreContext.loggedIn) + .frame(height: 25) + .padding(.horizontal, 20) + .padding(.vertical, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(isNameFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1) + ) + .padding(.bottom) + .focused($isNameFocused) + + Text(String(localized: "password")+"*") + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + ZStack(alignment: .trailing) { + Group { + if isSecured { + SecureField("password", text: $accountLoginViewModel.passwd) + .default_text_style(styleSize: 15) + .frame(height: 25) + .focused($isPasswordFocused) + } else { + TextField("password", text: $accountLoginViewModel.passwd) + .default_text_style(styleSize: 15) + .frame(height: 25) + .focused($isPasswordFocused) + } + } + Button(action: { + isSecured.toggle() + }, label: { + Image(self.isSecured ? "eye-slash" : "eye") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 20, height: 20) + }) + } + .disabled(coreContext.loggedIn) + .padding(.horizontal, 20) + .padding(.vertical, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(isPasswordFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1) + ) + .padding(.bottom) + + Text(String(localized: "Domain")+"*") + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + TextField("sip.linphone.org", text: $accountLoginViewModel.domain) + .default_text_style(styleSize: 15) + .disabled(coreContext.loggedIn) + .frame(height: 25) + .padding(.horizontal, 20) + .padding(.vertical, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(isDomainFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1) + ) + .padding(.bottom) + .focused($isDomainFocused) + + Text(String(localized: "Display Name")) + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + TextField("Display Name", text: $accountLoginViewModel.displayName) + .default_text_style(styleSize: 15) + .disabled(coreContext.loggedIn) + .frame(height: 25) + .padding(.horizontal, 20) + .padding(.vertical, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(isDisplayNameFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1) + ) + .padding(.bottom) + .focused($isDisplayNameFocused) + + Text(String(localized: "Transport")) + .default_text_style_700(styleSize: 15) + .padding(.bottom, -5) + + Menu { + Button("TLS") {accountLoginViewModel.transportType = "TLS"} + Button("TCP") {accountLoginViewModel.transportType = "TCP"} + Button("UDP") {accountLoginViewModel.transportType = "UDP"} + } label: { + Text(accountLoginViewModel.transportType) + .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(.bottom) + + Spacer() + + Button(action: { + self.accountLoginViewModel.login() + accountLoginViewModel.domain = "sip.linphone.org" + accountLoginViewModel.transportType = "TLS" + }, label: { + Text(coreContext.loggedIn ? "Log out" : "assistant_account_login") + .default_text_style_white_600(styleSize: 20) + .frame(height: 35) + .frame(maxWidth: .infinity) + }) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background( + (accountLoginViewModel.username.isEmpty || accountLoginViewModel.passwd.isEmpty || accountLoginViewModel.domain.isEmpty) + ? Color.orangeMain100 + : Color.orangeMain500) + .cornerRadius(60) + .disabled(accountLoginViewModel.username.isEmpty || accountLoginViewModel.passwd.isEmpty || accountLoginViewModel.domain.isEmpty) + .padding(.bottom, geometry.safeAreaInsets.bottom.isEqual(to: 0.0) ? 20 : 0) + } + .frame(maxWidth: sharedMainViewModel.maxWidth) + .padding(.horizontal, 20) + } + .frame(minHeight: geometry.size.height) + } + } + .navigationBarHidden(true) + } +} + +#Preview { + ThirdPartySipAccountLoginFragment(sharedMainViewModel: SharedMainViewModel(), accountLoginViewModel: AccountLoginViewModel()) +} diff --git a/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountWarningFragment.swift b/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountWarningFragment.swift new file mode 100644 index 000000000..4ce290e27 --- /dev/null +++ b/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountWarningFragment.swift @@ -0,0 +1,192 @@ +/* + * 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 + +struct ThirdPartySipAccountWarningFragment: View { + + @ObservedObject var sharedMainViewModel: SharedMainViewModel + @ObservedObject private var coreContext = CoreContext.shared + @ObservedObject var accountLoginViewModel: AccountLoginViewModel + + @Environment(\.dismiss) var dismiss + + var body: some View { + NavigationView { + GeometryReader { geometry in + ScrollView(.vertical) { + VStack { + ZStack { + Image("mountain") + .resizable() + .scaledToFill() + .frame(width: geometry.size.width, height: 100) + .clipped() + + VStack(alignment: .leading) { + HStack { + Image("caret-left") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.top, -65) + .onTapGesture { + withAnimation { + dismiss() + } + } + + Spacer() + } + .padding(.leading) + } + .frame(width: geometry.size.width) + + Text("Use a SIP account") + .default_text_style_white_800(styleSize: 20) + .padding(.top, 20) + } + .padding(.top, 35) + .padding(.bottom, 10) + + Spacer() + + VStack(alignment: .leading) { + HStack { + Spacer() + HStack(alignment: .center) { + Image("conversation") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 20, height: 20, alignment: .leading) + .onTapGesture { + withAnimation { + dismiss() + } + } + } + .padding(16) + .background(Color.grayMain2c200) + .cornerRadius(40) + .padding(.horizontal) + + HStack(alignment: .center) { + Image("video-call") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c500) + .frame(width: 20, height: 20, alignment: .leading) + .onTapGesture { + withAnimation { + dismiss() + } + } + } + .padding(16) + .background(Color.grayMain2c200) + .cornerRadius(40) + .padding(.horizontal) + + Spacer() + } + .padding(.bottom, 40) + + Text("Some features require a Linphone account, such as group messaging, video conferences...\n\n" + + "These features are hidden when you register with a third party SIP account.\n\n" + + "To enable it in a commercial projet, please contact us. ") + .default_text_style(styleSize: 15) + .multilineTextAlignment(.center) + .padding(.bottom) + + HStack { + Spacer() + + HStack { + Text("[linphone.org/contact](https://linphone.org/contact)") + .tint(Color.orangeMain500) + .default_text_style_orange_600(styleSize: 15) + .frame(height: 35) + } + .padding(.horizontal, 15) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(Color.orangeMain500, lineWidth: 1) + ) + + Spacer() + } + .padding(.vertical) + } + .frame(maxWidth: sharedMainViewModel.maxWidth) + .padding(.horizontal, 20) + + Spacer() + + Button(action: { + dismiss() + }, label: { + Text("I prefere create an account") + .default_text_style_orange_600(styleSize: 20) + .frame(height: 35) + .frame(maxWidth: .infinity) + }) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(Color.orangeMain500, lineWidth: 1) + ) + .frame(maxWidth: sharedMainViewModel.maxWidth) + .padding(.horizontal) + + NavigationLink(destination: { + ThirdPartySipAccountLoginFragment(sharedMainViewModel: sharedMainViewModel, accountLoginViewModel: accountLoginViewModel) + }, label: { + Text("I understand") + .default_text_style_white_600(styleSize: 20) + .frame(height: 35) + .frame(maxWidth: .infinity) + + }) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background(Color.orangeMain500) + .cornerRadius(60) + .frame(maxWidth: sharedMainViewModel.maxWidth) + .padding(.horizontal) + .padding(.bottom, geometry.safeAreaInsets.bottom.isEqual(to: 0.0) ? 20 : 0) + } + .frame(minHeight: geometry.size.height) + } + } + } + .navigationViewStyle(StackNavigationViewStyle()) + .navigationBarHidden(true) + } +} + +#Preview { + ThirdPartySipAccountWarningFragment(sharedMainViewModel: SharedMainViewModel(), accountLoginViewModel: AccountLoginViewModel()) +} diff --git a/Linphone/ui/assistant/viewmodel/AccountLoginViewModel.swift b/Linphone/UI/Assistant/Viewmodel/AccountLoginViewModel.swift similarity index 69% rename from Linphone/ui/assistant/viewmodel/AccountLoginViewModel.swift rename to Linphone/UI/Assistant/Viewmodel/AccountLoginViewModel.swift index 4ae39cee8..6f778145a 100644 --- a/Linphone/ui/assistant/viewmodel/AccountLoginViewModel.swift +++ b/Linphone/UI/Assistant/Viewmodel/AccountLoginViewModel.swift @@ -1,31 +1,34 @@ /* -* Copyright (c) 2010-2023 Belledonne Communications SARL. -* -* This file is part of Linphone -* -* 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 . -*/ + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 AccountLoginViewModel : ObservableObject { +class AccountLoginViewModel: ObservableObject { - var coreContext = CoreContext.shared - @Published var username : String = "user" - @Published var passwd : String = "pwd" - @Published var domain : String = "sip.example.org" - @Published var transportType : String = "TLS" + private var coreContext = CoreContext.shared + + @Published var username: String = "" + @Published var passwd: String = "" + @Published var domain: String = "sip.linphone.org" + @Published var displayName: String = "" + @Published var transportType: String = "TLS" init() {} @@ -34,10 +37,12 @@ class AccountLoginViewModel : ObservableObject { // Get the transport protocol to use. // TLS is strongly recommended // Only use UDP if you don't have the choice - var transport : TransportType - if (transportType == "TLS") { transport = TransportType.Tls } - else if (transportType == "TCP") { transport = TransportType.Tcp } - else { transport = TransportType.Udp } + var transport: TransportType + if transportType == "TLS" { + transport = TransportType.Tls + } else if transportType == "TCP" { + transport = TransportType.Tcp + } else { transport = TransportType.Udp } // To configure a SIP account, we need an Account object and an AuthInfo object // The first one is how to connect to the proxy server, the second one stores the credentials @@ -55,7 +60,7 @@ class AccountLoginViewModel : ObservableObject { // A SIP account is identified by an identity address that we can construct from the username and domain let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain)) - try! accountParams.setIdentityaddress(newValue: identity) + try accountParams.setIdentityaddress(newValue: identity) // We also need to configure where the proxy server is located let address = try Factory.Instance.createAddress(addr: String("sip:" + domain)) diff --git a/Linphone/UI/Assistant/Viewmodel/QRScanner.swift b/Linphone/UI/Assistant/Viewmodel/QRScanner.swift new file mode 100644 index 000000000..d5dd78159 --- /dev/null +++ b/Linphone/UI/Assistant/Viewmodel/QRScanner.swift @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 SwiftUI +import AVFoundation + +struct QRScanner: UIViewControllerRepresentable { + + @Binding var result: String + + func makeUIViewController(context: Context) -> QRScannerController { + let controller = QRScannerController() + controller.delegate = context.coordinator + + return controller + } + + func makeCoordinator() -> Coordinator { + Coordinator($result) + } + + func updateUIViewController(_ uiViewController: QRScannerController, context: Context) { + } +} + +class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate { + + private var coreContext = CoreContext.shared + + @Binding var scanResult: String + private var lastResult: String = "" + + init(_ scanResult: Binding) { + self._scanResult = scanResult + } + + func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { + + // Check if the metadataObjects array is not nil and it contains at least one object. + if metadataObjects.isEmpty { + scanResult = "Scan a QR code" + return + } + + // Get the metadata object. + guard let metadataObj = metadataObjects[0] as? AVMetadataMachineReadableCodeObject else { + return + } + + if metadataObj.type == AVMetadataObject.ObjectType.qr, + let result = metadataObj.stringValue { + if !result.isEmpty && result != lastResult { + if let url = NSURL(string: result) { + if UIApplication.shared.canOpenURL(url as URL) { + lastResult = result + do { + try coreContext.mCore.setProvisioninguri(newValue: result) + coreContext.mCore.stop() + try coreContext.mCore.start() + } catch { + + } + + } else { + coreContext.toastMessage = "Invalide URI" + } + } else { + coreContext.toastMessage = "Invalide URI" + } + } + } + } +} diff --git a/Linphone/UI/Assistant/Viewmodel/QRScannerController.swift b/Linphone/UI/Assistant/Viewmodel/QRScannerController.swift new file mode 100644 index 000000000..1a4042241 --- /dev/null +++ b/Linphone/UI/Assistant/Viewmodel/QRScannerController.swift @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 SwiftUI +import AVFoundation + +class QRScannerController: UIViewController { + var captureSession = AVCaptureSession() + var videoPreviewLayer: AVCaptureVideoPreviewLayer? + var qrCodeFrameView: UIView? + + var delegate: AVCaptureMetadataOutputObjectsDelegate? + + override func viewDidLoad() { + super.viewDidLoad() + + // Get the back-facing camera for capturing videos + guard let captureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { + print("Failed to get the camera device") + return + } + + let videoInput: AVCaptureDeviceInput + + do { + // Get an instance of the AVCaptureDeviceInput class using the previous device object. + videoInput = try AVCaptureDeviceInput(device: captureDevice) + + } catch { + // If any error occurs, simply print it out and don't continue any more. + print(error) + return + } + + // Set the input device on the capture session. + captureSession.addInput(videoInput) + + // Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session. + let captureMetadataOutput = AVCaptureMetadataOutput() + captureSession.addOutput(captureMetadataOutput) + + // Set delegate and use the default dispatch queue to execute the call back + captureMetadataOutput.setMetadataObjectsDelegate(delegate, queue: DispatchQueue.main) + captureMetadataOutput.metadataObjectTypes = [ .qr ] + + // Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer. + videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession) + videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill + videoPreviewLayer?.frame = view.layer.bounds + view.layer.addSublayer(videoPreviewLayer!) + + // Start video capture. + DispatchQueue.global(qos: .background).async { + self.captureSession.startRunning() + } + + } + +} diff --git a/Linphone/UI/Main/Contacts/ContactsView.swift b/Linphone/UI/Main/Contacts/ContactsView.swift new file mode 100644 index 000000000..1c5d3495a --- /dev/null +++ b/Linphone/UI/Main/Contacts/ContactsView.swift @@ -0,0 +1,36 @@ +/* + * 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 + +struct ContactsView: View { + var body: some View { + VStack { + Spacer() + Image("linphone") + .padding(.bottom, 20) + Text("Contacts View") + Spacer() + } + } +} + +#Preview { + ContactsView() +} diff --git a/Linphone/UI/Main/ContentView.swift b/Linphone/UI/Main/ContentView.swift new file mode 100644 index 000000000..ccb8e9a67 --- /dev/null +++ b/Linphone/UI/Main/ContentView.swift @@ -0,0 +1,50 @@ +/* + * 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 + +struct ContentView: View { + + @ObservedObject var sharedMainViewModel: SharedMainViewModel + @ObservedObject private var coreContext = CoreContext.shared + + var body: some View { + if !sharedMainViewModel.welcomeViewDisplayed { + WelcomeView(sharedMainViewModel: sharedMainViewModel) + } else if coreContext.mCore.defaultAccount == nil || sharedMainViewModel.displayProfileMode { + AssistantView(sharedMainViewModel: sharedMainViewModel) + } else { + TabView { + ContactsView() + .tabItem { + Label("Contacts", image: "address-book") + } + + HistoryView() + .tabItem { + Label("Calls", image: "phone") + } + } + } + } +} + +#Preview { + ContentView(sharedMainViewModel: SharedMainViewModel()) +} diff --git a/Linphone/UI/Main/Fragments/PopupLoadingView.swift b/Linphone/UI/Main/Fragments/PopupLoadingView.swift new file mode 100644 index 000000000..99f4f063e --- /dev/null +++ b/Linphone/UI/Main/Fragments/PopupLoadingView.swift @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 + +struct PopupLoadingView: View { + + @ObservedObject var sharedMainViewModel: SharedMainViewModel + + var body: some View { + GeometryReader { geometry in + VStack(alignment: .leading) { + + ProgressView() + .controlSize(.large) + .progressViewStyle(CircularProgressViewStyle(tint: Color.orangeMain500)) + .frame(maxWidth: .infinity) + .padding(.top) + .padding(.bottom) + + Text("Opération en cours...") + .tint(Color.grayMain2c600) + .default_text_style(styleSize: 15) + .frame(maxWidth: .infinity) + } + .padding(.horizontal, 20) + .padding(.vertical, 20) + .background(.white) + .cornerRadius(20) + .padding(.horizontal) + .frame(maxHeight: .infinity) + .frame(maxWidth: .infinity) + .shadow(color: Color.orangeMain500, radius: 0, x: 0, y: 2) + .frame(maxWidth: sharedMainViewModel.maxWidth) + .position(x: geometry.size.width / 2, y: geometry.size.height / 2) + } + } +} + +#Preview { + PopupLoadingView(sharedMainViewModel: SharedMainViewModel()) + .background(.black.opacity(0.65)) +} diff --git a/Linphone/UI/Main/Fragments/PopupView.swift b/Linphone/UI/Main/Fragments/PopupView.swift new file mode 100644 index 000000000..dbfd53afa --- /dev/null +++ b/Linphone/UI/Main/Fragments/PopupView.swift @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 Photos + +struct PopupView: View { + + @ObservedObject var sharedMainViewModel: SharedMainViewModel + + var permissionManager = PermissionManager.shared + + @Binding var isShowPopup: Bool + var title: Text + var content: Text? + + var titleFirstButton: Text? + var actionFirstButton: () -> Void + + var titleSecondButton: Text? + var actionSecondButton: () -> Void + + var body: some View { + GeometryReader { geometry in + VStack(alignment: .leading) { + title + .default_text_style_800(styleSize: 16) + .frame(alignment: .leading) + .padding(.bottom, 2) + + if content != nil { + content + .tint(Color.grayMain2c600) + .default_text_style(styleSize: 15) + .padding(.bottom, 20) + } + + if titleFirstButton != nil { + Button(action: { + actionFirstButton() + }, label: { + titleFirstButton + .default_text_style_orange_600(styleSize: 20) + .frame(height: 35) + .frame(maxWidth: .infinity) + }) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .cornerRadius(60) + .overlay( + RoundedRectangle(cornerRadius: 60) + .inset(by: 0.5) + .stroke(Color.orangeMain500, lineWidth: 1) + ) + .padding(.bottom, 10) + } + + if titleSecondButton != nil { + Button(action: { + actionSecondButton() + }, label: { + titleSecondButton + .default_text_style_white_600(styleSize: 20) + .frame(height: 35) + .frame(maxWidth: .infinity) + }) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background(Color.orangeMain500) + .cornerRadius(60) + } + } + .padding(.horizontal, 20) + .padding(.vertical, 20) + .background(.white) + .cornerRadius(20) + .padding(.horizontal) + .frame(maxHeight: .infinity) + .shadow(color: Color.orangeMain500, radius: 0, x: 0, y: 2) + .frame(maxWidth: sharedMainViewModel.maxWidth) + .position(x: geometry.size.width / 2, y: geometry.size.height / 2) + } + } +} + +#Preview { + PopupView(sharedMainViewModel: SharedMainViewModel(), isShowPopup: .constant(true), + title: Text("Title"), + content: Text("Content"), + titleFirstButton: Text("Deny all"), + actionFirstButton: {}, + titleSecondButton: Text("Accept all"), + actionSecondButton: {}) + .background(.black.opacity(0.65)) +} diff --git a/Linphone/UI/Main/Fragments/ToastView.swift b/Linphone/UI/Main/Fragments/ToastView.swift new file mode 100644 index 000000000..ab092763d --- /dev/null +++ b/Linphone/UI/Main/Fragments/ToastView.swift @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 + +struct ToastView: ViewModifier { + + @ObservedObject var sharedMainViewModel: SharedMainViewModel + + @Binding var isShowing: String + + func body(content: Content) -> some View { + ZStack { + content + toastView + } + } + + private var toastView: some View { + VStack { + if !isShowing.isEmpty { + HStack { + Image(isShowing == "Successful" ? "success" : "danger") + .resizable() + .frame(width: 25, height: 25, alignment: .leading) + + switch isShowing { + case "Successful": + Text("QR code validated!") + .multilineTextAlignment(.center) + .foregroundStyle(Color.greenSuccess500) + .default_text_style(styleSize: 15) + .padding(8) + + case "Failed": + Text("Invalid QR code!") + .multilineTextAlignment(.center) + .foregroundStyle(Color.redDanger500) + .default_text_style(styleSize: 15) + .padding(8) + + case "Invalide URI": + Text("Invalide URI") + .multilineTextAlignment(.center) + .foregroundStyle(Color.redDanger500) + .default_text_style(styleSize: 15) + .padding(8) + + case "Registration failed": + Text("The user name or password is incorrects") + .multilineTextAlignment(.center) + .foregroundStyle(Color.redDanger500) + .default_text_style(styleSize: 15) + .padding(8) + + default: + Text("Error") + .multilineTextAlignment(.center) + .foregroundStyle(Color.redDanger500) + .default_text_style(styleSize: 15) + .padding(8) + } + } + .frame(maxWidth: .infinity) + .background(.white) + .cornerRadius(50) + .overlay( + RoundedRectangle(cornerRadius: 50) + .inset(by: 0.5) + .stroke(isShowing == "Successful" ? Color.greenSuccess500 : Color.redDanger500, lineWidth: 1) + ) + .onTapGesture { + isShowing = "" + } + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + isShowing = "" + } + } + } + Spacer() + } + .frame(maxWidth: sharedMainViewModel.maxWidth) + .padding(.horizontal, 16) + .padding(.bottom, 18) + .animation(.linear(duration: 0.3), value: isShowing) + .transition(.opacity) + } +} + +extension View { + func toast(isShowing: Binding) -> some View { + self.modifier(ToastView(sharedMainViewModel: SharedMainViewModel(), isShowing: isShowing)) + } +} diff --git a/Linphone/UI/Main/History/HistoryView.swift b/Linphone/UI/Main/History/HistoryView.swift new file mode 100644 index 000000000..de3a16833 --- /dev/null +++ b/Linphone/UI/Main/History/HistoryView.swift @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 + +struct HistoryView: View { + + @ObservedObject private var coreContext = CoreContext.shared + + var body: some View { + VStack { + Spacer() + Image("linphone") + .padding(.bottom, 20) + Text("History View") + Spacer() + } + } +} + +#Preview { + HistoryView() +} diff --git a/Linphone/UI/Main/Viewmodel/SharedMainViewModel.swift b/Linphone/UI/Main/Viewmodel/SharedMainViewModel.swift new file mode 100644 index 000000000..5e8f6d39a --- /dev/null +++ b/Linphone/UI/Main/Viewmodel/SharedMainViewModel.swift @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 SharedMainViewModel: ObservableObject { + + @Published var welcomeViewDisplayed = false + @Published var generalTermsAccepted = false + @Published var displayProfileMode = false + + var maxWidth = 400.0 + + init() { + let preferences = UserDefaults.standard + + let welcomeViewKey = "welcome_view" + + if preferences.object(forKey: welcomeViewKey) == nil { + preferences.set(welcomeViewDisplayed, forKey: welcomeViewKey) + } else { + welcomeViewDisplayed = preferences.bool(forKey: welcomeViewKey) + } + + let generalTermsKey = "general_terms" + + if preferences.object(forKey: generalTermsKey) == nil { + preferences.set(generalTermsAccepted, forKey: generalTermsKey) + } else { + generalTermsAccepted = preferences.bool(forKey: generalTermsKey) + } + + let displayProfileModeKey = "display_profile_mode" + + if preferences.object(forKey: displayProfileModeKey) == nil { + preferences.set(displayProfileMode, forKey: displayProfileModeKey) + } else { + displayProfileMode = preferences.bool(forKey: displayProfileModeKey) + } + } + + func changeWelcomeView() { + let preferences = UserDefaults.standard + + welcomeViewDisplayed = true + let welcomeViewKey = "welcome_view" + preferences.set(welcomeViewDisplayed, forKey: welcomeViewKey) + } + + func changeGeneralTerms() { + let preferences = UserDefaults.standard + + generalTermsAccepted = true + let generalTermsKey = "general_terms" + preferences.set(generalTermsAccepted, forKey: generalTermsKey) + } + + func changeDisplayProfileMode() { + let preferences = UserDefaults.standard + + displayProfileMode = true + let displayProfileModeKey = "display_profile_mode" + preferences.set(displayProfileMode, forKey: displayProfileModeKey) + } + + func changeHideProfileMode() { + let preferences = UserDefaults.standard + + displayProfileMode = false + let displayProfileModeKey = "display_profile_mode" + preferences.set(displayProfileMode, forKey: displayProfileModeKey) + } +} diff --git a/Linphone/UI/Welcome/Fragments/WelcomePage1Fragment.swift b/Linphone/UI/Welcome/Fragments/WelcomePage1Fragment.swift new file mode 100644 index 000000000..aabdc6503 --- /dev/null +++ b/Linphone/UI/Welcome/Fragments/WelcomePage1Fragment.swift @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 SwiftUI + +struct WelcomePage1Fragment: View { + + var body: some View { + VStack { + Spacer() + VStack { + Image("linphone") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.orangeMain500) + .frame(width: 100, height: 100) + Text("Linphone") + .welcome_text_style_gray_800(styleSize: 30) + .padding(.bottom, 20) + Text("Une application de communication **sécurisée**, **open source** et **française**.") + .welcome_text_style_gray(styleSize: 15) + .multilineTextAlignment(.center) + + } + Spacer() + Spacer() + } + .frame(maxWidth: .infinity) + .padding(.horizontal, 20) + } +} + +#Preview { + WelcomePage1Fragment() +} diff --git a/Linphone/UI/Welcome/Fragments/WelcomePage2Fragment.swift b/Linphone/UI/Welcome/Fragments/WelcomePage2Fragment.swift new file mode 100644 index 000000000..45f1e8fa2 --- /dev/null +++ b/Linphone/UI/Welcome/Fragments/WelcomePage2Fragment.swift @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 SwiftUI + +struct WelcomePage2Fragment: View { + + var body: some View { + VStack { + Spacer() + VStack { + Image("secure-image") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.orangeMain500) + .frame(width: 70, height: 100) + Text("Sécurisé") + .welcome_text_style_gray_800(styleSize: 30) + .padding(.bottom, 20) + Text("Vos communications sont en sécurité grâce aux **Chiffrement de bout en bout**.") + .welcome_text_style_gray(styleSize: 15) + .multilineTextAlignment(.center) + + } + Spacer() + Spacer() + } + .frame(maxWidth: .infinity) + } +} + +#Preview { + WelcomePage2Fragment() +} diff --git a/Linphone/UI/Welcome/Fragments/WelcomePage3Fragment.swift b/Linphone/UI/Welcome/Fragments/WelcomePage3Fragment.swift new file mode 100644 index 000000000..1f9256037 --- /dev/null +++ b/Linphone/UI/Welcome/Fragments/WelcomePage3Fragment.swift @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 SwiftUI + +struct WelcomePage3Fragment: View { + + var body: some View { + VStack { + Spacer() + VStack { + Image("open-source") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.orangeMain500) + .frame(width: 100, height: 100) + Text("Open source") + .welcome_text_style_gray_800(styleSize: 30) + .padding(.bottom, 20) + Text("Une application open source et un **service gratuit** depuis **2001**.") + .welcome_text_style_gray(styleSize: 15) + .multilineTextAlignment(.center) + + } + Spacer() + Spacer() + } + .frame(maxWidth: .infinity) + } +} + +#Preview { + WelcomePage3Fragment() +} diff --git a/Linphone/UI/Welcome/WelcomeView.swift b/Linphone/UI/Welcome/WelcomeView.swift new file mode 100644 index 000000000..9bad70c3a --- /dev/null +++ b/Linphone/UI/Welcome/WelcomeView.swift @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 + +struct WelcomeView: View { + + @ObservedObject var sharedMainViewModel: SharedMainViewModel + + @State private var index = 0 + + var body: some View { + NavigationView { + GeometryReader { geometry in + ScrollView { + VStack { + ZStack { + Image("mountain") + .resizable() + .scaledToFill() + .frame(width: geometry.size.width, height: 100) + .clipped() + + VStack(alignment: .trailing) { + NavigationLink(destination: { + PermissionsFragment(sharedMainViewModel: sharedMainViewModel) + }, label: { + Text("Skip") + .underline() + .default_text_style_600(styleSize: 15) + + }) + .padding(.top, -35) + .padding(.trailing, 20) + .simultaneousGesture( + TapGesture().onEnded { + self.index = 2 + } + ) + Text("Welcome") + .welcome_text_style_white_800(styleSize: 35) + .padding(.trailing, 100) + .frame(width: geometry.size.width) + .padding(.bottom, -25) + Text("to Linphone") + .welcome_text_style_white_800(styleSize: 25) + .padding(.leading, 100) + .frame(width: geometry.size.width) + .padding(.bottom, -10) + } + .frame(width: geometry.size.width) + } + .padding(.top, 35) + .padding(.bottom, 10) + + Spacer() + + VStack { + TabView(selection: $index) { + ForEach((0..<3), id: \.self) { index in + if index == 0 { + WelcomePage1Fragment() + } else if index == 1 { + WelcomePage2Fragment() + } else if index == 2 { + WelcomePage3Fragment() + } else { + WelcomePage1Fragment() + } + } + } + .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)) + .frame(minHeight: 300) + .onAppear { + setupAppearance() + } + } + + Spacer() + + if index == 2 { + NavigationLink(destination: { + PermissionsFragment(sharedMainViewModel: sharedMainViewModel) + }, label: { + Text("Start") + .default_text_style_white_600(styleSize: 20) + .frame(height: 35) + .frame(maxWidth: .infinity) + + }) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background(Color.orangeMain500) + .cornerRadius(60) + .padding(.horizontal) + .padding(.bottom, geometry.safeAreaInsets.bottom.isEqual(to: 0.0) ? 20 : 0) + .frame(maxWidth: sharedMainViewModel.maxWidth) + } else { + Button(action: { + withAnimation { + index += 1 + } + }, label: { + Text("Next") + .default_text_style_white_600(styleSize: 20) + .frame(height: 35) + .frame(maxWidth: .infinity) + }) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background(Color.orangeMain500) + .cornerRadius(60) + .padding(.horizontal) + .padding(.bottom, geometry.safeAreaInsets.bottom.isEqual(to: 0.0) ? 20 : 0) + .frame(maxWidth: sharedMainViewModel.maxWidth) + } + } + .frame(minHeight: geometry.size.height) + } + } + } + } + + func setupAppearance() { + UIPageControl.appearance().currentPageIndicatorTintColor = UIColor(Color.orangeMain500) + if #available(iOS 16.0, *) { + + let dotCurrentImage = UIImage(named: "current-dot") + let dotImage = UIImage(named: "dot") + + UIPageControl.appearance().setCurrentPageIndicatorImage(dotCurrentImage, forPage: 0) + UIPageControl.appearance().setCurrentPageIndicatorImage(dotCurrentImage, forPage: 1) + UIPageControl.appearance().setCurrentPageIndicatorImage(dotCurrentImage, forPage: 2) + + UIPageControl.appearance().setIndicatorImage(dotImage, forPage: 0) + UIPageControl.appearance().setIndicatorImage(dotImage, forPage: 1) + UIPageControl.appearance().setIndicatorImage(dotImage, forPage: 2) + } + UIPageControl.appearance().pageIndicatorTintColor = UIColor(Color.grayMain2c200) + } +} + +#Preview { + WelcomeView(sharedMainViewModel: SharedMainViewModel()) +} diff --git a/Linphone/Utils/ColorExtension.swift b/Linphone/Utils/ColorExtension.swift new file mode 100644 index 000000000..19fc96c2e --- /dev/null +++ b/Linphone/Utils/ColorExtension.swift @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 SwiftUI + +extension Color { + + static let transparentColor = Color(hex: "#00000000") + static let black = Color(hex: "#000000") + static let white = Color(hex: "#FFFFFF") + + static let orangeMain700 = Color(hex: "#B72D00") + static let orangeMain500 = Color(hex: "#FF5E00") + static let orangeMain300 = Color(hex: "#FFB266") + static let orangeMain100 = Color(hex: "#FFEACB") + static let orangeMain100Alpha50 = Color(hex: "#80FFEACB") + + static let grayMain2c800 = Color(hex: "#22334D") + static let grayMain2c800Alpha65 = Color(hex: "#A622334D") + static let grayMain2c700 = Color(hex: "#364860") + static let grayMain2c600 = Color(hex: "#4E6074") + static let grayMain2c500 = Color(hex: "#6C7A87") + static let grayMain2c400 = Color(hex: "#9AABB5") + static let grayMain2c300 = Color(hex: "#C0D1D9") + static let grayMain2c200 = Color(hex: "#DFECF2") + static let grayMain2c100 = Color(hex: "#EEF6F8") + + static let gray100 = Color(hex: "#F9F9F9") + static let gray200 = Color(hex: "#EDEDED") + static let gray300 = Color(hex: "#C9C9C9") + static let gray400 = Color(hex: "#949494") + static let gray500 = Color(hex: "#4E4E4E") + static let gray600 = Color(hex: "#2E3030") + static let gray900 = Color(hex: "#070707") + + static let redDanger200 = Color(hex: "#F5CCBE") + static let redDanger500 = Color(hex: "#DD5F5F") + static let redDanger700 = Color(hex: "#9E3548") + + static let greenSuccess500 = Color(hex: "#4FAE80") + static let greenSuccess700 = Color(hex: "#377D71") + static let greenSuccess200 = Color(hex: "#ACF5C1") + + static let blueInfo500 = Color(hex: "#4AA8FF") + + static let orangeWarning600 = Color(hex: "#DBB820") + + static let orangeAway = Color(hex: "#FFA645") + + init(hex: String) { + let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) + var int: UInt64 = 0 + Scanner(string: hex).scanHexInt64(&int) + let alpha, red, green, blue: UInt64 + switch hex.count { + case 3: // RGB (12-bit) + (alpha, red, green, blue) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (alpha, red, green, blue) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (alpha, red, green, blue) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + (alpha, red, green, blue) = (1, 1, 1, 0) + } + + self.init( + .sRGB, + red: Double(red) / 255, + green: Double(green) / 255, + blue: Double(blue) / 255, + opacity: Double(alpha) / 255 + ) + } +} diff --git a/Linphone/Utils/PermissionManager.swift b/Linphone/Utils/PermissionManager.swift new file mode 100644 index 000000000..a39833010 --- /dev/null +++ b/Linphone/Utils/PermissionManager.swift @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 Photos + +class PermissionManager: ObservableObject { + + static let shared = PermissionManager() + + @Published var photoLibraryPermissionGranted = false + @Published var cameraPermissionGranted = false + + private init() {} + + func photoLibraryRequestPermission() { + PHPhotoLibrary.requestAuthorization(for: .readWrite, handler: {status in + DispatchQueue.main.async { + self.photoLibraryPermissionGranted = (status == .authorized || status == .limited || status == .restricted) + } + }) + } + + func cameraRequestPermission() { + AVCaptureDevice.requestAccess(for: .video, completionHandler: {accessGranted in + DispatchQueue.main.async { + self.cameraPermissionGranted = accessGranted + } + }) + } +} diff --git a/Linphone/Utils/TextExtension.swift b/Linphone/Utils/TextExtension.swift new file mode 100644 index 000000000..e9d58ae0b --- /dev/null +++ b/Linphone/Utils/TextExtension.swift @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * 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 SwiftUI + +extension View { + + func default_text_style_300(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Light", size: styleSize)) + .foregroundStyle(Color.grayMain2c600) + } + + func default_text_style(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Regular", size: styleSize)) + .foregroundStyle(Color.grayMain2c600) + } + + func default_text_style_500(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Medium", size: styleSize)) + .foregroundStyle(Color.grayMain2c600) + } + + func default_text_style_600(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-SemiBold", size: styleSize)) + .foregroundStyle(Color.grayMain2c600) + } + + func default_text_style_700(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Bold", size: styleSize)) + .foregroundStyle(Color.grayMain2c600) + } + + func default_text_style_800(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-ExtraBold", size: styleSize)) + .foregroundStyle(Color.grayMain2c600) + } + + func default_text_style_white_300(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Light", size: styleSize)) + .foregroundStyle(Color.white) + } + + func default_text_style_white(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Regular", size: styleSize)) + .foregroundStyle(Color.white) + } + + func default_text_style_white_500(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Medium", size: styleSize)) + .foregroundStyle(Color.white) + } + + func default_text_style_white_600(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-SemiBold", size: styleSize)) + .foregroundStyle(Color.white) + } + + func default_text_style_white_700(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Bold", size: styleSize)) + .foregroundStyle(Color.white) + } + + func default_text_style_white_800(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-ExtraBold", size: styleSize)) + .foregroundStyle(Color.white) + } + + func default_text_style_orange_300(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Light", size: styleSize)) + .foregroundStyle(Color.orangeMain500) + } + + func default_text_style_orange(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Regular", size: styleSize)) + .foregroundStyle(Color.orangeMain500) + } + + func default_text_style_orange_500(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Medium", size: styleSize)) + .foregroundStyle(Color.orangeMain500) + } + + func default_text_style_orange_600(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-SemiBold", size: styleSize)) + .foregroundStyle(Color.orangeMain500) + } + + func default_text_style_orange_700(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Bold", size: styleSize)) + .foregroundStyle(Color.orangeMain500) + } + + func default_text_style_orange_800(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-ExtraBold", size: styleSize)) + .foregroundStyle(Color.orangeMain500) + } + + func welcome_text_style_white_800(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-ExtraBold", size: styleSize)) + .foregroundStyle(Color.white) + } + + func welcome_text_style_gray_800(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-ExtraBold", size: styleSize)) + .foregroundStyle(Color.grayMain2c600) + } + + func welcome_text_style_gray(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Regular", size: styleSize)) + .foregroundStyle(Color.grayMain2c600) + } + + func profile_mode_text_style_gray_800(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-ExtraBold", size: styleSize)) + .foregroundStyle(Color.gray900) + } + + func profile_mode_text_style_gray(styleSize: CGFloat) -> some View { + self.font(Font.custom("NotoSans-Regular", size: styleSize)) + .foregroundStyle(Color.grayMain2c600) + } +} diff --git a/Linphone/core/CoreContext.swift b/Linphone/core/CoreContext.swift deleted file mode 100644 index fbc995456..000000000 --- a/Linphone/core/CoreContext.swift +++ /dev/null @@ -1,54 +0,0 @@ -/* -* Copyright (c) 2010-2023 Belledonne Communications SARL. -* -* This file is part of Linphone -* -* 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 CoreContext : ObservableObject { - - static let shared = CoreContext() - - var mCore: Core! - var mRegistrationDelegate : CoreDelegate! - - var coreVersion: String = Core.getVersion - @Published var loggedIn: Bool = false - - private init() { - - LoggingService.Instance.logLevel = LogLevel.Debug - - try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil) - try? mCore.start() - - // Create a Core listener to listen for the callback we need - // In this case, we want to know about the account registration status - mRegistrationDelegate = CoreDelegateStub(onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in - - // If account has been configured correctly, we will go through Progress and Ok states - // Otherwise, we will be Failed. - NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString()))\n") - if (state == .Ok) { - self.loggedIn = true - } else if (state == .Cleared) { - self.loggedIn = false - } - }) - mCore.addDelegate(delegate: mRegistrationDelegate) - } -} diff --git a/Linphone/ui/.DS_Store b/Linphone/ui/.DS_Store deleted file mode 100644 index 9bbad94a3..000000000 Binary files a/Linphone/ui/.DS_Store and /dev/null differ diff --git a/Linphone/ui/assistant/AssistantView.swift b/Linphone/ui/assistant/AssistantView.swift deleted file mode 100644 index f6f4e355d..000000000 --- a/Linphone/ui/assistant/AssistantView.swift +++ /dev/null @@ -1,90 +0,0 @@ -/* -* Copyright (c) 2010-2023 Belledonne Communications SARL. -* -* This file is part of Linphone -* -* 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 - -struct AssistantView: View { - - var coreContext = CoreContext.shared - @ObservedObject var accountLoginViewModel : AccountLoginViewModel - - var body: some View { - VStack { - HStack { - Text("Username:") - .font(.title) - TextField("", text : $accountLoginViewModel.username) - .textFieldStyle(RoundedBorderTextFieldStyle()) - .disabled(coreContext.loggedIn) - } - HStack { - Text("Password:") - .font(.title) - TextField("", text : $accountLoginViewModel.passwd) - .textFieldStyle(RoundedBorderTextFieldStyle()) - .disabled(coreContext.loggedIn) - } - HStack { - Text("Domain:") - .font(.title) - TextField("", text : $accountLoginViewModel.domain) - .textFieldStyle(RoundedBorderTextFieldStyle()) - .disabled(coreContext.loggedIn) - } - Picker(selection: $accountLoginViewModel.transportType, label: Text("Transport:")) { - Text("TLS").tag("TLS") - Text("TCP").tag("TCP") - Text("UDP").tag("UDP") - }.pickerStyle(SegmentedPickerStyle()).padding() - VStack { - HStack { - Button(action: { - if (self.coreContext.loggedIn) - { - self.accountLoginViewModel.unregister() - self.accountLoginViewModel.delete() - } else { - self.accountLoginViewModel.login() - } - }) - { - Text(coreContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account") - .font(.largeTitle) - .foregroundColor(Color.white) - .frame(width: 220.0, height: 90) - .background(Color.gray) - } - - } - HStack { - Text("Login State : ") - .font(.footnote) - Text(coreContext.loggedIn ? "Looged in" : "Unregistered") - .font(.footnote) - .foregroundColor(coreContext.loggedIn ? Color.green : Color.black) - }.padding(.top, 10.0) - } - Group { - Spacer() - Text("Core Version is \(coreContext.coreVersion)") - } - } - .padding() - } -} diff --git a/Linphone/ui/main/ContentView.swift b/Linphone/ui/main/ContentView.swift deleted file mode 100644 index 083a0011d..000000000 --- a/Linphone/ui/main/ContentView.swift +++ /dev/null @@ -1,33 +0,0 @@ -/* -* 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 - -struct ContentView: View { - - var body: some View { - AssistantView(accountLoginViewModel: AccountLoginViewModel()) - } -} - -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - AssistantView(accountLoginViewModel: AccountLoginViewModel()) - } -} diff --git a/Podfile b/Podfile index 4ecc8821a..18951b82a 100644 --- a/Podfile +++ b/Podfile @@ -23,3 +23,11 @@ target 'Linphone' do basic_pods end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0' + end + end +end \ No newline at end of file