// /* * Copyright (c) 2022 BWI GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import SwiftUI import UIKit import AVFoundation struct AuthenticationServerSelectionQRCodeScanner: View { @Environment(\.presentationMode) var presentationMode @Binding var qrCode: String @State var scanCompleted = false var body: some View { NavigationView { ScannerView(qrCode: $qrCode, scanCompleted: $scanCompleted) .navigationTitle(BWIL10n.authenticationServerSelectionScanCodeButtonTitle) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button(VectorL10n.close) { presentationMode.wrappedValue.dismiss() } } } } .onChange(of: scanCompleted) { newValue in if newValue { presentationMode.wrappedValue.dismiss() } } } } struct ScannerView: UIViewControllerRepresentable { @Binding var qrCode: String @Binding var scanCompleted: Bool class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate { var parent: ScannerView init(_ parent: ScannerView) { self.parent = parent } func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { if let metadataObject = metadataObjects.first { guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return } guard let stringValue = readableObject.stringValue else { return } AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate)) DispatchQueue.main.async { self.parent.qrCode = stringValue self.parent.scanCompleted = true } } } } func makeCoordinator() -> Coordinator { Coordinator(self) } func makeUIViewController(context: Context) -> some ScannerViewController { let controller = ScannerViewController() controller.delegate = context.coordinator return controller } func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { } } class ScannerViewController: UIViewController { var captureSession: AVCaptureSession? var previewLayer: AVCaptureVideoPreviewLayer! let metadataOutput = AVCaptureMetadataOutput() weak var delegate: AVCaptureMetadataOutputObjectsDelegate? override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = UIColor.black captureSession = AVCaptureSession() guard let videoCaptureDevice = AVCaptureDevice.default(for: .video), let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice) else { captureSession = nil return } if let captureSession = captureSession { guard captureSession.canAddInput(videoInput) && captureSession.canAddOutput(metadataOutput) else { self.captureSession = nil return } captureSession.addInput(videoInput) captureSession.addOutput(metadataOutput) metadataOutput.setMetadataObjectsDelegate(delegate, queue: .main) metadataOutput.metadataObjectTypes = [.qr] previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) previewLayer.frame = view.layer.bounds previewLayer.videoGravity = .resizeAspectFill view.layer.addSublayer(previewLayer) } } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) let dispatchQueue = DispatchQueue(label: "AVCapturesession.startRunning", qos: .background) dispatchQueue.async{ if (self.captureSession?.isRunning == false) { self.captureSession?.startRunning() } } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) let dispatchQueue = DispatchQueue(label: "AVCapturesession.stopRunning", qos: .background) dispatchQueue.async{ if (self.captureSession?.isRunning == true) { self.captureSession?.stopRunning() } } } }