瀏覽代碼

Merge pull request #1225 from deltachat/camera_permission_handling

Camera permission handling
cyBerta 4 年之前
父節點
當前提交
84a98b6204

+ 26 - 1
deltachat-ios/Chat/ChatViewController.swift

@@ -1049,7 +1049,32 @@ class ChatViewController: UITableViewController {
     }
 
     private func showCameraViewController() {
-        mediaPicker?.showCamera()
+        if AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
+            self.mediaPicker?.showCamera()
+        } else {
+            AVCaptureDevice.requestAccess(for: .video, completionHandler: {  [weak self] (granted: Bool) in
+                guard let self = self else { return }
+                if granted {
+                    self.mediaPicker?.showCamera()
+                } else {
+                    self.showCameraPermissionAlert()
+                }
+            })
+        }
+    }
+    
+    private func showCameraPermissionAlert() {
+        DispatchQueue.main.async { [weak self] in
+            let alert = UIAlertController(title: String.localized("perm_required_title"),
+                                          message: String.localized("perm_ios_explain_access_to_camera_denied"),
+                                          preferredStyle: .alert)
+            if let appSettings = URL(string: UIApplication.openSettingsURLString) {
+                alert.addAction(UIAlertAction(title: String.localized("open_settings"), style: .default, handler: { _ in
+                        UIApplication.shared.open(appSettings, options: [:], completionHandler: nil)}))
+                alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .destructive, handler: nil))
+            }
+            self?.present(alert, animated: true, completion: nil)
+        }
     }
 
     private func showPhotoVideoLibrary(delegate: MediaPickerDelegate) {

+ 75 - 29
deltachat-ios/Controller/QrCodeReaderController.swift

@@ -1,11 +1,15 @@
 import AVFoundation
 import UIKit
+import DcCore
 
 class QrCodeReaderController: UIViewController {
 
     weak var delegate: QrCodeReaderDelegate?
 
     private let captureSession = AVCaptureSession()
+    
+    private var infoLabelBottomConstraint: NSLayoutConstraint?
+    private var infoLabelCenterConstraint: NSLayoutConstraint?
 
     private lazy var videoPreviewLayer: AVCaptureVideoPreviewLayer = {
         let videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
@@ -36,34 +40,24 @@ class QrCodeReaderController: UIViewController {
         super.viewDidLoad()
         self.edgesForExtendedLayout = []
         title = String.localized("qrscan_title")
-
-        guard let captureDevice = AVCaptureDevice.DiscoverySession.init(
-            deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera],
-            mediaType: .video,
-            position: .back).devices.first else {
-            print("Failed to get the camera device")
-            return
-        }
-
-        do {
-            let input = try AVCaptureDeviceInput(device: captureDevice)
-            captureSession.addInput(input)
-
-            let captureMetadataOutput = AVCaptureMetadataOutput()
-            captureSession.addOutput(captureMetadataOutput)
-
-            captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
-            captureMetadataOutput.metadataObjectTypes = supportedCodeTypes
-        } catch {
-            // If any error occurs, simply print it out and don't continue any more.
-            logger.error("failed to setup QR Code Scanner: \(error)")
-            return
+        self.setupInfoLabel()
+        if AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
+            self.setupQRCodeScanner()
+        } else {
+            AVCaptureDevice.requestAccess(for: .video, completionHandler: {  [weak self] (granted: Bool) in
+                guard let self = self else { return }
+                if granted {
+                    self.setupQRCodeScanner()
+                } else {
+                    self.showCameraWarning()
+                    self.showPermissionAlert()
+                }
+            })
         }
-
-        setupSubviews()
     }
 
     override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
         startSession()
     }
 
@@ -82,18 +76,70 @@ class QrCodeReaderController: UIViewController {
     }
 
     // MARK: - setup
-    private func setupSubviews() {
+    
+    private func setupQRCodeScanner() {
+        guard let captureDevice = AVCaptureDevice.DiscoverySession.init(
+            deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera],
+            mediaType: .video,
+            position: .back).devices.first else {
+            self.showCameraWarning()
+            return
+        }
+        do {
+            let input = try AVCaptureDeviceInput(device: captureDevice)
+            self.captureSession.addInput(input)
+
+            let captureMetadataOutput = AVCaptureMetadataOutput()
+            self.captureSession.addOutput(captureMetadataOutput)
+
+            captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
+            captureMetadataOutput.metadataObjectTypes = self.supportedCodeTypes
+        } catch {
+            // If any error occurs, simply print it out and don't continue any more.
+            self.showCameraWarning()
+            return
+        }
         view.layer.addSublayer(videoPreviewLayer)
         videoPreviewLayer.frame = view.layer.bounds
+        view.bringSubviewToFront(infoLabel)
+    }
+    
+    private func setupInfoLabel() {
         view.addSubview(infoLabel)
         infoLabel.translatesAutoresizingMaskIntoConstraints = false
-
-        infoLabel.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
+        infoLabelBottomConstraint = infoLabel.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10)
+        infoLabelCenterConstraint = infoLabel.constraintCenterYTo(view)
+        infoLabelBottomConstraint?.isActive = true
         infoLabel.constraintAlignLeadingTo(view, paddingLeading: 5).isActive = true
         infoLabel.constraintAlignTrailingTo(view, paddingTrailing: 5).isActive = true
-        view.bringSubviewToFront(infoLabel)
     }
