From df1b4fda5707ef52d922b99cbcb5b2cfc0bf798b Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Wed, 7 Apr 2021 16:47:39 +1000 Subject: [PATCH 1/2] try to handle limited photo permission --- .../ConversationVC+Interaction.swift | 33 ++++++++++++++++--- Session/Meta/Session-Info.plist | 4 ++- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index a18f369f3..0560a7652 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -1,5 +1,6 @@ import CoreServices import Photos +import PhotosUI extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuActionDelegate, ScrollToBottomButtonDelegate, SendMediaNavDelegate, UIDocumentPickerDelegate, AttachmentApprovalViewControllerDelegate, GifPickerViewControllerDelegate, @@ -727,7 +728,34 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc } func requestLibraryPermissionIfNeeded() -> Bool { - switch PHPhotoLibrary.authorizationStatus() { + let authorizationStatus: PHAuthorizationStatus + if #available(iOS 14, *) { + authorizationStatus = PHPhotoLibrary.authorizationStatus(for: .readWrite) + if (authorizationStatus == .notDetermined) { + PHPhotoLibrary.requestAuthorization(for: .readWrite) { newStatus in + switch newStatus { + case .authorized: + print("RYAN: Full access.") + break + case .limited: + print("RYAN: Limited access.") + break + case .denied: + break + default: + break + } + } + return false + } + } else { + authorizationStatus = PHPhotoLibrary.authorizationStatus() + if (authorizationStatus == .notDetermined) { + PHPhotoLibrary.requestAuthorization { _ in } + return false + } + } + switch authorizationStatus { case .authorized, .limited: return true case .denied, .restricted: let modal = PermissionMissingModal(permission: "library") { } @@ -735,9 +763,6 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc modal.modalTransitionStyle = .crossDissolve present(modal, animated: true, completion: nil) return false - case .notDetermined: - PHPhotoLibrary.requestAuthorization { _ in } - return false default: return false } } diff --git a/Session/Meta/Session-Info.plist b/Session/Meta/Session-Info.plist index e05788065..e276c43a5 100644 --- a/Session/Meta/Session-Info.plist +++ b/Session/Meta/Session-Info.plist @@ -2,6 +2,8 @@ + PHPhotoLibraryPreventAutomaticLimitedAccessAlert + BuildDetails CarthageVersion @@ -59,7 +61,7 @@ NSContactsUsageDescription Signal uses your contacts to find users you know. We do not store your contacts on the server. NSFaceIDUsageDescription - Session's Screen Lock feature uses Face ID. + Session's Screen Lock feature uses Face ID. NSMicrophoneUsageDescription Session needs access to your microphone to record media. NSPhotoLibraryAddUsageDescription From cf148fe8459d64f08594c07a087a711b95af3f2a Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Thu, 8 Apr 2021 13:36:20 +1000 Subject: [PATCH 2/2] handle access to selected photos & optimise the process of request photo library permission --- .../ConversationVC+Interaction.swift | 55 ++++++++++--------- Session/Shared/OWSScreenLockUI.m | 4 ++ SessionMessagingKit/Utilities/Environment.h | 2 + SessionMessagingKit/Utilities/Environment.m | 1 + 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index 0560a7652..fa0ffdb84 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -92,12 +92,14 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc } func handleLibraryButtonTapped() { - // FIXME: We're not yet handling the case where the user only gives access to selected photos/videos - guard requestLibraryPermissionIfNeeded() else { return } - let sendMediaNavController = SendMediaNavigationController.showingMediaLibraryFirst() - sendMediaNavController.sendMediaNavDelegate = self - sendMediaNavController.modalPresentationStyle = .fullScreen - present(sendMediaNavController, animated: true, completion: nil) + requestLibraryPermissionIfNeeded { [weak self] in + DispatchQueue.main.async { + let sendMediaNavController = SendMediaNavigationController.showingMediaLibraryFirst() + sendMediaNavController.sendMediaNavDelegate = self + sendMediaNavController.modalPresentationStyle = .fullScreen + self?.present(sendMediaNavController, animated: true, completion: nil) + } + } } func handleGIFButtonTapped() { @@ -727,43 +729,46 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc } } - func requestLibraryPermissionIfNeeded() -> Bool { + func requestLibraryPermissionIfNeeded(handler: @escaping () -> Void) { let authorizationStatus: PHAuthorizationStatus if #available(iOS 14, *) { authorizationStatus = PHPhotoLibrary.authorizationStatus(for: .readWrite) if (authorizationStatus == .notDetermined) { - PHPhotoLibrary.requestAuthorization(for: .readWrite) { newStatus in - switch newStatus { - case .authorized: - print("RYAN: Full access.") - break - case .limited: - print("RYAN: Limited access.") - break - case .denied: - break - default: - break + // When the user chooses to select photos (which is the .limit status), + // the PHPhotoUI will present the picker view on the top of the front view. + // Since we have this ScreenLockUI showing when we request premissions, + // the picker view will be presented on the top of ScreenLockUI. + // However, the ScreenLockUI will dismiss with the permission request alert view, + // the picker view then will dissmiss, too. The selection process cannot be finished + // this way. So we add a flag (isRequestingPermission) to prevent the ScreenLockUI + // from showing when we request the photo library permission. + Environment.shared.isRequestingPermission = true + PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in + Environment.shared.isRequestingPermission = false + if ([PHAuthorizationStatus.authorized, PHAuthorizationStatus.limited].contains(status)) { + handler() } } - return false } } else { authorizationStatus = PHPhotoLibrary.authorizationStatus() if (authorizationStatus == .notDetermined) { - PHPhotoLibrary.requestAuthorization { _ in } - return false + PHPhotoLibrary.requestAuthorization { status in + if (status == .authorized) { + handler() + } + } } } switch authorizationStatus { - case .authorized, .limited: return true + case .authorized, .limited: + handler() case .denied, .restricted: let modal = PermissionMissingModal(permission: "library") { } modal.modalPresentationStyle = .overFullScreen modal.modalTransitionStyle = .crossDissolve present(modal, animated: true, completion: nil) - return false - default: return false + default: return } } diff --git a/Session/Shared/OWSScreenLockUI.m b/Session/Shared/OWSScreenLockUI.m index 65bf6f28e..e7cb44121 100644 --- a/Session/Shared/OWSScreenLockUI.m +++ b/Session/Shared/OWSScreenLockUI.m @@ -347,6 +347,10 @@ NS_ASSUME_NONNULL_BEGIN OWSLogVerbose(@"desiredUIState: none 3."); return ScreenLockUIStateNone; } + + if (Environment.shared.isRequestingPermission) { + return ScreenLockUIStateNone; + } if (Environment.shared.preferences.screenSecurityIsEnabled) { OWSLogVerbose(@"desiredUIState: screen protection 4."); diff --git a/SessionMessagingKit/Utilities/Environment.h b/SessionMessagingKit/Utilities/Environment.h index 64d7ddc61..edbd77376 100644 --- a/SessionMessagingKit/Utilities/Environment.h +++ b/SessionMessagingKit/Utilities/Environment.h @@ -29,6 +29,8 @@ @property (nonatomic, readonly) OWSPreferences *preferences; @property (nonatomic, readonly) OWSSounds *sounds; @property (nonatomic, readonly) OWSWindowManager *windowManager; +// We don't want to cover the window when we request the photo library permission +@property (nonatomic, readwrite) BOOL isRequestingPermission; @property (class, nonatomic) Environment *shared; diff --git a/SessionMessagingKit/Utilities/Environment.m b/SessionMessagingKit/Utilities/Environment.m index e4abb4602..81a39ccf2 100644 --- a/SessionMessagingKit/Utilities/Environment.m +++ b/SessionMessagingKit/Utilities/Environment.m @@ -58,6 +58,7 @@ static Environment *sharedEnvironment = nil; _proximityMonitoringManager = proximityMonitoringManager; _sounds = sounds; _windowManager = windowManager; + _isRequestingPermission = false; return self; }