diff --git a/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.h b/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.h index d6a9f30e9d..cbd4a710c8 100644 --- a/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.h +++ b/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.h @@ -61,6 +61,7 @@ //#define GLUIIMAGE_AUTOSCALE_LARGE_TEXTURES //#define CN1_USE_JAVASCRIPTCORE //#define ENABLE_GALLERY_MULTISELECT +//#define USE_PHOTOKIT_FOR_MULTIGALLERY //#define INCLUDE_CONTACTS_USAGE //#define INCLUDE_CALENDARS_USAGE //#define INCLUDE_CAMERA_USAGE @@ -92,6 +93,11 @@ #ifdef INCLUDE_PHOTOLIBRARY_USAGE #ifdef ENABLE_GALLERY_MULTISELECT #import "QBImagePickerController.h" + +#ifdef USE_PHOTOKIT_FOR_MULTIGALLERY +#import "PhotosUI/PhotosUI.h" +#endif + #endif #endif @@ -178,6 +184,9 @@ MFMessageComposeViewControllerDelegate, CLLocationManagerDelegate, AVAudioRecord #ifdef INCLUDE_PHOTOLIBRARY_USAGE #ifdef ENABLE_GALLERY_MULTISELECT ,QBImagePickerControllerDelegate +#ifdef USE_PHOTOKIT_FOR_MULTIGALLERY + ,PHPickerViewControllerDelegate +#endif #endif #endif > { diff --git a/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m b/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m index b227897dc4..b8411641dc 100644 --- a/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m +++ b/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m @@ -3384,6 +3384,104 @@ - (void)qb_imagePickerControllerDidCancel:(QBImagePickerController *)picker galleryPopover = NO; } +#ifdef USE_PHOTOKIT_FOR_MULTIGALLERY +- (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray *)results API_UNAVAILABLE(watchos) +{ + if (cn1_waitingForImages == NULL) { + cn1_waitingForImages = [[NSMutableString alloc] init]; + } else { + [cn1_waitingForImages setString:@""]; + } + + NSMutableArray *assets = [NSMutableArray new]; + for (PHPickerResult *result in results) { + NSString *assetIdentifier = result.assetIdentifier; + if (assetIdentifier) { + PHFetchResult *fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[assetIdentifier] options:nil]; + [assets addObject:fetchResult.firstObject]; + } + } + + cn1_waitingForImagesCount = [assets count]; + __block int idx=0; + PHImageRequestOptions *options = [PHImageRequestOptions new]; + options.synchronous = YES; + options.networkAccessAllowed = YES; + for (PHAsset *asset in assets) { + @autoreleasepool { + if (asset.mediaType == PHAssetMediaTypeImage) { + [[PHImageManager defaultManager] requestImageForAsset:asset + targetSize:CGSizeMake(asset.pixelWidth, asset.pixelHeight) + contentMode:PHImageContentModeDefault + options:options + resultHandler:^(UIImage *originalImage, NSDictionary *info) { + UIImage* image = originalImage; + if (([(NSNumber*)[info valueForKey:PHImageResultIsDegradedKey] boolValue] == YES)) { + return; + } + +//$$ DISABLE_IMAGE_ROTATION_FIX_START +#ifndef LOW_MEM_CAMERA + if (image.imageOrientation != UIImageOrientationUp) { + UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale); + [image drawInRect:(CGRect){0, 0, image.size}]; + image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + } +#endif +//$$ DISABLE_IMAGE_ROTATION_FIX_END + + NSData* data = UIImageJPEGRepresentation(image, 90 / 100.0f); + + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + NSString *path = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"temp_image_%d.jpg", idx++]]; + [data writeToFile:path atomically:YES]; + cn1_addSelectedImagePath(path); + }]; + } else { + PHVideoRequestOptions *options = [PHVideoRequestOptions new]; + options.version = PHVideoRequestOptionsVersionOriginal; + options.networkAccessAllowed = YES; + + [[PHImageManager defaultManager] requestAVAssetForVideo:asset + options:options + resultHandler: + ^(AVAsset * _Nullable avasset, + AVAudioMix * _Nullable audioMix, + NSDictionary * _Nullable info) + { + NSError *error; + AVURLAsset *avurlasset = (AVURLAsset*) avasset; + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + NSString *path = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"temp_video_%d.%@", idx++, avurlasset.URL.pathExtension]]; + // Write to documents folder + NSURL *fileURL = [NSURL fileURLWithPath:path]; + [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil]; + if ([[NSFileManager defaultManager] copyItemAtURL:avurlasset.URL + toURL:fileURL + error:&error]) { + cn1_addSelectedImagePath(path); + } else { + cn1_addSelectedImagePath([NSString stringWithFormat:@"!{Error: %@}", [error localizedDescription]]); + } + }]; + } + } + } + +#ifdef LOW_MEM_CAMERA + [picker dismissModalViewControllerAnimated:NO]; +#else + [picker dismissModalViewControllerAnimated:YES]; +#endif + popoverController = nil; + popoverControllerInstance = nil; + galleryPopover = NO; +} +#endif + #endif #endif diff --git a/Ports/iOSPort/nativeSources/IOSNative.m b/Ports/iOSPort/nativeSources/IOSNative.m index 13beda8113..b6cd5214d8 100644 --- a/Ports/iOSPort/nativeSources/IOSNative.m +++ b/Ports/iOSPort/nativeSources/IOSNative.m @@ -106,6 +106,13 @@ #ifdef CN1_INCLUDE_NOTIFICATIONS2 #import #endif +#ifdef INCLUDE_PHOTOLIBRARY_USAGE +#ifdef ENABLE_GALLERY_MULTISELECT +#ifdef USE_PHOTOKIT_FOR_MULTIGALLERY +#import +#endif +#endif +#endif /*static JAVA_OBJECT utf8_constant = JAVA_NULL; JAVA_OBJECT fromNSString(NSString* str) @@ -4234,6 +4241,75 @@ void com_codename1_impl_ios_IOSNative_captureCamera___boolean_int_int(CN1_THREAD #ifdef INCLUDE_PHOTOLIBRARY_USAGE #ifdef ENABLE_GALLERY_MULTISELECT + +#ifdef USE_PHOTOKIT_FOR_MULTIGALLERY +void openGalleryMultipleWithPhotoKit(JAVA_INT type) { +#ifdef USE_PHOTOKIT_FOR_MULTIGALLERY + if (@available(iOS 14, *)) { + openGalleryMultipleWithPhotoKit(type); + return; + } +#endif + dispatch_async(dispatch_get_main_queue(), ^{ + POOL_BEGIN(); + + if (@available(iOS 14, *)) { + PHPickerFilter *filter; + if (type==0 || type == 3){ + filter = [PHPickerFilter imagesFilter]; + } else if (type==1 || type == 4){ + filter = [PHPickerFilter videosFilter]; + } else { + filter = [PHPickerFilter anyFilterMatchingSubfilters:[NSArray arrayWithObjects:[PHPickerFilter imagesFilter], [PHPickerFilter videosFilter], nil]]; + } + + PHPickerConfiguration *config = [[PHPickerConfiguration alloc] initWithPhotoLibrary:[PHPhotoLibrary sharedPhotoLibrary]]; + config.filter = filter; + config.preferredAssetRepresentationMode = PHPickerConfigurationAssetRepresentationModeCurrent; + if (@available(iOS 15, *)) { + config.selection = PHPickerConfigurationSelectionOrdered; + } else { + // Fallback on earlier versions + } + config.selectionLimit = 0; + + + PHPickerViewController *pickerController =[[PHPickerViewController alloc] initWithConfiguration:config]; + + pickerController.delegate = [CodenameOne_GLViewController instance]; + + if(popoverSupported()) { + if (popoverController != nil) { + #ifndef CN1_USE_ARC + [popoverController release]; + #endif + popoverController = nil; + } + galleryPopover = YES; + popoverController = [[NSClassFromString(@"UIPopoverController") alloc] + initWithContentViewController:pickerController]; + + popoverController.delegate = [CodenameOne_GLViewController instance]; + [popoverController presentPopoverFromRect:CGRectMake(0,32,320,480) + inView:[[CodenameOne_GLViewController instance] view] + permittedArrowDirections:UIPopoverArrowDirectionAny + animated:YES]; + } else { + [[CodenameOne_GLViewController instance] presentModalViewController:pickerController animated:YES]; + } + + + } else { + // Fallback on earlier versions + } + + + + POOL_END(); + }); +} +#endif + void openGalleryMultiple(JAVA_INT type) { dispatch_async(dispatch_get_main_queue(), ^{ POOL_BEGIN(); diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java index ccfe55b55d..ba8833f684 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java @@ -49,6 +49,7 @@ public class IPhoneBuilder extends Executor { private boolean useMetal; private boolean enableGalleryMultiselect; + private boolean usePhotoKitForMultigallery; private boolean enableWKWebView, disableUIWebView; private String pod = "/usr/local/bin/pod"; private int podTimeout = 300000; // 5 minutes @@ -269,6 +270,8 @@ public boolean build(File sourceZip, BuildRequest request) throws BuildException addMinDeploymentTarget("8.0"); } } + usePhotoKitForMultigallery = "true".equals(request.getArg("ios.usePhotoKitForMultigallery", "false")); + enableWKWebView = "true".equals(request.getArg("ios.useWKWebView", "true")); if (enableWKWebView) { addMinDeploymentTarget("8.0"); @@ -810,6 +813,13 @@ public void usesClassMethod(String cls, String method) { throw new BuildException("Failed to enabled gallery multiselect support", ex); } } + if (usePhotoKitForMultigallery) { + try { + replaceInFile(new File(buildinRes, "CodenameOne_GLViewController.h"), "//#define USE_PHOTOKIT_FOR_MULTIGALLERY", "#define USE_PHOTOKIT_FOR_MULTIGALLERY"); + } catch (IOException ex) { + throw new BuildException("Failed to enabled gallery multiselect support", ex); + } + } if (enableWKWebView) { try { replaceInFile(new File(buildinRes, "CodenameOne_GLViewController.h"), "//#define ENABLE_WKWEBVIEW", "#define ENABLE_WKWEBVIEW"); @@ -1423,6 +1433,14 @@ public void usesClassMethod(String cls, String method) { } } + if (enableGalleryMultiselect && photoLibraryUsage && usePhotoKitForMultigallery) { + if (addLibs == null || addLibs.length() == 0) { + addLibs = "PhotosUI.framework"; + } else { + addLibs += ";PhotosUI.framework"; + } + } + if ((includePush || usesLocalNotifications) && xcodeVersion >= 9) { if (addLibs == null) { addLibs = "UserNotifications.framework";