-
+    
+    private func showCameraWarning() {
+        DispatchQueue.main.async { [weak self] in
+            guard let self = self else { return }
+            let text = String.localized("chat_camera_unavailable")
+            logger.error(text)
+            self.infoLabel.textColor = DcColors.defaultTextColor
+            self.infoLabel.text = text
+            self.infoLabelBottomConstraint?.isActive = false
+            self.infoLabelCenterConstraint?.isActive = true
+        }
+    }
+    
+    private func showPermissionAlert() {
+        DispatchQueue.main.async { [weak self] in
+            let alert = UIAlertController(title: String.localized("perm_required_title"),
+                                          message: String.localized("perm_ios_explain_access_to_camera_denied"),
+                                          preferredStyle: .alert)
+            if let appSettings = URL(string: UIApplication.openSettingsURLString) {
+                alert.addAction(UIAlertAction(title: String.localized("open_settings"), style: .default, handler: { _ in
+                        UIApplication.shared.open(appSettings, options: [:], completionHandler: nil)}))
+                alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .destructive, handler: nil))
+            }
+            self?.present(alert, animated: true, completion: nil)
+        }
+    }
+    
     private func updateVideoOrientation() {
 
         guard let connection = videoPreviewLayer.connection else {

+ 1 - 1
deltachat-ios/Controller/QrPageController.swift

@@ -89,7 +89,7 @@ class QrPageController: UIPageViewController, ProgressAlertHandler {
         } else {
             let qrCodeReaderController = makeQRReader()
             self.qrCodeReaderController = qrCodeReaderController
-            setViewControllers([qrCodeReaderController], direction: .forward, animated: true, completion: nil)
+            setViewControllers([qrCodeReaderController], direction: .forward, animated: false, completion: nil)
         }
     }
 

+ 2 - 0
deltachat-ios/en.lproj/Localizable.strings

@@ -770,3 +770,5 @@
 "login_error_no_internet_connection" = "No internet connection, can\'t log in to your server.";
 "share_account_not_configured" = "Account is not configured.";
 "cannot_play_unsupported_file_type" = "The audio file cannot be played.";
+"perm_ios_explain_access_to_camera_denied" = "To take photos, capture videos or use the QR-Code scanner, open the system settings and enable \"Camera\".";
+"open_settings" = "Open Settings";

+ 3 - 0
scripts/untranslated.xml

@@ -9,4 +9,7 @@
     <string name="login_error_no_internet_connection">No internet connection, can\'t log in to your server.</string>
     <string name="share_account_not_configured">Account is not configured.</string>
     <string name="cannot_play_unsupported_file_type">The audio file cannot be played.</string>
+    
+    <string name="perm_ios_explain_access_to_camera_denied">To take photos, capture videos or use the QR-Code scanner, open the system settings and enable \"Camera\".</string>
+    <string name="open_settings">Open Settings</string>
 </resources>