diff --git a/index.js b/index.js index c9f6806..f87fb9b 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,11 @@ import CameraKitGallery from './src/CameraKitGallery'; import CameraKitCamera from './src/CameraKitCamera'; +import CameraKitGalleryView from './src/CameraKitGalleryView'; export { CameraKitGallery, CameraKitCamera, + CameraKitGalleryView }; diff --git a/ios/lib/ReactNativeCameraKit.xcodeproj/project.pbxproj b/ios/lib/ReactNativeCameraKit.xcodeproj/project.pbxproj index 4742a13..678eb4d 100644 --- a/ios/lib/ReactNativeCameraKit.xcodeproj/project.pbxproj +++ b/ios/lib/ReactNativeCameraKit.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 262E421D1D182C1200C82B27 /* CKGalleryViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 262E421C1D182C1200C82B27 /* CKGalleryViewManager.m */; }; + 262E42201D183A6B00C82B27 /* CKGalleryCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 262E421F1D183A6B00C82B27 /* CKGalleryCollectionViewCell.m */; }; 26550AE61CFC2437007FF2DF /* CKGalleryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 26550AE51CFC2437007FF2DF /* CKGalleryManager.m */; }; 26550AF61CFC7086007FF2DF /* CKCameraManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 26550AF51CFC7086007FF2DF /* CKCameraManager.m */; }; 2685AA241CFD89A300E4A446 /* CKCamera.m in Sources */ = {isa = PBXBuildFile; fileRef = 2685AA231CFD89A300E4A446 /* CKCamera.m */; }; @@ -25,6 +27,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 262E421B1D182C1200C82B27 /* CKGalleryViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKGalleryViewManager.h; sourceTree = ""; }; + 262E421C1D182C1200C82B27 /* CKGalleryViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKGalleryViewManager.m; sourceTree = ""; }; + 262E421E1D183A6B00C82B27 /* CKGalleryCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKGalleryCollectionViewCell.h; sourceTree = ""; }; + 262E421F1D183A6B00C82B27 /* CKGalleryCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKGalleryCollectionViewCell.m; sourceTree = ""; }; 2646934E1CFB2A6B00F3A740 /* libReactNativeCameraKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libReactNativeCameraKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; 26550AE41CFC2437007FF2DF /* CKGalleryManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKGalleryManager.h; sourceTree = ""; }; 26550AE51CFC2437007FF2DF /* CKGalleryManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKGalleryManager.m; sourceTree = ""; }; @@ -70,6 +76,10 @@ 26550AF51CFC7086007FF2DF /* CKCameraManager.m */, 2685AA221CFD89A300E4A446 /* CKCamera.h */, 2685AA231CFD89A300E4A446 /* CKCamera.m */, + 262E421B1D182C1200C82B27 /* CKGalleryViewManager.h */, + 262E421C1D182C1200C82B27 /* CKGalleryViewManager.m */, + 262E421E1D183A6B00C82B27 /* CKGalleryCollectionViewCell.h */, + 262E421F1D183A6B00C82B27 /* CKGalleryCollectionViewCell.m */, ); path = ReactNativeCameraKit; sourceTree = ""; @@ -131,8 +141,10 @@ buildActionMask = 2147483647; files = ( 26550AF61CFC7086007FF2DF /* CKCameraManager.m in Sources */, + 262E42201D183A6B00C82B27 /* CKGalleryCollectionViewCell.m in Sources */, 26550AE61CFC2437007FF2DF /* CKGalleryManager.m in Sources */, 2685AA241CFD89A300E4A446 /* CKCamera.m in Sources */, + 262E421D1D182C1200C82B27 /* CKGalleryViewManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/lib/ReactNativeCameraKit/CKGalleryCollectionViewCell.h b/ios/lib/ReactNativeCameraKit/CKGalleryCollectionViewCell.h new file mode 100644 index 0000000..4f6b18f --- /dev/null +++ b/ios/lib/ReactNativeCameraKit/CKGalleryCollectionViewCell.h @@ -0,0 +1,18 @@ +// +// CKGalleryCollectionViewCell.h +// ReactNativeCameraKit +// +// Created by Ran Greenberg on 20/06/2016. +// Copyright © 2016 Wix. All rights reserved. +// + +#import + +@interface CKGalleryCollectionViewCell : UICollectionViewCell + +@property (nonatomic, strong) UIImage *thumbnailImage; +@property (nonatomic, copy) NSString *representedAssetIdentifier; + +@property (nonatomic) BOOL isSelected; + +@end diff --git a/ios/lib/ReactNativeCameraKit/CKGalleryCollectionViewCell.m b/ios/lib/ReactNativeCameraKit/CKGalleryCollectionViewCell.m new file mode 100644 index 0000000..127d2b2 --- /dev/null +++ b/ios/lib/ReactNativeCameraKit/CKGalleryCollectionViewCell.m @@ -0,0 +1,61 @@ +// +// CKGalleryCollectionViewCell.m +// ReactNativeCameraKit +// +// Created by Ran Greenberg on 20/06/2016. +// Copyright © 2016 Wix. All rights reserved. +// + +#import "CKGalleryCollectionViewCell.h" + +@interface CKGalleryCollectionViewCell () + +@property (strong, nonatomic) UIImageView *imageView; + +@end + +@implementation CKGalleryCollectionViewCell + +-(instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + + CGRect imageViewFrame = self.bounds; + imageViewFrame.size.width *= 0.97; + imageViewFrame.size.height *= 0.97; + + self.imageView = [[UIImageView alloc] initWithFrame:imageViewFrame]; + self.imageView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + + self.imageView.backgroundColor = [UIColor clearColor]; + [self addSubview:self.imageView]; + + return self; +} + +- (void)prepareForReuse { + [super prepareForReuse]; + self.imageView.image = nil; + self.isSelected = NO; + +} + +- (void)setThumbnailImage:(UIImage *)thumbnailImage { + _thumbnailImage = thumbnailImage; + self.imageView.image = thumbnailImage; +} + +-(void)setIsSelected:(BOOL)isSelected { + + _isSelected = isSelected; + if (_isSelected) { + self.backgroundColor = [UIColor blueColor]; + } + else { + self.backgroundColor = [UIColor clearColor]; + } + + + +} + +@end diff --git a/ios/lib/ReactNativeCameraKit/CKGalleryManager.m b/ios/lib/ReactNativeCameraKit/CKGalleryManager.m index 5b68bfa..77a1328 100644 --- a/ios/lib/ReactNativeCameraKit/CKGalleryManager.m +++ b/ios/lib/ReactNativeCameraKit/CKGalleryManager.m @@ -83,6 +83,12 @@ RCT_EXPORT_MODULE(); } }]; } + + else { + if (block) { + block(nil); + } + } } diff --git a/ios/lib/ReactNativeCameraKit/CKGalleryViewManager.h b/ios/lib/ReactNativeCameraKit/CKGalleryViewManager.h new file mode 100644 index 0000000..5478010 --- /dev/null +++ b/ios/lib/ReactNativeCameraKit/CKGalleryViewManager.h @@ -0,0 +1,21 @@ +// +// CKGalleryViewManager.h +// ReactNativeCameraKit +// +// Created by Ran Greenberg on 20/06/2016. +// Copyright © 2016 Wix. All rights reserved. +// + +#import + + +@import AVFoundation; +#import "RCTViewManager.h" +#import "RCTConvert.h" + + + +@interface CKGalleryViewManager : RCTViewManager + + +@end diff --git a/ios/lib/ReactNativeCameraKit/CKGalleryViewManager.m b/ios/lib/ReactNativeCameraKit/CKGalleryViewManager.m new file mode 100644 index 0000000..8616f14 --- /dev/null +++ b/ios/lib/ReactNativeCameraKit/CKGalleryViewManager.m @@ -0,0 +1,274 @@ +// +// CKGalleryViewManager.m +// ReactNativeCameraKit +// +// Created by Ran Greenberg on 20/06/2016. +// Copyright © 2016 Wix. All rights reserved. +// + +@import Photos; +#import "CKGalleryViewManager.h" +#import "CKGalleryCollectionViewCell.h" +#import "UIView+React.h" + +#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width) +#define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height) + +@interface CKGalleryView : UIView + +@property (nonatomic, strong) NSString *albumName; +@property (nonatomic, strong) UICollectionView *collectionView; +@property (nonatomic, strong) PHFetchResult *galleryFetchResults; +@property (nonatomic, strong) PHFetchResult *assetsCollection; + +@property (nonatomic, strong) PHCachingImageManager *imageManager; + +@property (nonatomic) CGSize cellSize; +@property (nonatomic, strong) NSMutableArray *selectedAssets; + + +@end + +static NSString * const CellReuseIdentifier = @"Cell"; + +@implementation CKGalleryView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + + + self.selectedAssets = [[NSMutableArray alloc] init]; + self.imageManager = [[PHCachingImageManager alloc] init]; + + PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init]; + allPhotosOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]]; + + PHFetchOptions *albumsOptions = [[PHFetchOptions alloc] init]; + albumsOptions.predicate = [NSPredicate predicateWithFormat:@"estimatedAssetCount > 0"]; + + + return self; +} + +-(CGSize)cellSize { + if (CGSizeEqualToSize(_cellSize, CGSizeZero)) { + CGFloat minSize = (MIN(SCREEN_WIDTH, SCREEN_HEIGHT) * 1.00)/3; + _cellSize = CGSizeMake(minSize, minSize); + } + return _cellSize; +} + + +-(void)reactSetFrame:(CGRect)frame { + [super reactSetFrame:frame]; + + if (!self.collectionView) { + + UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc] init]; + flowLayout.itemSize = self.cellSize; //TODO remve this, get it from the JS + [flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical]; + + + + self.collectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:flowLayout]; + self.collectionView.delegate = self; + self.collectionView.dataSource = self; + + [self.collectionView registerClass:[CKGalleryCollectionViewCell class] forCellWithReuseIdentifier:CellReuseIdentifier]; + [self addSubview:self.collectionView]; + self.collectionView.backgroundColor = [UIColor whiteColor]; + + } +} + + + +#pragma mark Collection view layout things + + +- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { + + return self.cellSize; +} + +- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { + return 0.0; +} + +- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { + return 0.0; +} + +// Layout: Set Edges +- (UIEdgeInsets)collectionView: +(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { + // return UIEdgeInsetsMake(0,8,0,8); // top, left, bottom, right + return UIEdgeInsetsMake(0,0,0,0); // top, left, bottom, right +} + + +-(void)setAlbumName:(NSString *)albumName { + + PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init]; + allPhotosOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]]; + + if ([albumName isEqualToString:@"All photos"]) { + self.assetsCollection = [PHAsset fetchAssetsWithOptions:allPhotosOptions]; + [self.collectionView reloadData]; + return; + } + + + PHFetchResult *collections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil]; + + [collections enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL * _Nonnull stop) { + + if ([collection.localizedTitle isEqualToString:albumName]) { + + self.assetsCollection = [PHAsset fetchAssetsInAssetCollection:collection options:nil];; + [self.collectionView reloadData]; + } + }]; + + [self.collectionView reloadData]; +} + + +#pragma mark - UICollectionViewDataSource + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { + return self.assetsCollection.count; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { + PHAsset *asset = self.assetsCollection[indexPath.row]; + + CKGalleryCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellReuseIdentifier forIndexPath:indexPath]; + cell.representedAssetIdentifier = asset.localIdentifier; + + [self.imageManager requestImageForAsset:asset + targetSize:CGSizeMake(self.cellSize.width*0.95, self.cellSize.height*0.95) + contentMode:PHImageContentModeDefault + 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; + } + }]; + + + return cell; +} + + +#pragma mark - UICollectionViewDelegate + +- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { + + id selectedCell =[collectionView cellForItemAtIndexPath:indexPath]; + PHAsset *asset = self.assetsCollection[indexPath.row]; + + if ([selectedCell isKindOfClass:[CKGalleryCollectionViewCell class]]) { + CKGalleryCollectionViewCell *ckCell = (CKGalleryCollectionViewCell*)selectedCell; + ckCell.isSelected = !ckCell.isSelected; + + [self.selectedAssets removeObject:asset]; + + + if (ckCell.isSelected) { + if (asset) { + [self.selectedAssets addObject:asset]; + } + } + + // [self.imageManager requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) { + // + // self.selectedImagesUrls addObject: + // + // + // }]; + + // [asset requestContentEditingInputWithOptions:nil + // completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) + // { + // [self.selectedImagesUrls removeObject:contentEditingInput.fullSizeImageURL.description]; + // + // + // if (ckCell.isSelected) { + // [self.selectedImagesUrls addObject:contentEditingInput.fullSizeImageURL.absoluteString]; + // } + // }]; + + } + +} + +- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { + +} + +@end + +@interface CKGalleryViewManager () + +@property (nonatomic, strong) CKGalleryView *galleryView; + +@end + + +@implementation CKGalleryViewManager + + +RCT_EXPORT_MODULE() + +- (UIView *)view +{ + self.galleryView = [[CKGalleryView alloc] init]; + return self.galleryView; +} + + +RCT_EXPORT_VIEW_PROPERTY(albumName, NSString); + +RCT_EXPORT_METHOD(getSelectedImages:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + + NSError *error = nil; + NSURL *directoryURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]] isDirectory:YES]; + [[NSFileManager defaultManager] createDirectoryAtURL:directoryURL withIntermediateDirectories:YES attributes:nil error:&error]; + + NSMutableArray *assetsUrls = [[NSMutableArray alloc] init]; + + for (PHAsset *asset in self.galleryView.selectedAssets) { + [self.galleryView.imageManager requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) { + + NSURL *fileURLKey = info[@"PHImageFileURLKey"]; + if (!fileURLKey) { + if (resolve) { + resolve(nil); + } + } + + NSString *fileName = ((NSURL*)info[@"PHImageFileURLKey"]).lastPathComponent; + + NSURL *fileURL = [directoryURL URLByAppendingPathComponent:fileName]; + NSError *error = nil; + [imageData writeToURL:fileURL options:NSDataWritingAtomic error:&error]; + + [assetsUrls addObject:fileURL.absoluteString]; + + + if (asset == self.galleryView.selectedAssets.lastObject) { + if (resolve) { + resolve(@{@"selectedImages":assetsUrls}); + } + } + + }]; + } + // + +} + +@end diff --git a/src/CameraKitCamera.js b/src/CameraKitCamera.js index 401bd40..bd16aab 100644 --- a/src/CameraKitCamera.js +++ b/src/CameraKitCamera.js @@ -12,7 +12,7 @@ export default class CameraKitCamera extends React.Component { return } - static async checkDeviceAuthorizarionStatus() { + static async checkDeviceAuthorizarionStatus() { const deviceAutorizationStatus = await NativeCameraAction.checkDeviceAuthorizationStatus(); return deviceAutorizationStatus; diff --git a/src/CameraKitGallery.js b/src/CameraKitGallery.js index 52d6a34..c7eb136 100644 --- a/src/CameraKitGallery.js +++ b/src/CameraKitGallery.js @@ -12,31 +12,26 @@ async function getAlbumsWithThumbnails() { } -async function getThumbnailForAlbumName(albumName) { - const albumsThumbnail = await CKGallery.getThumbnailForAlbumName(albumName); - return albumsThumbnail; -} - - function getPhotosForAlbum(albumName, numberOfPhotos, callback, error) { let groupType = (albumName.toLowerCase() === 'all photos') ? 'SavedPhotos' : 'All'; const fetchParams = { first: numberOfPhotos, - groupTypes: groupType + groupTypes: groupType, + assetType: 'Photos' }; if (albumName.toLowerCase() !== 'all photos') { fetchParams.groupName = albumName; } + CameraRoll.getPhotos(fetchParams) .then((data) => callback(data), (e) => error(e)); } export default { getAlbumsWithThumbnails, - getThumbnailForAlbumName, getPhotosForAlbum } diff --git a/src/CameraKitGalleryView.js b/src/CameraKitGalleryView.js new file mode 100644 index 0000000..6217d85 --- /dev/null +++ b/src/CameraKitGalleryView.js @@ -0,0 +1,23 @@ +import React, {Component} from 'react'; +import { + requireNativeComponent, + NativeModules +} from 'react-native'; + +const GalleryView = requireNativeComponent('CKGalleryView', null); +const GalleryViewManager = NativeModules.CKGalleryViewManager; + +export default class CameraKitGalleryView extends Component { + + render() { + return + } + + async getSelectedImages() { + + const selectedImages = await GalleryViewManager.getSelectedImages(); + return selectedImages; + } + + +}