From 743842328d48e88b8b5bcac1ad1595f8c78c4654 Mon Sep 17 00:00:00 2001 From: Luke Farrell Date: Tue, 10 May 2022 11:57:01 +0200 Subject: [PATCH 1/3] BREAKING CHANGE: Use requestImageForAsset to retrieve UIImage and remove includeExtra (as it will be default) --- README.md | 3 +- .../main/java/com/imagepicker/Options.java | 2 - .../src/main/java/com/imagepicker/Utils.java | 17 +-- example/src/App.tsx | 8 - ios/ImagePickerManager.m | 137 +++++++----------- ios/ImagePickerUtils.m | 8 +- src/index.ts | 1 - src/types.ts | 1 - 8 files changed, 60 insertions(+), 117 deletions(-) diff --git a/README.md b/README.md index 360f6ef29..d6d2506f6 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,6 @@ The `callback` will be called with a response object, refer to [The Response Obj | quality | OK | OK | 0 to 1, photos | | cameraType | OK | OK | 'back' or 'front'. May not be supported in few android devices | | includeBase64 | OK | OK | If true, creates base64 string of the image (Avoid using on large image files due to performance) | | -| includeExtra | OK | OK | If true, will include extra data which requires library permissions to be requested (i.e. exif data) | | saveToPhotos | OK | OK | (Boolean) Only for launchCamera, saves the image/video file captured to public photo | | selectionLimit | OK | OK | Default is `1`, use `0` to allow any number of files. Only iOS version >= 14 support `0` and also it supports providing any integer value | | presentationStyle | OK | NO | Controls how the picker is presented. 'pageSheet', 'fullScreen', 'pageSheet', 'formSheet', 'popover', 'overFullScreen', 'overCurrentContext'. Default is 'currentContext' | @@ -118,7 +117,7 @@ The `callback` will be called with a response object, refer to [The Response Obj | fileName | OK | OK | BOTH | NO | The file name | | duration | OK | OK | VIDEO ONLY | NO | The selected video duration in seconds | | bitrate | --- | OK | VIDEO ONLY | NO | The average bitrate (in bits/sec) of the selected video, if available. (Android only) | -| timestamp | OK | OK | BOTH | YES | Timestamp of the asset. Only included if 'includeExtra' is true | +| timestamp | OK | OK | BOTH | YES | Timestamp of the asset. | | id | OK | OK | BOTH | YES | local identifier of the photo or video. On Android, this is the same as fileName | ## Note on file storage diff --git a/android/src/main/java/com/imagepicker/Options.java b/android/src/main/java/com/imagepicker/Options.java index 40de8b2ab..81ce6f050 100644 --- a/android/src/main/java/com/imagepicker/Options.java +++ b/android/src/main/java/com/imagepicker/Options.java @@ -6,7 +6,6 @@ public class Options { int selectionLimit; Boolean includeBase64; - Boolean includeExtra; int videoQuality = 1; int quality; int maxWidth; @@ -21,7 +20,6 @@ public class Options { mediaType = options.getString("mediaType"); selectionLimit = options.getInt("selectionLimit"); includeBase64 = options.getBoolean("includeBase64"); - includeExtra = options.getBoolean("includeExtra"); String videoQualityString = options.getString("videoQuality"); if(!TextUtils.isEmpty(videoQualityString) && !videoQualityString.toLowerCase().equals("high")) { diff --git a/android/src/main/java/com/imagepicker/Utils.java b/android/src/main/java/com/imagepicker/Utils.java index 4c5a2a68d..78b32238e 100644 --- a/android/src/main/java/com/imagepicker/Utils.java +++ b/android/src/main/java/com/imagepicker/Utils.java @@ -404,17 +404,13 @@ static ReadableMap getImageResponseMap(Uri uri, Options options, Context context map.putInt("width", dimensions[0]); map.putInt("height", dimensions[1]); map.putString("type", getMimeType(uri, context)); + map.putString("timestamp", imageMetadata.getDateTime()); + map.putString("id", fileName); if (options.includeBase64) { map.putString("base64", getBase64String(uri, context)); } - if(options.includeExtra) { - // Add more extra data here ... - map.putString("timestamp", imageMetadata.getDateTime()); - map.putString("id", fileName); - } - return map; } @@ -431,12 +427,9 @@ static ReadableMap getVideoResponseMap(Uri uri, Options options, Context context map.putString("type", getMimeType(uri, context)); map.putInt("width", videoMetadata.getWidth()); map.putInt("height", videoMetadata.getHeight()); - - if(options.includeExtra) { - // Add more extra data here ... - map.putString("timestamp", videoMetadata.getDateTime()); - map.putString("id", fileName); - } + map.putString("timestamp", videoMetadata.getDateTime()); + map.putString("id", fileName); + // Add more extra data here ... return map; } diff --git a/example/src/App.tsx b/example/src/App.tsx index 0f4a67617..b6fec33ba 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -4,9 +4,6 @@ import {DemoTitle, DemoButton, DemoResponse} from './components'; import * as ImagePicker from 'react-native-image-picker'; -/* toggle includeExtra */ -const includeExtra = true; - export default function App() { const [response, setResponse] = React.useState(null); @@ -82,7 +79,6 @@ const actions: Action[] = [ saveToPhotos: true, mediaType: 'photo', includeBase64: false, - includeExtra, }, }, { @@ -94,7 +90,6 @@ const actions: Action[] = [ selectionLimit: 0, mediaType: 'photo', includeBase64: false, - includeExtra, }, }, { @@ -103,7 +98,6 @@ const actions: Action[] = [ options: { saveToPhotos: true, mediaType: 'video', - includeExtra, }, }, { @@ -112,7 +106,6 @@ const actions: Action[] = [ options: { selectionLimit: 0, mediaType: 'video', - includeExtra, }, }, { @@ -121,7 +114,6 @@ const actions: Action[] = [ options: { selectionLimit: 0, mediaType: 'mixed', - includeExtra, }, }, ]; diff --git a/ios/ImagePickerManager.m b/ios/ImagePickerManager.m index 0c6138f63..b4188d49a 100644 --- a/ios/ImagePickerManager.m +++ b/ios/ImagePickerManager.m @@ -73,19 +73,14 @@ - (void)launchImagePicker:(NSDictionary *)options callback:(RCTResponseSenderBlo picker.delegate = self; picker.modalPresentationStyle = [RCTConvert UIModalPresentationStyle:options[@"presentationStyle"]]; picker.presentationController.delegate = self; - - if([self.options[@"includeExtra"] boolValue]) { - [self checkPhotosPermissions:^(BOOL granted) { - if (!granted) { - self.callback(@[@{@"errorCode": errPermission}]); - return; - } - [self showPickerViewController:picker]; - }]; - } else { + [self checkPhotosPermissions:^(BOOL granted) { + if (!granted) { + self.callback(@[@{@"errorCode": errPermission}]); + return; + } [self showPickerViewController:picker]; - } + }]; return; } @@ -95,17 +90,13 @@ - (void)launchImagePicker:(NSDictionary *)options callback:(RCTResponseSenderBlo [ImagePickerUtils setupPickerFromOptions:picker options:self.options target:target]; picker.delegate = self; - if([self.options[@"includeExtra"] boolValue]) { - [self checkPhotosPermissions:^(BOOL granted) { - if (!granted) { - self.callback(@[@{@"errorCode": errPermission}]); - return; - } - [self showPickerViewController:picker]; - }]; - } else { - [self showPickerViewController:picker]; - } + [self checkPhotosPermissions:^(BOOL granted) { + if (!granted) { + self.callback(@[@{@"errorCode": errPermission}]); + return; + } + [self showPickerViewController:picker]; + }]; } - (void) showPickerViewController:(UIViewController *)picker @@ -118,29 +109,15 @@ - (void) showPickerViewController:(UIViewController *)picker #pragma mark - Helpers --(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phAsset:(PHAsset * _Nullable)phAsset { - NSString *fileType = [ImagePickerUtils getFileType:data]; - +-(NSMutableDictionary *)mapImageToAsset:(UIImage *)image phAsset:(PHAsset *)phAsset { if ((target == camera) && [self.options[@"saveToPhotos"] boolValue]) { UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); } - - if (![fileType isEqualToString:@"gif"]) { - image = [ImagePickerUtils resizeImage:image - maxWidth:[self.options[@"maxWidth"] floatValue] - maxHeight:[self.options[@"maxHeight"] floatValue]]; - } - if ([fileType isEqualToString:@"jpg"]) { - data = UIImageJPEGRepresentation(image, [self.options[@"quality"] floatValue]); - } else if ([fileType isEqualToString:@"png"]) { - data = UIImagePNGRepresentation(image); - } + NSData *data = UIImageJPEGRepresentation(image, [self.options[@"quality"] floatValue]); NSMutableDictionary *asset = [[NSMutableDictionary alloc] init]; - asset[@"type"] = [@"image/" stringByAppendingString:fileType]; - - NSString *fileName = [self getImageFileName:fileType]; + NSString *fileName = [self getImageFileName:@"jpeg"]; NSString *path = [[NSTemporaryDirectory() stringByStandardizingPath] stringByAppendingPathComponent:fileName]; [data writeToFile:path atomically:YES]; @@ -158,15 +135,12 @@ -(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phA asset[@"fileSize"] = fileSizeValue; } + asset[@"type"] = [@"image/" stringByAppendingString:@"jpeg"]; asset[@"fileName"] = fileName; asset[@"width"] = @(image.size.width); asset[@"height"] = @(image.size.height); - - if(phAsset){ - asset[@"timestamp"] = [self getDateTimeInUTC:phAsset.creationDate]; - asset[@"id"] = phAsset.localIdentifier; - // Add more extra data here ... - } + asset[@"timestamp"] = [self getDateTimeInUTC:phAsset.creationDate]; + asset[@"id"] = phAsset.localIdentifier; return asset; } @@ -212,12 +186,9 @@ -(NSMutableDictionary *)mapVideoToAsset:(NSURL *)url phAsset:(PHAsset * _Nullabl asset[@"fileSize"] = [ImagePickerUtils getFileSizeFromUrl:videoDestinationURL]; asset[@"width"] = @(dimentions.width); asset[@"height"] = @(dimentions.height); - - if(phAsset){ - asset[@"timestamp"] = [self getDateTimeInUTC:phAsset.creationDate]; - asset[@"id"] = phAsset.localIdentifier; - // Add more extra data here ... - } + asset[@"timestamp"] = [self getDateTimeInUTC:phAsset.creationDate]; + asset[@"id"] = phAsset.localIdentifier; + // Add more extra data here ... return asset; } @@ -347,22 +318,17 @@ - (void)imagePickerController:(UIImagePickerController *)picker didFinishPicking { dispatch_block_t dismissCompletionBlock = ^{ NSMutableArray *assets = [[NSMutableArray alloc] initWithCapacity:1]; - PHAsset *asset = nil; + PHAsset *asset = [ImagePickerUtils fetchPHAssetOnIOS13:info]; if (photoSelected == YES) { return; } photoSelected = YES; - // If include extra, we fetch the PHAsset, this required library permissions - if([self.options[@"includeExtra"] boolValue]) { - asset = [ImagePickerUtils fetchPHAssetOnIOS13:info]; - } - if ([info[UIImagePickerControllerMediaType] isEqualToString:(NSString *) kUTTypeImage]) { UIImage *image = [ImagePickerManager getUIImageFromInfo:info]; - [assets addObject:[self mapImageToAsset:image data:[NSData dataWithContentsOfURL:[ImagePickerManager getNSURLFromInfo:info]] phAsset:asset]]; + [assets addObject:[self mapImageToAsset:image phAsset:asset]]; } else { NSError *error; NSDictionary *videoAsset = [self mapVideoToAsset:info[UIImagePickerControllerMediaURL] phAsset:asset error:&error]; @@ -429,41 +395,42 @@ - (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray *assets = [[NSMutableArray alloc] initWithCapacity:results.count]; for (PHPickerResult *result in results) { - PHAsset *asset = nil; NSItemProvider *provider = result.itemProvider; - - // If include extra, we fetch the PHAsset, this required library permissions - if([self.options[@"includeExtra"] boolValue] && result.assetIdentifier != nil) { - PHFetchResult* fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[result.assetIdentifier] options:nil]; - asset = fetchResult.firstObject; - } + PHFetchResult* fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[result.assetIdentifier] options:nil]; + PHAsset *asset = fetchResult.firstObject; dispatch_group_enter(completionGroup); - if ([provider canLoadObjectOfClass:[UIImage class]]) { - NSString *identifier = provider.registeredTypeIdentifiers.firstObject; - // Matches both com.apple.live-photo-bundle and com.apple.private.live-photo-bundle - if ([identifier containsString:@"live-photo-bundle"]) { - // Handle live photos - identifier = @"public.jpeg"; - } - - [provider loadFileRepresentationForTypeIdentifier:identifier completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) { - NSData *data = [[NSData alloc] initWithContentsOfURL:url]; - UIImage *image = [[UIImage alloc] initWithData:data]; - - [assets addObject:[self mapImageToAsset:image data:data phAsset:asset]]; - dispatch_group_leave(completionGroup); - }]; - } else if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeMovie]) { + if(asset.mediaType == PHAssetMediaTypeImage) { + PHImageRequestOptions *requestOptions = [[PHImageRequestOptions alloc] init]; + requestOptions.synchronous = YES; + requestOptions.networkAccessAllowed = YES; + requestOptions.version = PHImageRequestOptionsVersionCurrent; + requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic; + requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact; + + float maxWidth = [self.options[@"maxWidth"] floatValue]; + float maxHeight = [self.options[@"maxHeight"] floatValue]; + CGSize targetSize = CGSizeMake(maxWidth, maxHeight); + + [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:targetSize contentMode:PHImageContentModeDefault options:requestOptions resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) { + NSMutableDictionary *imageAsset = [self mapImageToAsset:result phAsset:asset]; + [assets addObject:imageAsset]; + + dispatch_group_leave(completionGroup); + }]; + } else if(asset.mediaType == PHAssetMediaTypeVideo) { [provider loadFileRepresentationForTypeIdentifier:(NSString *)kUTTypeMovie completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) { [assets addObject:[self mapVideoToAsset:url phAsset:asset error:nil]]; dispatch_group_leave(completionGroup); }]; - } else { - // The provider didn't have an item matching photo or video (fails on M1 Mac Simulator) - dispatch_group_leave(completionGroup); - } + } else if(asset.mediaType == PHAssetMediaTypeAudio) { + // We don't handle Audio files with this library + dispatch_group_leave(completionGroup); + } else { + dispatch_group_leave(completionGroup); + } + } dispatch_group_notify(completionGroup, dispatch_get_main_queue(), ^{ diff --git a/ios/ImagePickerUtils.m b/ios/ImagePickerUtils.m index 620ddc99e..ac5bb0975 100644 --- a/ios/ImagePickerUtils.m +++ b/ios/ImagePickerUtils.m @@ -51,12 +51,8 @@ + (PHPickerConfiguration *)makeConfigurationFromOptions:(NSDictionary *)options #if __has_include() PHPickerConfiguration *configuration; - if(options[@"includeExtra"]) { - PHPhotoLibrary *photoLibrary = [PHPhotoLibrary sharedPhotoLibrary]; - configuration = [[PHPickerConfiguration alloc] initWithPhotoLibrary:photoLibrary]; - } else { - configuration = [[PHPickerConfiguration alloc] init]; - } + PHPhotoLibrary *photoLibrary = [PHPhotoLibrary sharedPhotoLibrary]; + configuration = [[PHPickerConfiguration alloc] initWithPhotoLibrary:photoLibrary]; configuration.preferredAssetRepresentationMode = PHPickerConfigurationAssetRepresentationModeCurrent; configuration.selectionLimit = [options[@"selectionLimit"] integerValue]; diff --git a/src/index.ts b/src/index.ts index f76c78b11..a7b2b189c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,6 @@ const DEFAULT_OPTIONS: ImageLibraryOptions & CameraOptions = { selectionLimit: 1, saveToPhotos: false, durationLimit: 0, - includeExtra: false, presentationStyle: 'pageSheet' }; diff --git a/src/types.ts b/src/types.ts index 6e678ceb0..a25590dae 100644 --- a/src/types.ts +++ b/src/types.ts @@ -8,7 +8,6 @@ export interface ImageLibraryOptions { quality?: PhotoQuality; videoQuality?: AndroidVideoOptions | iOSVideoOptions; includeBase64?: boolean; - includeExtra?: boolean; presentationStyle?: | 'currentContext' | 'fullScreen' From 41bde14e5fb73f5a7dcdd2d83fced96d6953fc87 Mon Sep 17 00:00:00 2001 From: Rodrigo Rodrigues Date: Tue, 28 Feb 2023 14:18:31 -0300 Subject: [PATCH 2/3] fixing ts issues --- src/platforms/native.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platforms/native.ts b/src/platforms/native.ts index 6c764b071..e61798338 100644 --- a/src/platforms/native.ts +++ b/src/platforms/native.ts @@ -18,7 +18,6 @@ const DEFAULT_OPTIONS: ImageLibraryOptions & CameraOptions = { selectionLimit: 1, saveToPhotos: false, durationLimit: 0, - includeExtra: false, presentationStyle: 'pageSheet', }; From 9bc88c2c217fa5196f5d1f9c3b9deb26575f9901 Mon Sep 17 00:00:00 2001 From: Rodrigo Rodrigues Date: Tue, 28 Feb 2023 14:37:17 -0300 Subject: [PATCH 3/3] fixing build issues --- ios/ImagePickerManager.mm | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/ios/ImagePickerManager.mm b/ios/ImagePickerManager.mm index 60aa55b13..2699a6060 100644 --- a/ios/ImagePickerManager.mm +++ b/ios/ImagePickerManager.mm @@ -145,30 +145,26 @@ - (void) showPickerViewController:(UIViewController *)picker --(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phAsset:(PHAsset * _Nullable)phAsset { - NSString *fileType = [ImagePickerUtils getFileType:data]; +-(NSMutableDictionary *)mapImageToAsset:(UIImage *)image phAsset:(PHAsset * _Nullable)phAsset { +// NSString *fileType = [ImagePickerUtils getFileType:data]; if (target == camera) { if ([self.options[@"saveToPhotos"] boolValue]) { UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); } - data = extractImageData(image); +// data = extractImageData(image); } UIImage* newImage = image; - if (![fileType isEqualToString:@"gif"]) { - newImage = [ImagePickerUtils resizeImage:image - maxWidth:[self.options[@"maxWidth"] floatValue] - maxHeight:[self.options[@"maxHeight"] floatValue]]; - } +// if (![fileType isEqualToString:@"gif"]) { +// newImage = [ImagePickerUtils resizeImage:image +// maxWidth:[self.options[@"maxWidth"] floatValue] +// maxHeight:[self.options[@"maxHeight"] floatValue]]; +// } float quality = [self.options[@"quality"] floatValue]; - if (![image isEqual:newImage] || (quality >= 0 && quality < 1)) { - if ([fileType isEqualToString:@"jpg"]) { - data = UIImageJPEGRepresentation(newImage, quality); - } else if ([fileType isEqualToString:@"png"]) { - data = UIImagePNGRepresentation(newImage); - } - } + + NSData *data = UIImageJPEGRepresentation(newImage, quality); + NSMutableDictionary *asset = [[NSMutableDictionary alloc] init]; NSString *fileName = [self getImageFileName:@"jpeg"]; @@ -470,7 +466,6 @@ - (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray