react-native-camera-kit/ios/lib/ReactNativeCameraKit/CKGalleryManager.m

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