diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index e474aa9b2..0c0e86e58 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -574,6 +574,7 @@ endif () if (IOS) target_link_libraries( MerginMaps PUBLIC AppleFrameworks::CoreLocation AppleFrameworks::CoreHaptics + AppleFrameworks::PhotosUI ) # TODO is this needed? this change requires cmake 3.28+ # qt_add_ios_ffmpeg_libraries(MerginMaps) # Qt Multimedia diff --git a/app/ios/iosimagepicker.mm b/app/ios/iosimagepicker.mm index 37e7217cf..3baa6e060 100644 --- a/app/ios/iosimagepicker.mm +++ b/app/ios/iosimagepicker.mm @@ -16,7 +16,6 @@ #include #include #include -#include #include #include "iosinterface.h" diff --git a/app/ios/iosinterface.mm b/app/ios/iosinterface.mm index 7a8e3cbf1..8b3894cd4 100644 --- a/app/ios/iosinterface.mm +++ b/app/ios/iosinterface.mm @@ -18,7 +18,6 @@ #import #import "ios/iosinterface.h" #include "iosviewdelegate.h" -#include "inpututils.h" #import #include "position/positionkit.h" #include "compass.h" @@ -177,7 +176,26 @@ +( void )showImagePicker:( int )sourceType : ( IOSImagePicker * )handler UIWindow *rootWindow = app.windows[0]; UIViewController *rootViewController = rootWindow.rootViewController; - if ( ![UIImagePickerController isSourceTypeAvailable:( UIImagePickerControllerSourceType ) sourceType] ) + bool isCamera = ( UIImagePickerControllerSourceType ) sourceType == UIImagePickerControllerSourceTypeCamera; + + if ( !isCamera ) + { + // Gallery: use PHPickerViewController + PHPickerConfiguration *config = [[PHPickerConfiguration alloc] init]; + config.filter = [PHPickerFilter imagesFilter]; + config.selectionLimit = 1; + + PHPickerViewController *picker = [[PHPickerViewController alloc] initWithConfiguration:config]; + static IOSGalleryPickerDelegate *galleryDelegate = nullptr; + galleryDelegate = [[IOSGalleryPickerDelegate alloc] initWithHandler:handler]; + picker.delegate = galleryDelegate; + + [rootViewController presentViewController:picker animated:YES completion:nil]; + return; + } + + // Camera: use UIImagePickerController + if ( ![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] ) { NSString *alertTitle = @"Image picker"; NSString *alertMessage = @"The functionality is not available"; @@ -188,7 +206,7 @@ +( void )showImagePicker:( int )sourceType : ( IOSImagePicker * )handler preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *actionOk = [UIAlertAction actionWithTitle:alertOkButtonText style:UIAlertActionStyleDefault - handler:nil]; //You can use a block here to handle a press on this button + handler:nil]; [alertController addAction:actionOk]; [rootViewController presentViewController:alertController animated:YES completion:nil]; } @@ -196,7 +214,7 @@ +( void )showImagePicker:( int )sourceType : ( IOSImagePicker * )handler { UIImagePickerController *picker = [[UIImagePickerController alloc] init]; imagePickerController = picker; - picker.sourceType = ( UIImagePickerControllerSourceType ) sourceType; + picker.sourceType = UIImagePickerControllerSourceTypeCamera; static IOSViewDelegate *delegate = nullptr; delegate = [[IOSViewDelegate alloc] initWithHandler:handler]; @@ -218,32 +236,13 @@ +( void )showImagePicker:( int )sourceType : ( IOSImagePicker * )handler delegate->processingPicture = YES; NSString *imagePath = generateImagePath( delegate->handler->targetDir().toNSString() ); - QString err; - - bool isCameraPhoto = picker.sourceType == UIImagePickerControllerSourceType::UIImagePickerControllerSourceTypeCamera; - if ( isCameraPhoto ) - { - // Camera handling - err = [IOSInterface handleCameraPhoto:info:imagePath]; - } - else - { - // Gallery handling - // Copy an image with metadata from imageURL to targetPath - NSURL *infoImageUrl = info[UIImagePickerControllerImageURL]; - if ( !InputUtils::copyFile( QString::fromNSString( infoImageUrl.absoluteString ), QString::fromNSString( imagePath ) ) ) - { - err = QStringLiteral( "Copying image from a gallery failed." ); - } - infoImageUrl = nil; - } + QString err = [IOSInterface handleCameraPhoto:info:imagePath]; [picker dismissViewControllerAnimated:YES completion:nil]; if ( delegate->handler ) { QVariantMap data; - QString imagePathData( [imagePath UTF8String] ); - data["imagePath"] = imagePathData; + data["imagePath"] = QString( [imagePath UTF8String] ); data["error"] = err; QMetaObject::invokeMethod( delegate->handler, "onImagePickerFinished", Qt::DirectConnection, Q_ARG( bool, err.isEmpty() ), diff --git a/app/ios/iosviewdelegate.h b/app/ios/iosviewdelegate.h index 92e5c4d21..7947f1b63 100644 --- a/app/ios/iosviewdelegate.h +++ b/app/ios/iosviewdelegate.h @@ -17,6 +17,7 @@ #define IOSVIEWDELEGATE_H #include +#import #include "iosimagepicker.h" /** @@ -36,4 +37,11 @@ UINavigationControllerDelegate> - ( id ) initWithHandler:( IOSImagePicker * )handler; @end +/** + * we use PHPickerViewController delegate for gallery image selection, which keeps GPS metadata + */ +@interface IOSGalleryPickerDelegate : NSObject +- ( instancetype ) initWithHandler:( IOSImagePicker * )handler; +@end + #endif // IOSVIEWDELEGATE_H diff --git a/app/ios/iosviewdelegate.mm b/app/ios/iosviewdelegate.mm index df43b62f6..362fbb204 100644 --- a/app/ios/iosviewdelegate.mm +++ b/app/ios/iosviewdelegate.mm @@ -14,12 +14,9 @@ ***************************************************************************/ #include +#include #import "iosviewdelegate.h" -@interface IOSViewDelegate() - -@end - @implementation IOSViewDelegate -( id ) initWithHandler:( IOSImagePicker * )handler @@ -50,3 +47,68 @@ - ( void )imagePickerControllerDidCancel:( UIImagePickerController * )picker } @end + +@implementation IOSGalleryPickerDelegate +{ + QPointer _handler; +} + +- ( instancetype ) initWithHandler:( IOSImagePicker * )handler +{ + self = [super init]; + if ( self ) + { + _handler = handler; + } + return self; +} + +- ( void )picker:( PHPickerViewController * )picker didFinishPicking:( NSArray * )results +{ + [picker dismissViewControllerAnimated:YES completion:nil]; + + if ( results.count == 0 ) + { + return; // user cancelled + } + + if ( !_handler ) + { + return; + } + + PHPickerResult *result = results.firstObject; + + NSDateFormatter *df = [[NSDateFormatter alloc] init]; + [df setDateFormat:@"yyyyMMdd_HHmmss"]; + NSString *fileName = [[df stringFromDate:[NSDate date]] stringByAppendingString:@".jpg"]; + NSString *imagePath = [_handler->targetDir().toNSString() stringByAppendingPathComponent:fileName]; + + [result.itemProvider loadDataRepresentationForTypeIdentifier:@"public.jpeg" + completionHandler: ^ ( NSData * data, NSError * error ) + { + BOOL writeSuccess = data && !error && [data writeToFile:imagePath atomically:YES]; + if ( !writeSuccess ) + { + qWarning() << "Gallery Picker: failed to write image data to" << QString::fromNSString( imagePath ); + } + + dispatch_async( dispatch_get_main_queue(), ^ + { + if ( _handler ) + { + QVariantMap resultData; + resultData["imagePath"] = QString::fromNSString( imagePath ); + if ( !writeSuccess ) + { + resultData["error"] = QStringLiteral( "Copying image from gallery failed." ); + } + QMetaObject::invokeMethod( _handler, "onImagePickerFinished", Qt::DirectConnection, + Q_ARG( bool, writeSuccess ), + Q_ARG( const QVariantMap, resultData ) ); + } + } ); + }]; +} + +@end diff --git a/cmake/FindAppleFrameworks.cmake b/cmake/FindAppleFrameworks.cmake index 53b916b9c..17be9bcab 100644 --- a/cmake/FindAppleFrameworks.cmake +++ b/cmake/FindAppleFrameworks.cmake @@ -19,6 +19,7 @@ set(APPLE_FRAMEWORKS SystemConfiguration CoreLocation CoreHaptics + PhotosUI ) foreach (framework ${APPLE_FRAMEWORKS})