iOS: request authorization before presenting image-picker

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 <richard.gustavsen@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Timur Pocheptsov 2023-03-24 10:53:11 +01:00
parent b4afba0c34
commit 21a6c86979
4 changed files with 44 additions and 3 deletions

View File

@ -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)

View File

@ -65,6 +65,7 @@ qt_internal_extend_target(QIOSIntegrationPlugin CONDITION NOT TVOS
LIBRARIES
${FWAssetsLibrary}
${FWUniformTypeIdentifiers}
${FWPhotos}
)
add_subdirectory(optional)

View File

@ -39,6 +39,7 @@ private:
bool showImagePickerDialog(QWindow *parent);
bool showNativeDocumentPickerDialog(QWindow *parent);
void showImagePickerDialog_helper(QWindow *parent);
};
QT_END_NAMESPACE

View File

@ -3,6 +3,8 @@
#import <UIKit/UIKit.h>
#import <Photos/Photos.h>
#include <QtCore/qstandardpaths.h>
#include <QtGui/qwindow.h>
#include <QDebug>
@ -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<UIView *>(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<UIView *>(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 arent 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<QWindow> winGuard(parent);
QPointer<QIOSFileDialog> 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;
}