From b5bc6dcab9e40fc8196e3324793b8959493181bf Mon Sep 17 00:00:00 2001 From: Ran Greenberg Date: Wed, 15 Jun 2016 14:40:04 +0300 Subject: [PATCH] first version on objective-c implementation --- example/GalleryScreen.js | 7 +- example/ios/example.xcodeproj/project.pbxproj | 30 ++ .../ReactNativeCameraKit/CKGalleryManager.m | 348 +++++------------- src/CameraKitGallery.js | 4 +- 4 files changed, 129 insertions(+), 260 deletions(-) diff --git a/example/GalleryScreen.js b/example/GalleryScreen.js index b64ced1..b619c59 100644 --- a/example/GalleryScreen.js +++ b/example/GalleryScreen.js @@ -47,7 +47,7 @@ export default class GalleryScreen extends Component { componentDidMount() { if (this.state.albumName) { - CameraKitGallery.getPhotosForAlbum(this.state.albumName, 5, (data) => this._appendAssets(data), (e) => logError(e)); + CameraKitGallery.getPhotosForAlbum(this.state.albumName, 100, (data) => this._appendAssets(data), (e) => logError(e)); } } @@ -136,17 +136,18 @@ const styles = StyleSheet.create({ listView: { //flex:1, //flexDirection:'column', + paddingTop: 0, margin: 8, backgroundColor: '#D6DAC2', - //alignSelf: 'stretch' }, row: { - flexDirection: 'row', + flexDirection: 'column', flex: 1, }, image: { margin: 4, + marginBottom: 0 }, }); diff --git a/example/ios/example.xcodeproj/project.pbxproj b/example/ios/example.xcodeproj/project.pbxproj index 7bcb877..34c63d9 100644 --- a/example/ios/example.xcodeproj/project.pbxproj +++ b/example/ios/example.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; + 262E41281D1058D300C82B27 /* libRCTCameraRoll.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 262E41271D1058C100C82B27 /* libRCTCameraRoll.a */; }; 2658E2F11CFB4231001477C8 /* libReactNativeCameraKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2646937E1CFB305600F3A740 /* libReactNativeCameraKit.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; /* End PBXBuildFile section */ @@ -89,6 +90,13 @@ remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; remoteInfo = React; }; + 262E41261D1058C100C82B27 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 262E41221D1058C100C82B27 /* RCTCameraRoll.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5115D1A9E6B3D00147676; + remoteInfo = RCTCameraRoll; + }; 2646937D1CFB305600F3A740 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 264693791CFB305600F3A740 /* ReactNativeCameraKit.xcodeproj */; @@ -132,6 +140,7 @@ 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = example/main.m; sourceTree = ""; }; 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; + 262E41221D1058C100C82B27 /* RCTCameraRoll.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCameraRoll.xcodeproj; path = "../node_modules/react-native/Libraries/CameraRoll/RCTCameraRoll.xcodeproj"; sourceTree = ""; }; 264693791CFB305600F3A740 /* ReactNativeCameraKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeCameraKit.xcodeproj; path = "../node_modules/react-native-camera-kit/ios/lib/ReactNativeCameraKit.xcodeproj"; sourceTree = ""; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; @@ -149,6 +158,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 262E41281D1058D300C82B27 /* libRCTCameraRoll.a in Frameworks */, 2658E2F11CFB4231001477C8 /* libReactNativeCameraKit.a in Frameworks */, 146834051AC3E58100842450 /* libReact.a in Frameworks */, 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, @@ -261,6 +271,14 @@ name = Products; sourceTree = ""; }; + 262E41231D1058C100C82B27 /* Products */ = { + isa = PBXGroup; + children = ( + 262E41271D1058C100C82B27 /* libRCTCameraRoll.a */, + ); + name = Products; + sourceTree = ""; + }; 2646937A1CFB305600F3A740 /* Products */ = { isa = PBXGroup; children = ( @@ -280,6 +298,7 @@ 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( + 262E41221D1058C100C82B27 /* RCTCameraRoll.xcodeproj */, 264693791CFB305600F3A740 /* ReactNativeCameraKit.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */, 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, @@ -394,6 +413,10 @@ ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; }, + { + ProductGroup = 262E41231D1058C100C82B27 /* Products */; + ProjectRef = 262E41221D1058C100C82B27 /* RCTCameraRoll.xcodeproj */; + }, { ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; @@ -500,6 +523,13 @@ remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 262E41271D1058C100C82B27 /* libRCTCameraRoll.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTCameraRoll.a; + remoteRef = 262E41261D1058C100C82B27 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 2646937E1CFB305600F3A740 /* libReactNativeCameraKit.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; diff --git a/ios/lib/ReactNativeCameraKit/CKGalleryManager.m b/ios/lib/ReactNativeCameraKit/CKGalleryManager.m index db84e2c..5b68bfa 100644 --- a/ios/lib/ReactNativeCameraKit/CKGalleryManager.m +++ b/ios/lib/ReactNativeCameraKit/CKGalleryManager.m @@ -18,13 +18,15 @@ typedef void (^AlbumsBlock)(NSDictionary *albums); @property (nonatomic, strong) PHFetchResult *smartAlbums; @property (nonatomic, strong) PHFetchResult *topLevelUserCollections; - @end + @implementation CKGalleryManager + RCT_EXPORT_MODULE(); + -(instancetype)init { self = [super init]; @@ -43,272 +45,108 @@ RCT_EXPORT_MODULE(); albumsOptions.predicate = [NSPredicate predicateWithFormat:@"estimatedAssetCount > 0"]; self.allPhotos = [PHAsset fetchAssetsWithOptions:allPhotosOptions]; -// self.smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumMyPhotoStream options:nil]; + self.smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumMyPhotoStream options:nil]; self.topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil]; - } - --(void)getAllAlbumsNameAndThumbnails:(AlbumsBlock)block { +-(void)extractCollection:(id)collection + imageRequestOptions:(PHImageRequestOptions*)options + thumbnailSize:(CGSize)thumbnailSize + block:(AlbumsBlock)block { - PHImageRequestOptions *cropToSquare = [[PHImageRequestOptions alloc] init]; - cropToSquare.resizeMode = PHImageRequestOptionsResizeModeExact; - cropToSquare.synchronous = YES; + NSInteger collectionCount = ([collection isKindOfClass:[PHAssetCollection class]]) ? [PHAsset fetchAssetsInAssetCollection:collection options:nil].count : ((PHFetchResult*)collection).count; + + if (collectionCount > 0){ + + NSString *albumName = ([collection isKindOfClass:[PHAssetCollection class]]) ? ((PHAssetCollection*)collection).localizedTitle : @"All photos"; + PHFetchResult *fetchResult = ([collection isKindOfClass:[PHAssetCollection class]]) ? [PHAsset fetchKeyAssetsInAssetCollection:collection options:nil] : (PHAssetCollection*)collection; + PHAsset *thumbnail = [fetchResult firstObject]; + + NSMutableDictionary *albumInfo = [[NSMutableDictionary alloc] init]; + albumInfo[@"albumName"] = albumName; + albumInfo[@"imagesCount"] = [NSNumber numberWithInteger:collectionCount]; + + [[PHImageManager defaultManager] + requestImageForAsset:thumbnail + targetSize:thumbnailSize + contentMode:PHImageContentModeAspectFit + options:options + resultHandler:^(UIImage *result, NSDictionary *info) { + + if (!albumInfo[@"image"]) { + albumInfo[@"image"] = [UIImageJPEGRepresentation(result, 1.0) base64Encoding]; + } + + if (block) { + block(albumInfo); + } + }]; + } +} + + +-(void)extractCollectionsDetails:(PHFetchResult*)collections + imageRequestOptions:(PHImageRequestOptions*)options + thumbnailSize:(CGSize)thumbnailSize + block:(AlbumsBlock)block { + + NSMutableDictionary *albumsDict = [[NSMutableDictionary alloc] init]; + NSInteger collectionCount = collections.count; + + [collections enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL * _Nonnull stop) { + + [self extractCollection:collection imageRequestOptions:options thumbnailSize:thumbnailSize block:^(NSDictionary *album) { + + NSString *albumName = collection.localizedTitle; + if (album) { + albumsDict[albumName] = album; + } + + if (idx == collectionCount-1) { + if (block) { + block(albumsDict); + } + } + }]; + }]; +} + + +RCT_EXPORT_METHOD(getAlbumsWithThumbnails:(RCTPromiseResolveBlock)resolve + reject:(__unused RCTPromiseRejectBlock)reject) { + + PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init]; + imageRequestOptions.resizeMode = PHImageRequestOptionsResizeModeExact; + imageRequestOptions.synchronous = YES; NSInteger retinaScale = [UIScreen mainScreen].scale; CGSize retinaSquare = CGSizeMake(100*retinaScale, 100*retinaScale); NSMutableDictionary *albumsDict = [[NSMutableDictionary alloc] init]; - NSInteger smartAlbumsCount = self.smartAlbums.count; - - - - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - - if (smartAlbumsCount) { - dispatch_semaphore_t sem = dispatch_semaphore_create(0); - - [self.smartAlbums enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL * _Nonnull stop) { - - NSInteger collectionCount = [PHAsset fetchAssetsInAssetCollection:collection options:nil].count; - - if (collectionCount > 0){ - - NSString *albumName = collection.localizedTitle; - albumName = [NSString stringWithFormat:@"%@", albumName]; - - PHFetchResult *fetchResult = [PHAsset fetchKeyAssetsInAssetCollection:collection options:nil]; - PHAsset *thumbnail = [fetchResult firstObject]; - - NSMutableDictionary *albumInfo = [[NSMutableDictionary alloc] init]; - albumInfo[@"albumName"] = albumName; - albumInfo[@"imagesCount"] = [NSNumber numberWithInteger:collectionCount]; - - albumsDict[albumName] = albumInfo; - - [[PHImageManager defaultManager] - requestImageForAsset:thumbnail - targetSize:retinaSquare - contentMode:PHImageContentModeAspectFit - options:cropToSquare - resultHandler:^(UIImage *result, NSDictionary *info) { - - if (!albumInfo[@"image"]) { - - albumInfo[@"image"] = [UIImageJPEGRepresentation(result, 1.0) base64Encoding]; - } - - if (idx == smartAlbumsCount-1) { - - dispatch_semaphore_signal(sem); - - } - - }]; - } - else if (idx == smartAlbumsCount-1) { - dispatch_semaphore_signal(sem); - } - - }]; - dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); - } - - - - NSInteger topLevelAlbumsCount = self.topLevelUserCollections.count; - - [self.topLevelUserCollections enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL * _Nonnull stop) { - NSInteger collectionCount = [PHAsset fetchAssetsInAssetCollection:collection options:nil].count; - - if (collectionCount > 0){ - - NSString *albumName = collection.localizedTitle; - albumName = [NSString stringWithFormat:@"%@", albumName]; - - PHFetchResult *fetchResult = [PHAsset fetchKeyAssetsInAssetCollection:collection options:nil]; - PHAsset *thumbnail = [fetchResult firstObject]; - - NSMutableDictionary *albumInfo = [[NSMutableDictionary alloc] init]; - albumInfo[@"albumName"] = [NSString stringWithFormat:@"%@", albumName]; - albumInfo[@"imagesCount"] = [NSNumber numberWithInteger:collectionCount]; - - albumsDict[albumName] = albumInfo; - - - // __block BOOL isInvokeBlock = NO; - - [[PHImageManager defaultManager] - requestImageForAsset:thumbnail - targetSize:retinaSquare - contentMode:PHImageContentModeAspectFit - options:cropToSquare - resultHandler:^(UIImage *result, NSDictionary *info) { - - if (!albumInfo[@"image"]) { - albumInfo[@"image"] = [UIImageJPEGRepresentation(result, 1.0) base64Encoding]; - } - - if (idx == topLevelAlbumsCount-1) { - -// if (block) { -// -// block(albumsDict); -// } - } - }]; - } - }]; - - NSInteger allPhotosCount = self.allPhotos.count; - -// NSInteger collectionCount = [PHAsset fetchAssetsInAssetCollection:collection options:nil].count; - - if (allPhotosCount > 0){ - - NSString *albumName = @"All Photos"; - albumName = [NSString stringWithFormat:@"%@", albumName]; - -// PHFetchResult *fetchResult = [PHAsset fetchKeyAssetsInAssetCollection:self.allPhotos options:nil]; - PHAsset *thumbnail = [self.allPhotos firstObject]; - - NSMutableDictionary *albumInfo = [[NSMutableDictionary alloc] init]; - albumInfo[@"albumName"] = [NSString stringWithFormat:@"%@", albumName]; - albumInfo[@"imagesCount"] = [NSNumber numberWithInteger:allPhotosCount]; - - albumsDict[albumName] = albumInfo; - - - // __block BOOL isInvokeBlock = NO; - - [[PHImageManager defaultManager] - requestImageForAsset:thumbnail - targetSize:retinaSquare - contentMode:PHImageContentModeAspectFit - options:cropToSquare - resultHandler:^(UIImage *result, NSDictionary *info) { - - if (!albumInfo[@"image"]) { - albumInfo[@"image"] = [UIImageJPEGRepresentation(result, 1.0) base64Encoding]; - } - -// if (idx == topLevelAlbumsCount-1) { - - if (block) { - - block(albumsDict); - } -// } - }]; - } - - }); + [self extractCollectionsDetails:self.topLevelUserCollections + imageRequestOptions:imageRequestOptions + thumbnailSize:retinaSquare + block:^(NSDictionary *albums) { + + if (albums) { + [albumsDict addEntriesFromDictionary:albums]; + } + + [self extractCollection:self.allPhotos imageRequestOptions:imageRequestOptions thumbnailSize:retinaSquare block:^(NSDictionary *album) { + + if (album) { + albumsDict[album[@"albumName"]] = album; + } + + if (resolve) { + NSDictionary *ans = @{[NSString stringWithFormat:@"albums"]: albumsDict}; + resolve(ans); + } + }]; + }]; } --(void)getThumbnial:(PHFetchResult*)albums - albumName:(NSString*)albumName - cropToSquare:(PHImageRequestOptions*)cropToSquare - retinaSquare:(CGSize)retinaSquare block:(CallbackGalleryBlock)block { - - [albums enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL *stop) { - - if ([albumName isEqualToString:collection.localizedTitle]) { - *stop = YES; - - PHFetchResult *fetchResult = [PHAsset fetchKeyAssetsInAssetCollection:collection options:nil]; - PHAsset *asset = [fetchResult firstObject]; - - CGFloat cropSideLength = MIN(asset.pixelWidth, asset.pixelHeight); - CGRect square = CGRectMake(0, 0, cropSideLength, cropSideLength); - CGRect cropRect = CGRectApplyAffineTransform(square, - CGAffineTransformMakeScale(1.0 / asset.pixelWidth, - 1.0 / asset.pixelHeight)); - - - // make sure resolve call only once - __block BOOL isInvokeResolve = NO; - - [[PHImageManager defaultManager] - requestImageForAsset:asset - targetSize:retinaSquare - contentMode:PHImageContentModeAspectFit - options:cropToSquare - resultHandler:^(UIImage *result, NSDictionary *info) { - - - NSData *imageData = [UIImagePNGRepresentation(result) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; - - - if (!imageData) { - imageData = UIImagePNGRepresentation(result); - - if (!imageData) { - CGDataProviderRef provider = CGImageGetDataProvider(result.CGImage); - imageData = (id)CFBridgingRelease(CGDataProviderCopyData(provider)); - } - } - - - NSString *encodedString = [imageData base64Encoding]; - - - // NSString *encodedString = [data base64Encoding]; - - - - if (block && !isInvokeResolve) { - isInvokeResolve = YES; - block(YES, encodedString); - } - - }]; - } - else { - if (block) { - block(NO, nil); - } - } - }]; -} - - - -RCT_EXPORT_METHOD(getAlbumsWithThumbnails:(RCTPromiseResolveBlock)resolve - reject:(__unused RCTPromiseRejectBlock)reject) -{ - - [self getAllAlbumsNameAndThumbnails:^(NSDictionary *albums) { - if (resolve) { - - NSDictionary *ans = @{[NSString stringWithFormat:@"albums"]: albums}; - resolve(ans); - } - }]; -} - - -RCT_EXPORT_METHOD(getImagesForAlbumName:(NSInteger)numberOfImages - albumName:(NSString*)albumName - resolve:(RCTPromiseResolveBlock)resolve - reject:(__unused RCTPromiseRejectBlock)reject) -{ - // [self.imageManager requestImageForAsset:asset - // targetSize:AssetGridThumbnailSize - // contentMode:PHImageContentModeAspectFill - // options:nil - // resultHandler:^(UIImage *result, NSDictionary *info) { - // // Set the cell's thumbnail image if it's still showing the same asset. - // if ([cell.representedAssetIdentifier isEqualToString:asset.localIdentifier]) { - // cell.thumbnailImage = result; - // } - // }]; - -} - - - @end diff --git a/src/CameraKitGallery.js b/src/CameraKitGallery.js index 9b2d170..8f3d315 100644 --- a/src/CameraKitGallery.js +++ b/src/CameraKitGallery.js @@ -19,7 +19,7 @@ async function getThumbnailForAlbumName(albumName) { function getPhotosForAlbum(albumName, numberOfPhotos, callback, error) { - let groupType = (albumName === 'Camera Roll') ? 'SavedPhotos' : 'All'; + let groupType = (albumName === 'All Photos') ? 'SavedPhotos' : 'All'; //const photoStream = ['Bursts', 'Recently Added', 'Selfies', 'Recently Added', 'Screenshots', 'My Photo Stream']; //if (_.include(photoStream, albumName)) { // groupType = 'PhotoStream'; @@ -29,7 +29,7 @@ function getPhotosForAlbum(albumName, numberOfPhotos, callback, error) { const fetchParams = { first: numberOfPhotos, - groupName: albumName, + //groupName: albumName, groupTypes: groupType, }; CameraRoll.getPhotos(fetchParams)