Skip to content

Commit

Permalink
3690 use photokit for multigallery (#3801)
Browse files Browse the repository at this point in the history
* feat: [3690] Add support for PHPickerController for multigallery

Currently behind a define.  Requires PhotosUI framework

* Use photokit on ios 14+ if enabled.

* Added support for using PhotoKit for multigallery in xcode local builder.

New build hint to enable it ios.usePhotoKitForMultigallery=true

---------

Co-authored-by: Steve Hannah <[email protected]>
  • Loading branch information
shannah and Steve Hannah authored Apr 7, 2024
1 parent cf20011 commit 879e336
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Ports/iOSPort/nativeSources/CodenameOne_GLViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
> {
Expand Down
98 changes: 98 additions & 0 deletions Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -3384,6 +3384,104 @@ - (void)qb_imagePickerControllerDidCancel:(QBImagePickerController *)picker
galleryPopover = NO;
}

#ifdef USE_PHOTOKIT_FOR_MULTIGALLERY
- (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPickerResult *> *)results API_UNAVAILABLE(watchos)
{
if (cn1_waitingForImages == NULL) {
cn1_waitingForImages = [[NSMutableString alloc] init];
} else {
[cn1_waitingForImages setString:@""];
}

NSMutableArray<PHAsset *> *assets = [NSMutableArray new];
for (PHPickerResult *result in results) {
NSString *assetIdentifier = result.assetIdentifier;
if (assetIdentifier) {
PHFetchResult<PHAsset *> *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

Expand Down
76 changes: 76 additions & 0 deletions Ports/iOSPort/nativeSources/IOSNative.m
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@
#ifdef CN1_INCLUDE_NOTIFICATIONS2
#import <UserNotifications/UserNotifications.h>
#endif
#ifdef INCLUDE_PHOTOLIBRARY_USAGE
#ifdef ENABLE_GALLERY_MULTISELECT
#ifdef USE_PHOTOKIT_FOR_MULTIGALLERY
#import <PhotosUI/PhotosUI.h>
#endif
#endif
#endif

/*static JAVA_OBJECT utf8_constant = JAVA_NULL;
JAVA_OBJECT fromNSString(NSString* str)
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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";
Expand Down

0 comments on commit 879e336

Please sign in to comment.