Skip to content

Commit

Permalink
feat(asset): return id of asset for iOS and Android (#1889)
Browse files Browse the repository at this point in the history
* feat: local asset id for images and videos on iOS

* feat: id for Android

* rename fetchPHAsset
  • Loading branch information
helenaford committed Dec 10, 2021
1 parent 6bc0c8e commit 82fe096
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 29 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ The `callback` will be called with a response object, refer to [The Response Obj
| height | OK | OK | Image dimensions (photos only) |
| fileSize | OK | OK | The file size |
| type | OK | OK | The file type (photos only) |
| fileName | OK | OK | The file name |
| fileName | OK | OK | The file name
| duration | OK | OK | The selected video duration in seconds
| bitrate | --- | OK | The average bitrate (in bits/sec) of the selected video, if available.|
| bitrate | --- | OK | The average bitrate (in bits/sec) of the selected video, if available. (Android only)
| timestamp | OK | OK | Timestamp of the photo. Only included if 'includeExtra' is true
| id | OK | OK | local identifier of the photo or video. On Android, this is the same as fileName |
## Note on file storage
Expand Down
12 changes: 9 additions & 3 deletions android/src/main/java/com/imagepicker/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,9 @@ static ReadableMap getImageResponseMap(Uri uri, Options options, Context context

if(options.includeExtra) {
String datetime = getDateTimeExif(uri, context);
// Add more exif data here ...
// Add more extra data here ...
map.putString("timestamp", datetime);
map.putString("id", fileName);
}

return map;
Expand Down Expand Up @@ -469,7 +470,7 @@ static ReadableMap getImageResponseMap(Uri uri, Options options, Context context
}
}

static ReadableMap getVideoResponseMap(Uri uri, Context context) {
static ReadableMap getVideoResponseMap(Uri uri, Options options, Context context) {
String fileName = uri.getLastPathSegment();
WritableMap map = Arguments.createMap();
map.putString("uri", uri.toString());
Expand All @@ -479,6 +480,11 @@ static ReadableMap getVideoResponseMap(Uri uri, Context context) {
map.putInt("bitrate", videoMetadata.getBitrate());
map.putString("fileName", fileName);
map.putString("type", getMimeType(uri, context));

if(options.includeExtra) {
map.putString("id", fileName);
}

return map;
}

Expand All @@ -495,7 +501,7 @@ static ReadableMap getResponseMap(List<Uri> fileUris, Options options, Context c
uri = resizeImage(uri, context, options);
assets.pushMap(getImageResponseMap(uri, options, context));
} else if (isVideoType(uri, context)) {
assets.pushMap(getVideoResponseMap(uri, context));
assets.pushMap(getVideoResponseMap(uri, options, context));
} else {
throw new RuntimeException("Unsupported file type");
}
Expand Down
6 changes: 3 additions & 3 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ PODS:
- React-cxxreact (= 0.63.2)
- React-jsi (= 0.63.2)
- React-jsinspector (0.63.2)
- react-native-image-picker (4.0.6):
- react-native-image-picker (4.4.2):
- React-Core
- React-RCTActionSheet (0.63.2):
- React-Core/RCTActionSheetHeaders (= 0.63.2)
Expand Down Expand Up @@ -357,7 +357,7 @@ SPEC CHECKSUMS:
React-jsi: 54245e1d5f4b690dec614a73a3795964eeef13a8
React-jsiexecutor: 8ca588cc921e70590820ce72b8789b02c67cce38
React-jsinspector: b14e62ebe7a66e9231e9581279909f2fc3db6606
react-native-image-picker: a6e56460d34905c849ada551db30897dc7f3d535
react-native-image-picker: 6a18bc5c053ba096ec9b027bd497129cfda30e6b
React-RCTActionSheet: 910163b6b09685a35c4ebbc52b66d1bfbbe39fc5
React-RCTAnimation: 9a883bbe1e9d2e158d4fb53765ed64c8dc2200c6
React-RCTBlob: 39cf0ece1927996c4466510e25d2105f67010e13
Expand All @@ -372,4 +372,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: 3bd773631df40b2af744ef96561c948615050599

COCOAPODS: 1.10.1
COCOAPODS: 1.11.2
8 changes: 8 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import {DemoTitle, DemoButton, DemoResponse} from './components';

import * as ImagePicker from '../../src';

/* toggle includeExtra */
const includeExtra = true;

export default function App() {
const [response, setResponse] = React.useState<any>(null);

Expand Down Expand Up @@ -79,6 +82,7 @@ const actions: Action[] = [
saveToPhotos: true,
mediaType: 'photo',
includeBase64: false,
includeExtra,
},
},
{
Expand All @@ -90,6 +94,7 @@ const actions: Action[] = [
selectionLimit: 0,
mediaType: 'photo',
includeBase64: false,
includeExtra,
},
},
{
Expand All @@ -98,6 +103,7 @@ const actions: Action[] = [
options: {
saveToPhotos: true,
mediaType: 'video',
includeExtra,
},
},
{
Expand All @@ -106,6 +112,7 @@ const actions: Action[] = [
options: {
selectionLimit: 0,
mediaType: 'video',
includeExtra,
},
},
{
Expand All @@ -114,6 +121,7 @@ const actions: Action[] = [
options: {
selectionLimit: 0,
mediaType: 'mixed',
includeExtra,
},
},
];
42 changes: 21 additions & 21 deletions ios/ImagePickerManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,14 @@ -(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phA
NSString *creationDate = [formatter stringFromDate:phAsset.creationDate];

asset[@"timestamp"] = creationDate;
// Add more exif data here ...
asset[@"id"] = phAsset.localIdentifier;
// Add more extra data here ...
}

return asset;
}

-(NSMutableDictionary *)mapVideoToAsset:(NSURL *)url error:(NSError **)error {
-(NSMutableDictionary *)mapVideoToAsset:(NSURL *)url phAsset:(PHAsset * _Nullable)phAsset error:(NSError **)error {
NSString *fileName = [url lastPathComponent];
NSString *path = [[NSTemporaryDirectory() stringByStandardizingPath] stringByAppendingPathComponent:fileName];
NSURL *videoDestinationURL = [NSURL fileURLWithPath:path];
Expand Down Expand Up @@ -201,13 +202,19 @@ -(NSMutableDictionary *)mapVideoToAsset:(NSURL *)url error:(NSError **)error {
}
}
}

NSMutableDictionary *asset = [[NSMutableDictionary alloc] init];
asset[@"fileName"] = fileName;
asset[@"duration"] = [NSNumber numberWithDouble:CMTimeGetSeconds([AVAsset assetWithURL:videoDestinationURL].duration)];
asset[@"uri"] = videoDestinationURL.absoluteString;
asset[@"type"] = [ImagePickerUtils getFileTypeFromUrl:videoDestinationURL];
asset[@"fileSize"] = [ImagePickerUtils getFileSizeFromUrl:videoDestinationURL];

if (phAsset) {
asset[@"id"] = phAsset.localIdentifier;
// Add more extra data here ...
}


return asset;
}
Expand Down Expand Up @@ -331,32 +338,25 @@ - (void)imagePickerController:(UIImagePickerController *)picker didFinishPicking
{
dispatch_block_t dismissCompletionBlock = ^{
NSMutableArray<NSDictionary *> *assets = [[NSMutableArray alloc] initWithCapacity:1];
PHAsset *asset = nil;

// 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]) {
PHAsset *asset = nil;
UIImage *image = [ImagePickerManager getUIImageFromInfo:info];

// If include exif, we fetch the PHAsset, this required library permissions
if([self.options[@"includeExtra"] boolValue]) {
NSURL *referenceURL = [info objectForKey:UIImagePickerControllerReferenceURL];

if(referenceURL != nil) {
// We fetch the asset like this to support iOS 10 and lower
// see: https://stackoverflow.com/a/52529904/4177049
PHFetchResult* fetchResult = [PHAsset fetchAssetsWithALAssetURLs:@[referenceURL] options:nil];
asset = fetchResult.firstObject;
}
}

[assets addObject:[self mapImageToAsset:image data:[NSData dataWithContentsOfURL:[ImagePickerManager getNSURLFromInfo:info]] phAsset:asset]];
} else {
NSError *error;
NSDictionary *asset = [self mapVideoToAsset:info[UIImagePickerControllerMediaURL] error:&error];
if (asset == nil) {
NSDictionary *videoAsset = [self mapVideoToAsset:info[UIImagePickerControllerMediaURL] phAsset:asset error:&error];
if (videoAsset == nil) {
self.callback(@[@{@"errorCode": errOthers, @"errorMessage": error.localizedFailureReason}]);
return;
}
[assets addObject:asset];
[assets addObject:videoAsset];
}

NSMutableDictionary *response = [[NSMutableDictionary alloc] init];
Expand Down Expand Up @@ -410,7 +410,7 @@ - (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPick
PHAsset *asset = nil;
NSItemProvider *provider = result.itemProvider;

// If include exif, we fetch the PHAsset, this required library permissions
// 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;
Expand All @@ -434,7 +434,7 @@ - (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPick
}];
} else if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeMovie]) {
[provider loadFileRepresentationForTypeIdentifier:(NSString *)kUTTypeMovie completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) {
[assets addObject:[self mapVideoToAsset:url error:nil]];
[assets addObject:[self mapVideoToAsset:url phAsset:asset error:nil]];
dispatch_group_leave(completionGroup);
}];
} else {
Expand Down
3 changes: 3 additions & 0 deletions ios/ImagePickerUtils.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "ImagePickerManager.h"
#import <Photos/Photos.h>

@class PHPickerConfiguration;

Expand All @@ -17,5 +18,7 @@
+ (NSString *) getFileTypeFromUrl:(NSURL *)url;

+ (NSString *) getFileSizeFromUrl:(NSURL *)url;

+ (PHAsset *)fetchPHAssetOnIOS13:(NSDictionary<NSString *,id> *)info;

@end
14 changes: 14 additions & 0 deletions ios/ImagePickerUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,18 @@ + (UIImage*)resizeImage:(UIImage*)image maxWidth:(float)maxWidth maxHeight:(floa
return newImage;
}

+ (PHAsset *)fetchPHAssetOnIOS13:(NSDictionary<NSString *,id> *)info
{
NSURL *referenceURL = [info objectForKey:UIImagePickerControllerReferenceURL];

if(!referenceURL) {
return nil;
}

// We fetch the asset like this to support iOS 10 and lower
// see: https://stackoverflow.com/a/52529904/4177049
PHFetchResult* fetchResult = [PHAsset fetchAssetsWithALAssetURLs:@[referenceURL] options:nil];
return fetchResult.firstObject;
}

@end
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface Asset {
duration?: number;
bitrate?: number;
timestamp?: string;
id?: string;
}

export interface ImagePickerResponse {
Expand Down

0 comments on commit 82fe096

Please sign in to comment.