Sfoglia il codice sorgente

Merge pull request #1964 from nextcloud/fix/save-photo-auth

Fix photo library access + passcode window
Marino Faggiana 3 anni fa
parent
commit
c318c1f217

+ 46 - 46
iOSClient/AppDelegate.swift

@@ -188,12 +188,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     // L' applicazione entrerà in primo piano (attivo sempre)
     func applicationDidBecomeActive(_ application: UIApplication) {
-        
-        // Privacy
-        hidePrivacyProtectionWindow()
-        
-        NCSettingsBundleHelper.setVersionAndBuildNumber()
-        
+
+        if !NCAskAuthorization.shared.isRequesting {
+            // Privacy
+            hidePrivacyProtectionWindow()
+        }
+
         NCSettingsBundleHelper.setVersionAndBuildNumber()
 
         if account == "" { return }
@@ -245,9 +245,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         if account == "" { return }
 
-        // Privacy
-        showPrivacyProtectionWindow()
-                
+        if CCUtility.getPrivacyScreenEnabled() {
+            // Privacy
+            showPrivacyProtectionWindow()
+        }
+
         // Clear operation queue
         NCOperationQueue.shared.cancelAllQueue()
         // Clear download
@@ -266,20 +268,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     func applicationDidEnterBackground(_ application: UIApplication) {
 
         if account == "" { return }
-        
+
         // STOP TIMER UPLOAD PROCESS
         if NCUtility.shared.isSimulator() {
             networkingProcessUpload?.stopTimer()
         }
-                
+
         if #available(iOS 13.0, *) {
             scheduleAppRefresh()
             scheduleBackgroundProcessing()
         }
-        
+
         // Passcode
         presentPasscode { }
-        
+
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationDidEnterBackground)
     }
 
@@ -297,7 +299,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         if account == "" { return }
 
         NCCommunicationCommon.shared.writeLog("initialize Main")
-                
+
         // Registeration push notification
         NCPushNotification.shared().pushNotification()
 
@@ -655,17 +657,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     // MARK: - Account Request
 
     func accountRequestChangeAccount(account: String) {
-
         changeAccount(account)
     }
     
     func requestAccount() {
-              
+
         if isPasscodePresented() { return }
         if !CCUtility.getAccountRequest() { return }
-        
+
         let accounts = NCManageDatabase.shared.getAllAccount()
-        
+
         if accounts.count > 1 {
             
             if let vcAccountRequest = UIStoryboard(name: "NCAccountRequest", bundle: nil).instantiateInitialViewController() as? NCAccountRequest {
@@ -692,30 +693,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     }
 
     // MARK: - Passcode
-    
-    func presentPasscode(completion: @escaping ()->()) {
+
+    func presentPasscode(completion: @escaping () -> ()) {
 
         let laContext = LAContext()
         var error: NSError?
 
-        defer {
-            self.requestAccount()
-        }
-
-        guard !account.isEmpty, CCUtility.isPasscodeAtStartEnabled() else { return }
-        
-        // If activated hide the privacy protection
-        hidePrivacyProtectionWindow()
+        defer { self.requestAccount() }
 
-        // Dismiss present window?.rootViewController? [ONLY PASSCODE]
         let presentedViewController = window?.rootViewController?.presentedViewController
-        if presentedViewController is NCLoginNavigationController {
-            return
-        } else {
-            presentedViewController?.dismiss(animated: false)
-        }
+        guard !account.isEmpty, CCUtility.isPasscodeAtStartEnabled(), !(presentedViewController is NCLoginNavigationController) else { return }
+
+        // Make sure we have a privacy window (in case it's not enabled)
+        showPrivacyProtectionWindow()
 
-        let passcodeViewController = TOPasscodeViewController.init(passcodeType: .sixDigits, allowCancel: false)
+        let passcodeViewController = TOPasscodeViewController(passcodeType: .sixDigits, allowCancel: false)
         passcodeViewController.delegate = self
         passcodeViewController.keypadButtonShowLettering = false
         if CCUtility.getEnableTouchFaceID() && laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
@@ -729,8 +721,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                 passcodeViewController.automaticallyPromptForBiometricValidation = false
             }
         }
-        
-        window?.rootViewController?.present(passcodeViewController, animated: true, completion: {
+
+        // show passcode on top of privacy window
+        privacyProtectionWindow?.rootViewController?.present(passcodeViewController, animated: true, completion: {
             completion()
         })
     }
@@ -761,6 +754,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     func didInputCorrectPasscode(in passcodeViewController: TOPasscodeViewController) {
         DispatchQueue.main.async {
             passcodeViewController.dismiss(animated: true) {
+                self.hidePrivacyProtectionWindow()
                 self.requestAccount()
             }
         }
@@ -773,24 +767,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     // MARK: - Privacy Protection
        
     private func showPrivacyProtectionWindow() {
-        
-        guard CCUtility.getPrivacyScreenEnabled() else { return }
-        
+        guard privacyProtectionWindow == nil else {
+            privacyProtectionWindow?.isHidden = false
+            return
+        }
+
         privacyProtectionWindow = UIWindow(frame: UIScreen.main.bounds)
-          
+
         let storyboard = UIStoryboard(name: "LaunchScreen", bundle: nil)
         let initialViewController = storyboard.instantiateInitialViewController()
 
         self.privacyProtectionWindow?.rootViewController = initialViewController
-        
+
         privacyProtectionWindow?.windowLevel = .alert + 1
         privacyProtectionWindow?.makeKeyAndVisible()
     }
 
-    private func hidePrivacyProtectionWindow() {
-        
-        privacyProtectionWindow?.isHidden = true
-        privacyProtectionWindow = nil
+    func hidePrivacyProtectionWindow() {
+        guard !(privacyProtectionWindow?.rootViewController?.presentedViewController is TOPasscodeViewController) else { return }
+        UIWindow.animate(withDuration: 0.25) {
+            self.privacyProtectionWindow?.alpha = 0
+        } completion: { _ in
+            self.privacyProtectionWindow?.isHidden = true
+            self.privacyProtectionWindow = nil
+        }
     }
     
     // MARK: - Open URL

+ 2 - 0
iOSClient/Brand/iOSClient.plist

@@ -85,6 +85,8 @@
 		<string>Inconsolata-ExtraBold.ttf</string>
 		<string>Inconsolata-Black.ttf</string>
 	</array>
+	<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
+	<true/>
 	<key>UIBackgroundModes</key>
 	<array>
 		<string>audio</string>

+ 7 - 16
iOSClient/Main/NCFunctionCenter.swift

@@ -306,27 +306,18 @@ import SVGKit
     func saveAlbum(metadata: tableMetadata) {
 
         let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
-        let status = PHPhotoLibrary.authorizationStatus()
 
-        if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && status == PHAuthorizationStatus.authorized {
-
-            if let image = UIImage(contentsOfFile: fileNamePath) {
-                UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveAlbum(_:didFinishSavingWithError:contextInfo:)), nil)
-            } else {
-                NCContentPresenter.shared.messageNotification("_save_selected_files_", description: "_file_not_saved_cameraroll_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorFileNotSaved)
+        NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: appDelegate.mainTabBar?.window?.rootViewController) { hasPermission in
+            guard hasPermission else {
+                return NCContentPresenter.shared.messageNotification("_access_photo_not_enabled_", description: "_access_photo_not_enabled_msg_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorFileNotSaved)
             }
-
-        } else if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue && status == PHAuthorizationStatus.authorized {
-
-            if UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(fileNamePath) {
-                UISaveVideoAtPathToSavedPhotosAlbum(fileNamePath, self, #selector(saveAlbum(_:didFinishSavingWithError:contextInfo:)), nil)
+            if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue, let image = UIImage(contentsOfFile: fileNamePath) {
+                UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.saveAlbum(_:didFinishSavingWithError:contextInfo:)), nil)
+            } else if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue, UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(fileNamePath) {
+                UISaveVideoAtPathToSavedPhotosAlbum(fileNamePath, self, #selector(self.saveAlbum(_:didFinishSavingWithError:contextInfo:)), nil)
             } else {
                 NCContentPresenter.shared.messageNotification("_save_selected_files_", description: "_file_not_saved_cameraroll_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorFileNotSaved)
             }
-
-        } else if status != PHAuthorizationStatus.authorized {
-
-            NCContentPresenter.shared.messageNotification("_access_photo_not_enabled_", description: "_access_photo_not_enabled_msg_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorFileNotSaved)
         }
     }
 

+ 1 - 14
iOSClient/Settings/CCManageAutoUpload.m

@@ -219,20 +219,7 @@
     [super viewWillAppear:animated];
     
     appDelegate.activeViewController = self;
-    
-    // Request permission for camera roll access
-    [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
-        switch (status) {
-            case PHAuthorizationStatusRestricted:
-                NSLog(@"[LOG] user can't grant access to camera roll");
-                break;
-            case PHAuthorizationStatusDenied:
-                NSLog(@"[LOG] user denied access to camera roll");
-                break;
-            default:
-                break;
-        }
-    }];
+    [[NCAskAuthorization shared] askAuthorizationPhotoLibraryWithViewController:self completion:^(BOOL status) { }];
 }
 
 - (void)initialize

+ 8 - 1
iOSClient/Utility/NCAskAuthorization.swift

@@ -29,6 +29,8 @@ class NCAskAuthorization: NSObject {
         return instance
     }()
 
+    private(set) var isRequesting = false
+
     func askAuthorizationAudioRecord(viewController: UIViewController?, completion: @escaping (_ hasPermission: Bool) -> Void) {
 
         switch AVAudioSession.sharedInstance().recordPermission {
@@ -65,7 +67,7 @@ class NCAskAuthorization: NSObject {
         }
     }
 
-    func askAuthorizationPhotoLibrary(viewController: UIViewController?, completion: @escaping (_ hasPermission: Bool) -> Void) {
+    @objc func askAuthorizationPhotoLibrary(viewController: UIViewController?, completion: @escaping (_ hasPermission: Bool) -> Void) {
 
         switch PHPhotoLibrary.authorizationStatus() {
         case PHAuthorizationStatus.authorized:
@@ -85,7 +87,12 @@ class NCAskAuthorization: NSObject {
             }
             break
         case PHAuthorizationStatus.notDetermined:
+            isRequesting = true
             PHPhotoLibrary.requestAuthorization { allowed in
+                self.isRequesting = false
+                DispatchQueue.main.async {
+                    (UIApplication.shared.delegate as? AppDelegate)?.hidePrivacyProtectionWindow()
+                }
                 DispatchQueue.main.async {
                     if allowed == PHAuthorizationStatus.authorized {
                         completion(true)