QR code: Ability to add tunnels with a QR code scan

Signed-off-by: Eric Kuck <eric@bluelinelabs.com>
This commit is contained in:
Eric Kuck 2018-08-21 11:00:41 -05:00 committed by Roopesh Chander
parent 3d4409fc93
commit 3082863fd1
1 changed files with 107 additions and 0 deletions

View File

@ -0,0 +1,107 @@
//
// QRScanViewController.swift
// WireGuard
//
// Created by Eric Kuck on 8/20/18.
// Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All rights reserved.
//
import AVFoundation
import CoreData
import UIKit
protocol QRScanViewControllerDelegate: class {
func didSave(tunnel: Tunnel, qrScanViewController: QRScanViewController)
}
class QRScanViewController: UIViewController {
private var viewContext: NSManagedObjectContext!
private weak var delegate: QRScanViewControllerDelegate?
var captureSession: AVCaptureSession? = AVCaptureSession()
let metadataOutput = AVCaptureMetadataOutput()
var previewLayer: AVCaptureVideoPreviewLayer!
func configure(context: NSManagedObjectContext, delegate: QRScanViewControllerDelegate? = nil) {
viewContext = context
self.delegate = delegate
}
override func viewDidLoad() {
super.viewDidLoad()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video),
let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice),
let captureSession = captureSession,
captureSession.canAddInput(videoInput),
captureSession.canAddOutput(metadataOutput) else {
scanDidEncounterError(title: "Scanning Not Supported", message: "This device does not have the ability to scan QR codes.")
return
}
captureSession.addInput(videoInput)
captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, 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)
if captureSession?.isRunning == false {
captureSession?.startRunning()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if captureSession?.isRunning == true {
captureSession?.stopRunning()
}
}
func scanDidComplete(withCode code: String) {
do {
let tunnel = try Tunnel.fromConfig(code, context: viewContext)
delegate?.didSave(tunnel: tunnel, qrScanViewController: self)
} catch {
scanDidEncounterError(title: "Invalid Code", message: "The scanned code is not a valid WireGuard config file.")
}
}
func scanDidEncounterError(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak self] _ in
self?.navigationController?.popViewController(animated: true)
}))
present(alertController, animated: true)
captureSession = nil
}
}
extension QRScanViewController: AVCaptureMetadataOutputObjectsDelegate {
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
captureSession?.stopRunning()
guard let metadataObject = metadataObjects.first,
let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject,
let stringValue = readableObject.stringValue else {
scanDidEncounterError(title: "Invalid Code", message: "The scanned code could not be read.")
return
}
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
scanDidComplete(withCode: stringValue)
}
}
extension QRScanViewController: Identifyable {}