428 lines
16 KiB
Objective-C
428 lines
16 KiB
Objective-C
//
|
|
// CKGallery.m
|
|
// ReactNativeCameraKit
|
|
//
|
|
// Created by Ran Greenberg on 30/05/2016.
|
|
// Copyright © 2016 Wix. All rights reserved.
|
|
//
|
|
|
|
#if __has_include(<React/RCTBridge.h>)
|
|
#import <React/RCTConvert.h>
|
|
#else
|
|
#import "RCTConvert.h"
|
|
#endif
|
|
|
|
#import "CKGalleryManager.h"
|
|
#import "CKGalleryViewManager.h"
|
|
#import "CKCompressedImage.h"
|
|
|
|
typedef void (^AlbumsBlock)(NSDictionary *albums);
|
|
|
|
@interface CKGalleryManager ()
|
|
|
|
@property (nonatomic, strong) PHFetchResult *allPhotos;
|
|
@property (nonatomic, strong) PHFetchResult *smartAlbums;
|
|
@property (nonatomic, strong) PHFetchResult *topLevelUserCollections;
|
|
@property (nonatomic, strong) PHFetchOptions *fetchOptions;
|
|
|
|
@end
|
|
|
|
|
|
@implementation CKGalleryManager
|
|
|
|
|
|
RCT_EXPORT_MODULE();
|
|
|
|
|
|
#pragma mark - lazy loading methods
|
|
|
|
|
|
-(PHFetchOptions*)fetchOptions {
|
|
if (!_fetchOptions) {
|
|
_fetchOptions = [[PHFetchOptions alloc] init];
|
|
_fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
|
|
}
|
|
return _fetchOptions;
|
|
}
|
|
|
|
|
|
-(PHFetchResult *)allPhotos {
|
|
if (!_allPhotos) {
|
|
_allPhotos = [PHAsset fetchAssetsWithOptions:self.fetchOptions];
|
|
}
|
|
return _allPhotos;
|
|
}
|
|
|
|
|
|
-(PHFetchResult *)topLevelUserCollections {
|
|
if (!_topLevelUserCollections) {
|
|
_topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
|
|
}
|
|
return _topLevelUserCollections;
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
-(void)extractCollection:(id)collection
|
|
imageRequestOptions:(PHImageRequestOptions*)options
|
|
thumbnailSize:(CGSize)thumbnailSize
|
|
block:(AlbumsBlock)block {
|
|
|
|
NSInteger collectionCount;
|
|
if ([collection isKindOfClass:[PHAssetCollection class]]) {
|
|
collectionCount = [PHAsset fetchAssetsInAssetCollection:collection options:nil].count;
|
|
}
|
|
else if ([collection isKindOfClass:[PHFetchResult class]]){
|
|
collectionCount = ((PHFetchResult*)collection).count;
|
|
}
|
|
else {
|
|
collectionCount = 0;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}];
|
|
}
|
|
|
|
else {
|
|
if (block) {
|
|
block(nil);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-(void)extractCollectionsDetails:(PHFetchResult*)collections
|
|
imageRequestOptions:(PHImageRequestOptions*)options
|
|
thumbnailSize:(CGSize)thumbnailSize
|
|
block:(AlbumsBlock)block {
|
|
|
|
NSMutableArray *albumsArray = [[NSMutableArray alloc] init];
|
|
NSInteger collectionCount = collections.count;
|
|
|
|
if (collectionCount == 0) {
|
|
if (block) {
|
|
block(nil);
|
|
}
|
|
}
|
|
|
|
[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) {
|
|
[albumsArray addObject:album];
|
|
}
|
|
|
|
if (idx == collectionCount-1) {
|
|
if (block) {
|
|
block(@{@"albums" : albumsArray});
|
|
}
|
|
}
|
|
}];
|
|
}];
|
|
}
|
|
|
|
|
|
RCT_EXPORT_METHOD(getAlbumsWithThumbnails:(RCTPromiseResolveBlock)resolve
|
|
reject:(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);
|
|
|
|
__block NSMutableArray *albumsArray = [[NSMutableArray alloc] init];
|
|
|
|
[self extractCollectionsDetails:self.topLevelUserCollections
|
|
imageRequestOptions:imageRequestOptions
|
|
thumbnailSize:retinaSquare
|
|
block:^(NSDictionary *albums) {
|
|
|
|
[self extractCollection:self.allPhotos imageRequestOptions:imageRequestOptions thumbnailSize:retinaSquare block:^(NSDictionary *allPhotosAlbum) {
|
|
|
|
|
|
if (resolve) {
|
|
NSMutableArray *albumsArrayAns = [[NSMutableArray alloc] init];;
|
|
|
|
if(albums[@"albums"]) {
|
|
[albumsArrayAns addObjectsFromArray:albums[@"albums"]];
|
|
}
|
|
if(allPhotosAlbum) {
|
|
[albumsArrayAns insertObject:allPhotosAlbum atIndex:0];
|
|
}
|
|
|
|
if (!albumsArrayAns || albumsArrayAns.count == 0) {
|
|
NSError *error = [[NSError alloc] initWithDomain:NSCocoaErrorDomain
|
|
code:-100 userInfo:nil];
|
|
|
|
reject(@"-100", @"no albums", error);
|
|
}
|
|
else {
|
|
if (resolve) {
|
|
NSDictionary *ans = @{@"albums": albumsArrayAns };
|
|
resolve(ans);
|
|
}
|
|
}
|
|
}
|
|
}];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(getImagesForIds:(NSArray*)imagesIdArray
|
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
reject:(__unused RCTPromiseRejectBlock)reject) {
|
|
[self imagesForIds:imagesIdArray imageQuality:nil resolve:resolve reject:reject];
|
|
}
|
|
|
|
|
|
RCT_EXPORT_METHOD(getImagesForIds:(NSArray*)imagesIdArray
|
|
imageQuality:(NSString*)imageQuality
|
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
reject:(__unused RCTPromiseRejectBlock)reject) {
|
|
|
|
[self imagesForIds:imagesIdArray imageQuality:imageQuality resolve:resolve reject:reject];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(resizeImage:(NSDictionary*)image
|
|
quality:(NSString*)quality
|
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
reject:(__unused RCTPromiseRejectBlock)reject) {
|
|
|
|
NSMutableDictionary *ans = [NSMutableDictionary dictionaryWithDictionary:image];
|
|
|
|
NSString *imageUrlString = image[@"uri"];
|
|
if (imageUrlString) {
|
|
|
|
NSURL *url = [NSURL URLWithString:imageUrlString];
|
|
if (!url) {
|
|
resolve(nil);
|
|
}
|
|
NSData *data = [NSData dataWithContentsOfURL:url];
|
|
|
|
UIImage *originalImage = [UIImage imageWithData:data];
|
|
CKCompressedImage *compressedImage = [[CKCompressedImage alloc] initWithImage:originalImage imageQuality:quality];
|
|
|
|
|
|
NSURL *temporaryFileURL = [CKCamera saveToTmpFolder:compressedImage.data];
|
|
if (temporaryFileURL) {
|
|
ans[@"uri"] = temporaryFileURL.description;
|
|
ans[@"name"] = temporaryFileURL.lastPathComponent;
|
|
ans[@"size"] = @(compressedImage.data.length);
|
|
ans[@"width"] = @(compressedImage.image.size.width);
|
|
ans[@"height"] = @(compressedImage.image.size.height);
|
|
}
|
|
}
|
|
resolve(ans);
|
|
}
|
|
|
|
|
|
-(void)imagesForIds:(NSArray*)imagesIdArray
|
|
imageQuality:(NSString*)imageQuality
|
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
reject:(__unused RCTPromiseRejectBlock)reject {
|
|
|
|
if (!imagesIdArray || ![imagesIdArray isKindOfClass:[NSArray class]] || imagesIdArray.count <= 0 ) {
|
|
resolve(@{@"images": @[]});
|
|
return;
|
|
}
|
|
|
|
NSMutableArray *assetsArray = [[NSMutableArray alloc] initWithArray:imagesIdArray];
|
|
|
|
PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
|
|
imageRequestOptions.synchronous = YES;
|
|
|
|
PHFetchResult *assets = [PHAsset fetchAssetsWithLocalIdentifiers:imagesIdArray options:nil];
|
|
|
|
for (PHAsset *asset in assets) {
|
|
|
|
NSDictionary *assetInfoDict = [CKGalleryViewManager infoForAsset:asset imageRequestOptions:imageRequestOptions imageQuality:imageQuality];
|
|
NSString *assetLocalId = asset.localIdentifier;
|
|
|
|
if (assetInfoDict && assetInfoDict[@"uri"] && assetInfoDict[@"size"] && assetInfoDict[@"name"] && assetLocalId) {
|
|
|
|
NSUInteger originalArrayIndex = [imagesIdArray indexOfObject:assetLocalId];
|
|
|
|
[assetsArray replaceObjectAtIndex:originalArrayIndex withObject:@{@"uri": assetInfoDict[@"uri"],
|
|
@"width": assetInfoDict[@"width"],
|
|
@"height": assetInfoDict[@"height"],
|
|
@"size": assetInfoDict[@"size"],
|
|
@"name": assetInfoDict[@"name"],
|
|
@"id": assetLocalId}];
|
|
}
|
|
}
|
|
|
|
NSMutableArray *resolveArray = [NSMutableArray new];
|
|
for (id obj in assetsArray) {
|
|
if ([obj isKindOfClass:[NSDictionary class]]) {
|
|
[resolveArray addObject:obj];
|
|
}
|
|
}
|
|
|
|
if (resolve) {
|
|
resolve(@{@"images": resolveArray});
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
RCT_EXPORT_METHOD(checkDevicePhotosAuthorizationStatus:(RCTPromiseResolveBlock)resolve
|
|
reject:(__unused RCTPromiseRejectBlock)reject) {
|
|
NSNumber *status = [CKGalleryManager checkDeviceGalleryAuthorizationStatus];
|
|
if (resolve) {
|
|
resolve(status);
|
|
}
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(requestDevicePhotosAuthorization:(RCTPromiseResolveBlock)resolve
|
|
reject:(__unused RCTPromiseRejectBlock)reject) {
|
|
[CKGalleryManager requestDeviceGalleryAuthorization:^(BOOL isAuthorized) {
|
|
if (resolve) {
|
|
resolve(@(isAuthorized));
|
|
}
|
|
}];
|
|
}
|
|
|
|
|
|
+(void)requestDeviceGalleryAuthorization:(CallbackGalleryAuthorizationStatus)callback {
|
|
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
|
|
if (callback) {
|
|
switch (status) {
|
|
case PHAuthorizationStatusAuthorized:
|
|
callback(true);
|
|
break;
|
|
case PHAuthorizationStatusRestricted:
|
|
callback(false);
|
|
break;
|
|
case PHAuthorizationStatusDenied:
|
|
callback(false);
|
|
break;
|
|
default:
|
|
callback(false);
|
|
break;
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
+(NSNumber*)checkDeviceGalleryAuthorizationStatus {
|
|
|
|
PHAuthorizationStatus authorizationStatus = [PHPhotoLibrary authorizationStatus];
|
|
|
|
if (authorizationStatus == PHAuthorizationStatusAuthorized) {
|
|
return @YES;
|
|
}
|
|
else if (authorizationStatus == PHAuthorizationStatusNotDetermined) {
|
|
return @(-1);
|
|
}
|
|
return @NO;
|
|
}
|
|
|
|
+(NSString*)getImageLocalIdentifierForFetchOptions:(PHFetchOptions*)fetchOption {
|
|
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOption];
|
|
PHAsset *lastImageAsset = [fetchResult firstObject];
|
|
return lastImageAsset.localIdentifier;
|
|
}
|
|
|
|
+(void)saveImageToCameraRoll:(NSData*)imageData temporaryFileURL:(NSURL*)temporaryFileURL block:(SaveBlock)block {
|
|
// To preserve the metadata, we create an asset from the JPEG NSData representation.
|
|
// Note that creating an asset from a UIImage discards the metadata.
|
|
// In iOS 9, we can use -[PHAssetCreationRequest addResourceWithType:data:options].
|
|
// In iOS 8, we save the image to a temporary file and use +[PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:].
|
|
if ( [PHAssetCreationRequest class] && imageData) {
|
|
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
|
[[PHAssetCreationRequest creationRequestForAsset] addResourceWithType:PHAssetResourceTypePhoto data:imageData options:nil];
|
|
} completionHandler:^( BOOL success, NSError *error ) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if ( block ) {
|
|
block(success);
|
|
}
|
|
});
|
|
|
|
}];
|
|
}
|
|
else {
|
|
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
|
[PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:temporaryFileURL];
|
|
} completionHandler:^( BOOL success, NSError *error ) {
|
|
if ( block ) {
|
|
block(success);
|
|
}
|
|
}];
|
|
}
|
|
}
|
|
|
|
+(void)saveImageURLToCameraRoll:(NSString*)temporaryFileURL block:(SaveBlock)block {
|
|
NSURL *imageURL = [NSURL URLWithString:temporaryFileURL];
|
|
if(!imageURL) {
|
|
block(NO);
|
|
return;
|
|
}
|
|
[CKGalleryManager saveImageToCameraRoll:nil temporaryFileURL:imageURL block:block];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(saveImageURLToCameraRoll:(NSString*)imageURL
|
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
[CKGalleryManager saveImageURLToCameraRoll:imageURL block:^(BOOL success) {
|
|
if (resolve) {
|
|
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:@{@"success": @(success)}];
|
|
NSString *localIdentifier = [CKGalleryManager getImageLocalIdentifierForFetchOptions:self.fetchOptions];
|
|
if(localIdentifier) {
|
|
result[@"id"] = localIdentifier;
|
|
}
|
|
resolve(result);
|
|
}
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(deleteTempImage:(NSString*)tempImageURL
|
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
NSError *error;
|
|
NSFileManager *defaultFileManager = [NSFileManager defaultManager];
|
|
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:@{@"success": @(YES)}];
|
|
tempImageURL = [tempImageURL stringByReplacingOccurrencesOfString:@"file://" withString:@""];
|
|
if([defaultFileManager fileExistsAtPath:tempImageURL]) {
|
|
BOOL success = [[NSFileManager defaultManager] removeItemAtPath:tempImageURL error:&error];
|
|
result[@"success"] = @(success);
|
|
if(error) {
|
|
result[@"error"] = [error description];
|
|
}
|
|
}
|
|
|
|
if(resolve) {
|
|
resolve(result);
|
|
}
|
|
}
|
|
|
|
|
|
@end
|