diff --git a/Linphone.xcodeproj/project.pbxproj b/Linphone.xcodeproj/project.pbxproj index f4fe4d61b..f086c50b4 100644 --- a/Linphone.xcodeproj/project.pbxproj +++ b/Linphone.xcodeproj/project.pbxproj @@ -18,6 +18,10 @@ 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 */; }; @@ -55,6 +59,10 @@ 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 = ""; }; @@ -114,6 +122,7 @@ D717071D2AC5922E0037746F /* ColorExtension.swift */, D717071F2AC5989C0037746F /* TextExtension.swift */, D74C9D002ACB098C0021626A /* PermissionManager.swift */, + D72343312ACEFF58009AA24E /* QRScannerController.swift */, ); path = Utils; sourceTree = ""; @@ -206,6 +215,7 @@ isa = PBXGroup; children = ( D719ABCE2ABC779A00B41C10 /* AccountLoginViewModel.swift */, + D72343332ACEFFC3009AA24E /* QRScanner.swift */, ); path = Viewmodel; sourceTree = ""; @@ -224,6 +234,7 @@ isa = PBXGroup; children = ( D74C9CFE2ACAEC5E0021626A /* PopupView.swift */, + D72343352AD037AF009AA24E /* ToastView.swift */, ); path = Fragments; sourceTree = ""; @@ -281,6 +292,7 @@ D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */, D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */, D748BF2D2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift */, + D723432F2ACEFEF8009AA24E /* QrCodeScannerFragment.swift */, ); path = Fragments; sourceTree = ""; @@ -420,6 +432,10 @@ D748BF2E2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift in Sources */, D748BF2C2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift in Sources */, D74C9CF82ACACECE0021626A /* WelcomePage1Fragment.swift in Sources */, + D72343362AD037AF009AA24E /* ToastView.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 */, @@ -563,6 +579,7 @@ 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; @@ -606,6 +623,7 @@ 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; 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/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/Core/CoreContext.swift b/Linphone/Core/CoreContext.swift index 789384f73..444526dac 100644 --- a/Linphone/Core/CoreContext.swift +++ b/Linphone/Core/CoreContext.swift @@ -1,21 +1,21 @@ /* -* 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 @@ -25,9 +25,11 @@ final class CoreContext : ObservableObject { var mCore: Core! var mRegistrationDelegate : CoreDelegate! + var mConfigurationDelegate : CoreDelegate! var coreVersion: String = Core.getVersion - @Published var loggedIn : Bool = false + @Published var loggedIn : Bool = false + @Published var configuringSuccessful : String = "" private init() {} @@ -41,17 +43,30 @@ final class CoreContext : ObservableObject { // 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 + + mRegistrationDelegate = + CoreDelegateStub( + onConfiguringStatus: { (core: Core, state: Config.ConfiguringState, message: String) in + NSLog("New configuration state is \(state) = \(message)\n") + if (state == .Successful) { + self.configuringSuccessful = "Successful" + } else { + self.configuringSuccessful = "Failed" + } + }, - // 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 + 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/LinphoneApp.swift b/Linphone/LinphoneApp.swift index 2b7f833fa..e63f62ae4 100644 --- a/Linphone/LinphoneApp.swift +++ b/Linphone/LinphoneApp.swift @@ -22,12 +22,14 @@ import SwiftUI @main struct LinphoneApp: App { + @ObservedObject private var coreContext = CoreContext.shared @State private var isActive = false var body: some Scene { WindowGroup { if isActive { ContentView(sharedMainViewModel: SharedMainViewModel()) + .toast(isShowing: $coreContext.configuringSuccessful) }else { SplashScreen(isActive: $isActive) } diff --git a/Linphone/Localizable.xcstrings b/Linphone/Localizable.xcstrings index 9fbfb1302..de219faee 100644 --- a/Linphone/Localizable.xcstrings +++ b/Linphone/Localizable.xcstrings @@ -155,6 +155,12 @@ }, "Interoperable mode" : { + }, + "Invalid QR code!" : { + + }, + "Invalide URI" : { + }, "Linphone" : { @@ -190,6 +196,9 @@ }, "Personnalize your profil mode" : { + }, + "QR code validated!" : { + }, "Register" : { diff --git a/Linphone/UI/Assistant/Fragments/LoginFragment.swift b/Linphone/UI/Assistant/Fragments/LoginFragment.swift index b1ba24389..10c3c9201 100644 --- a/Linphone/UI/Assistant/Fragments/LoginFragment.swift +++ b/Linphone/UI/Assistant/Fragments/LoginFragment.swift @@ -150,32 +150,33 @@ struct LoginFragment: View { } } .padding(.bottom, 10) - - Button(action: { - - }) { - HStack { - Image("qr-code") - .renderingMode(.template) - .resizable() - .foregroundStyle(Color.orange_main_500) - .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.orange_main_500, lineWidth: 1) - ) - .padding(.bottom) + + NavigationLink(destination: { + QrCodeScannerFragment() + }, label: { + HStack { + Image("qr-code") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.orange_main_500) + .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.orange_main_500, lineWidth: 1) + ) + .padding(.bottom) NavigationLink(destination: { ThirdPartySipAccountWarningFragment(accountLoginViewModel: accountLoginViewModel) @@ -231,6 +232,7 @@ struct LoginFragment: View { } } } + .navigationViewStyle(StackNavigationViewStyle()) } } diff --git a/Linphone/UI/Assistant/Fragments/QrCodeScannerFragment.swift b/Linphone/UI/Assistant/Fragments/QrCodeScannerFragment.swift new file mode 100644 index 000000000..03f9fe288 --- /dev/null +++ b/Linphone/UI/Assistant/Fragments/QrCodeScannerFragment.swift @@ -0,0 +1,57 @@ +// +// QrCodeScannerFragment.swift +// Linphone +// +// Created by Benoît Martins on 05/10/2023. +// + +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.configuringSuccessful == "Successful" { + ZStack{ + + }.onAppear { + dismiss() + } + } + } +} + +#Preview { + QrCodeScannerFragment() +} diff --git a/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountLoginFragment.swift b/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountLoginFragment.swift index f072ff1f5..e2a80eff9 100644 --- a/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountLoginFragment.swift +++ b/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountLoginFragment.swift @@ -50,7 +50,7 @@ struct ThirdPartySipAccountLoginFragment: View { .renderingMode(.template) .resizable() .foregroundStyle(Color.gray_main2_500) - .frame(width: 20, height: 20, alignment: .leading) + .frame(width: 25, height: 25, alignment: .leading) .padding(.top, -65) .onTapGesture { withAnimation { diff --git a/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountWarningFragment.swift b/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountWarningFragment.swift index 386f98b57..98b8c6478 100644 --- a/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountWarningFragment.swift +++ b/Linphone/UI/Assistant/Fragments/ThirdPartySipAccountWarningFragment.swift @@ -44,7 +44,7 @@ struct ThirdPartySipAccountWarningFragment: View { .renderingMode(.template) .resizable() .foregroundStyle(Color.gray_main2_500) - .frame(width: 20, height: 20, alignment: .leading) + .frame(width: 25, height: 25, alignment: .leading) .padding(.top, -65) .onTapGesture { withAnimation { @@ -175,6 +175,7 @@ struct ThirdPartySipAccountWarningFragment: View { } } } + .navigationViewStyle(StackNavigationViewStyle()) .navigationBarHidden(true) } } diff --git a/Linphone/UI/Assistant/Viewmodel/QRScanner.swift b/Linphone/UI/Assistant/Viewmodel/QRScanner.swift new file mode 100644 index 000000000..3e9b46419 --- /dev/null +++ b/Linphone/UI/Assistant/Viewmodel/QRScanner.swift @@ -0,0 +1,77 @@ +// +// QRScanner.swift +// Linphone +// +// Created by Benoît Martins on 05/10/2023. +// + +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.count == 0 { + scanResult = "Scan a QR code" + return + } + + // Get the metadata object. + let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject + + 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 + //scanResult = result + do { + try coreContext.mCore.setProvisioninguri(newValue: result) + coreContext.mCore.stop() + try coreContext.mCore.start() + }catch { + + } + + } else { + coreContext.configuringSuccessful = "Invalide URI" + } + } else { + coreContext.configuringSuccessful = "Invalide URI" + } + } + } + } +} diff --git a/Linphone/UI/Main/Fragments/ToastView.swift b/Linphone/UI/Main/Fragments/ToastView.swift new file mode 100644 index 000000000..fff782c09 --- /dev/null +++ b/Linphone/UI/Main/Fragments/ToastView.swift @@ -0,0 +1,70 @@ +// +// ToastView.swift +// Linphone +// +// Created by Benoît Martins on 06/10/2023. +// + +import SwiftUI + +struct ToastView: ViewModifier { + + @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) + + Text(isShowing == "Successful" ? "QR code validated!" : (isShowing == "Failed" ? "Invalid QR code!" : "Invalide URI")) + .multilineTextAlignment(.center) + .foregroundStyle(isShowing == "Successful" ? Color.green_success_500 : Color.red_danger_500) + .default_text_style(styleSize: 15) + .padding(8) + } + .frame(maxWidth: .infinity) + .frame(height: 40) + .background(.white) + .cornerRadius(50) + .overlay( + RoundedRectangle(cornerRadius: 50) + .inset(by: 0.5) + .stroke(isShowing == "Successful" ? Color.green_success_500 : Color.red_danger_500, lineWidth: 1) + ) + .onTapGesture { + isShowing = "" + } + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + isShowing = "" + } + } + } + Spacer() + } + .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(isShowing: isShowing)) + } +} + +//#Preview { +//ToastView() +//} diff --git a/Linphone/UI/Welcome/WelcomeView.swift b/Linphone/UI/Welcome/WelcomeView.swift index ce4052e11..7ec1e7ae9 100644 --- a/Linphone/UI/Welcome/WelcomeView.swift +++ b/Linphone/UI/Welcome/WelcomeView.swift @@ -118,14 +118,14 @@ struct WelcomeView: View{ } if self.isShowPopup { - PopupView(isShowPopup: $isShowPopup, title: Text("Conditions de service"), content: Text("En continuant, vous acceptez ces conditions, \(Text("[notre politique de confidentialité](https://linphone.org/privacy-policy)").underline()) et \(Text("[nos conditions d’utilisation](https://linphone.org/general-terms)").underline())."), titleFirstButton: Text("Deny all"), actionFirstButton: {self.isShowPopup.toggle()}, titleSecondButton: Text("Accept all"), actionSecondButton: {permissionManager.photoLibraryRequestPermission()}) + PopupView(isShowPopup: $isShowPopup, title: Text("Conditions de service"), content: Text("En continuant, vous acceptez ces conditions, \(Text("[notre politique de confidentialité](https://linphone.org/privacy-policy)").underline()) et \(Text("[nos conditions d’utilisation](https://linphone.org/general-terms)").underline())."), titleFirstButton: Text("Deny all"), actionFirstButton: {self.isShowPopup.toggle()}, titleSecondButton: Text("Accept all"), actionSecondButton: {permissionManager.cameraRequestPermission()}) .background(.black.opacity(0.65)) .onTapGesture { self.isShowPopup.toggle() } } } - .onReceive(permissionManager.$photoLibraryPermissionGranted, perform: { (granted) in + .onReceive(permissionManager.$cameraPermissionGranted, perform: { (granted) in if granted { withAnimation { sharedMainViewModel.changeGeneralTerms() diff --git a/Linphone/Utils/QRScannerController.swift b/Linphone/Utils/QRScannerController.swift new file mode 100644 index 000000000..7d37f2cb5 --- /dev/null +++ b/Linphone/Utils/QRScannerController.swift @@ -0,0 +1,64 @@ +// +// QRScannerController.swift +// Linphone +// +// Created by Benoît Martins on 05/10/2023. +// + +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() + } + + } + +}