From 21a6c86979064b70b11b14b97c8f828d08e29781 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 24 Mar 2023 10:53:11 +0100 Subject: [PATCH] iOS: request authorization before presenting image-picker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now, image picker view is shown first (it does not require access to photos, since it's essentially Photos app getting access to photos ...). Then, we use AssetsLibrary to get asset for an url (using ALAssetsLibrarie's -assetForURL method). This may trigger a permission-related alert, asking to: a. Select more photos or ... b. Allow access to all photos or ... c. Deny access. Showing this alert _after_ picker has selected an image makes little sense (and probably was never intended this way anyway). Instead, we now use Photos.framework to check the authorization and, if needed, we request an authorization (when the current status is 'Nondetermined'). If authorization is 'Granted' as a result, we show picker view and proceed as normal/before. Pick-to: 6.5 Task-number: QTBUG-109120 Change-Id: I0acfd7b0476346360d75a5e37f5845aaf2d6e3e0 Reviewed-by: Richard Moe Gustavsen Reviewed-by: Tor Arne Vestbø --- cmake/QtFrameworkHelpers.cmake | 1 + src/plugins/platforms/ios/CMakeLists.txt | 1 + src/plugins/platforms/ios/qiosfiledialog.h | 1 + src/plugins/platforms/ios/qiosfiledialog.mm | 44 +++++++++++++++++++-- 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/cmake/QtFrameworkHelpers.cmake b/cmake/QtFrameworkHelpers.cmake index 1eed9df379..e8db635963 100644 --- a/cmake/QtFrameworkHelpers.cmake +++ b/cmake/QtFrameworkHelpers.cmake @@ -6,6 +6,7 @@ macro(qt_find_apple_system_frameworks) qt_internal_find_apple_system_framework(FWAppKit AppKit) qt_internal_find_apple_system_framework(FWCFNetwork CFNetwork) qt_internal_find_apple_system_framework(FWAssetsLibrary AssetsLibrary) + qt_internal_find_apple_system_framework(FWPhotos Photos) qt_internal_find_apple_system_framework(FWAudioToolbox AudioToolbox) qt_internal_find_apple_system_framework(FWApplicationServices ApplicationServices) qt_internal_find_apple_system_framework(FWCarbon Carbon) diff --git a/src/plugins/platforms/ios/CMakeLists.txt b/src/plugins/platforms/ios/CMakeLists.txt index e0473b3ce4..3745783248 100644 --- a/src/plugins/platforms/ios/CMakeLists.txt +++ b/src/plugins/platforms/ios/CMakeLists.txt @@ -65,6 +65,7 @@ qt_internal_extend_target(QIOSIntegrationPlugin CONDITION NOT TVOS LIBRARIES ${FWAssetsLibrary} ${FWUniformTypeIdentifiers} + ${FWPhotos} ) add_subdirectory(optional) diff --git a/src/plugins/platforms/ios/qiosfiledialog.h b/src/plugins/platforms/ios/qiosfiledialog.h index 12a3af7181..f00c154c03 100644 --- a/src/plugins/platforms/ios/qiosfiledialog.h +++ b/src/plugins/platforms/ios/qiosfiledialog.h @@ -39,6 +39,7 @@ private: bool showImagePickerDialog(QWindow *parent); bool showNativeDocumentPickerDialog(QWindow *parent); + void showImagePickerDialog_helper(QWindow *parent); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosfiledialog.mm b/src/plugins/platforms/ios/qiosfiledialog.mm index 526c040335..5dea3c6446 100644 --- a/src/plugins/platforms/ios/qiosfiledialog.mm +++ b/src/plugins/platforms/ios/qiosfiledialog.mm @@ -3,6 +3,8 @@ #import +#import + #include #include #include @@ -53,6 +55,13 @@ bool QIOSFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality window return false; } +void QIOSFileDialog::showImagePickerDialog_helper(QWindow *parent) +{ + UIWindow *window = parent ? reinterpret_cast(parent->winId()).window + : qt_apple_sharedApplication().keyWindow; + [window.rootViewController presentViewController:m_viewController animated:YES completion:nil]; +} + bool QIOSFileDialog::showImagePickerDialog(QWindow *parent) { if (!m_viewController) { @@ -71,9 +80,38 @@ bool QIOSFileDialog::showImagePickerDialog(QWindow *parent) return false; } - UIWindow *window = parent ? reinterpret_cast(parent->winId()).window - : qt_apple_sharedApplication().keyWindow; - [window.rootViewController presentViewController:m_viewController animated:YES completion:nil]; + // "Old style" authorization (deprecated, but we have to work with AssetsLibrary anyway). + // + // From the documentation: + // "The authorizationStatus and requestAuthorization: methods aren’t compatible with the + // limited library and return PHAuthorizationStatusAuthorized when the user authorizes your + // app for limited access only." + // + // This is good enough for us. + + const auto authStatus = [PHPhotoLibrary authorizationStatus]; + if (authStatus == PHAuthorizationStatusAuthorized) { + showImagePickerDialog_helper(parent); + } else if (authStatus == PHAuthorizationStatusNotDetermined) { + QPointer winGuard(parent); + QPointer thisGuard(this); + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (status == PHAuthorizationStatusAuthorized) { + if (thisGuard && winGuard) + thisGuard->showImagePickerDialog_helper(winGuard); + + } else if (thisGuard) { + emit thisGuard->reject(); + } + }); + }]; + } else { + // Treat 'Limited' (we don't know how to deal with anyway) and 'Denied' as errors. + // FIXME: logging category? + qWarning() << "QIOSFileDialog: insufficient permission, cannot pick images"; + return false; + } return true; }