Updates from Tue Mar 10

- [ReactNative] Make tests run on TravisCI | Alex Kotliarskyi
- [Relay] Update Relay + ES6 class containers | Christoph Pojer
- [React Native] Add RCTAdSupport.xcodeproj | Alexsander Akers
- [ReactNative][Android] Fix after a new React version was downstreamed | Philipp von Weitershausen
- [React Native] Add preliminary animation API | Alex Akers
- [ReactKit] Create test for OSS ReactKit | Alex Kotliarskyi
- [React Native][Device ID][wip] implement most basic js access | Alex Akers
- [ReactNative] OSS Picker | Spencer Ahrens
- [ReactNative] Fix typo in RCTUIManager | Tadeu Zagallo
- [ReactNative] Fix GeoLocation files letter case | Tadeu Zagallo
- Unified the method signature for addUIBlock: to further simplify porting ViewManagers | Nick Lockwood
- [ReactNative] Oss GeoMap | Tadeu Zagallo
- [ReactNative] OSS CameraRoll | Tadeu Zagallo
- [ReactNative] allowLossyConversion on NSString->NSData conversion | Andrew Rasmussen
- [React Native][RFC] Print __DEV__ value on app start | Alex Kotliarskyi
This commit is contained in:
Alex Kotliarskyi
2015-03-10 19:11:28 -07:00
parent 83581cfe6b
commit fab5ec617d
71 changed files with 4353 additions and 82 deletions

View File

@@ -0,0 +1,7 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTBridgeModule.h"
@interface RCTCameraRollManager : NSObject <RCTBridgeModule>
@end

View File

@@ -0,0 +1,148 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTCameraRollManager.h"
#import <AssetsLibrary/AssetsLibrary.h>
#import <CoreLocation/CoreLocation.h>
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "RCTImageLoader.h"
#import "RCTLog.h"
@implementation RCTCameraRollManager
- (void)saveImageWithTag:(NSString *)imageTag successCallback:(RCTResponseSenderBlock)successCallback errorCallback:(RCTResponseSenderBlock)errorCallback
{
RCT_EXPORT();
[RCTImageLoader loadImageWithTag:imageTag callback:^(NSError *loadError, UIImage *loadedImage) {
if (loadError) {
errorCallback(@[[loadError localizedDescription]]);
return;
}
[[RCTImageLoader assetsLibrary] writeImageToSavedPhotosAlbum:[loadedImage CGImage] metadata:nil completionBlock:^(NSURL *assetURL, NSError *saveError) {
if (saveError) {
NSString *errorMessage = [NSString stringWithFormat:@"Error saving cropped image: %@", saveError];
RCTLogWarn(@"%@", errorMessage);
errorCallback(@[errorMessage]);
return;
}
successCallback(@[[assetURL absoluteString]]);
}];
}];
}
- (void)callCallback:(RCTResponseSenderBlock)callback withAssets:(NSArray *)assets hasNextPage:(BOOL)hasNextPage
{
if (![assets count]) {
callback(@[@{
@"edges": assets,
@"page_info": @{
@"has_next_page": @NO}
}]);
return;
}
callback(@[@{
@"edges": assets,
@"page_info": @{
@"start_cursor": assets[0][@"node"][@"image"][@"uri"],
@"end_cursor": assets[assets.count - 1][@"node"][@"image"][@"uri"],
@"has_next_page": @(hasNextPage)}
}]);
}
- (void)getPhotos:(NSDictionary *)params callback:(RCTResponseSenderBlock)callback errorCallback:(RCTResponseSenderBlock)errorCallback
{
RCT_EXPORT();
NSUInteger first = [params[@"first"] integerValue];
NSString *afterCursor = params[@"after"];
NSString *groupTypesStr = params[@"groupTypes"];
NSString *groupName = params[@"groupName"];
ALAssetsGroupType groupTypes;
if ([groupTypesStr isEqualToString:@"Album"]) {
groupTypes = ALAssetsGroupAlbum;
} else if ([groupTypesStr isEqualToString:@"All"]) {
groupTypes = ALAssetsGroupAll;
} else if ([groupTypesStr isEqualToString:@"Event"]) {
groupTypes = ALAssetsGroupEvent;
} else if ([groupTypesStr isEqualToString:@"Faces"]) {
groupTypes = ALAssetsGroupFaces;
} else if ([groupTypesStr isEqualToString:@"Library"]) {
groupTypes = ALAssetsGroupLibrary;
} else if ([groupTypesStr isEqualToString:@"PhotoStream"]) {
groupTypes = ALAssetsGroupPhotoStream;
} else {
groupTypes = ALAssetsGroupSavedPhotos;
}
BOOL __block foundAfter = NO;
BOOL __block hasNextPage = NO;
BOOL __block calledCallback = NO;
NSMutableArray *assets = [[NSMutableArray alloc] init];
[[RCTImageLoader assetsLibrary] enumerateGroupsWithTypes:groupTypes usingBlock:^(ALAssetsGroup *group, BOOL *stopGroups) {
if (group && (groupName == nil || [groupName isEqualToString:[group valueForProperty:ALAssetsGroupPropertyName]])) {
[group setAssetsFilter:ALAssetsFilter.allPhotos];
[group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stopAssets) {
if (result) {
NSString *uri = [(NSURL *)[result valueForProperty:ALAssetPropertyAssetURL] absoluteString];
if (afterCursor && !foundAfter) {
if ([afterCursor isEqualToString:uri]) {
foundAfter = YES;
}
return; // Skip until we get to the first one
}
if (first == [assets count]) {
*stopAssets = YES;
*stopGroups = YES;
hasNextPage = YES;
RCTAssert(calledCallback == NO, @"Called the callback before we finished processing the results.");
[self callCallback:callback withAssets:assets hasNextPage:hasNextPage];
calledCallback = YES;
return;
}
CGSize dimensions = [result defaultRepresentation].dimensions;
CLLocation *loc = [result valueForProperty:ALAssetPropertyLocation];
NSDate *date = [result valueForProperty:ALAssetPropertyDate];
[assets addObject:@{
@"node": @{
@"type": [result valueForProperty:ALAssetPropertyType],
@"group_name": [group valueForProperty:ALAssetsGroupPropertyName],
@"image": @{
@"uri": uri,
@"height": @(dimensions.height),
@"width": @(dimensions.width),
@"isStored": @YES,
},
@"timestamp": @([date timeIntervalSince1970]),
@"location": loc ?
@{
@"latitude": @(loc.coordinate.latitude),
@"longitude": @(loc.coordinate.longitude),
@"altitude": @(loc.altitude),
@"heading": @(loc.course),
@"speed": @(loc.speed),
} : @{},
}
}];
}
}];
} else {
// Sometimes the enumeration continues even if we set stop above, so we guard against calling the callback
// multiple times here.
if (!calledCallback) {
[self callCallback:callback withAssets:assets hasNextPage:hasNextPage];
calledCallback = YES;
}
}
} failureBlock:^(NSError *error) {
if (error.code != ALAssetsLibraryAccessUserDeniedError) {
RCTLogError(@"Failure while iterating through asset groups %@", error);
}
errorCallback(@[error.description]);
}];
}
@end

View File

@@ -10,6 +10,8 @@
1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */; };
1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */; };
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; };
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */; };
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */; };
58B511901A9E6BD600147676 /* RCTNetworkImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118C1A9E6BD600147676 /* RCTNetworkImageView.m */; };
58B511911A9E6BD600147676 /* RCTNetworkImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118E1A9E6BD600147676 /* RCTNetworkImageViewManager.m */; };
@@ -34,6 +36,10 @@
1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStaticImageManager.m; sourceTree = "<group>"; };
1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTGIFImage.h; sourceTree = "<group>"; };
1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImage.m; sourceTree = "<group>"; };
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCameraRollManager.h; sourceTree = "<group>"; };
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCameraRollManager.m; sourceTree = "<group>"; };
143879361AAD32A300F088A5 /* RCTImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageLoader.h; sourceTree = "<group>"; };
143879371AAD32A300F088A5 /* RCTImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoader.m; sourceTree = "<group>"; };
58B5115D1A9E6B3D00147676 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; };
58B511891A9E6BD600147676 /* RCTImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageDownloader.h; sourceTree = "<group>"; };
58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageDownloader.m; sourceTree = "<group>"; };
@@ -57,6 +63,10 @@
58B511541A9E6B3D00147676 = {
isa = PBXGroup;
children = (
143879361AAD32A300F088A5 /* RCTImageLoader.h */,
143879371AAD32A300F088A5 /* RCTImageLoader.m */,
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */,
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */,
1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */,
1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */,
58B511891A9E6BD600147676 /* RCTImageDownloader.h */,
@@ -142,6 +152,8 @@
1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */,
58B511901A9E6BD600147676 /* RCTNetworkImageView.m in Sources */,
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */,
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */,
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@@ -0,0 +1,13 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import <Foundation/Foundation.h>
@class ALAssetsLibrary;
@class UIImage;
@interface RCTImageLoader : NSObject
+ (ALAssetsLibrary *)assetsLibrary;
+ (void)loadImageWithTag:(NSString *)tag callback:(void (^)(NSError *error, UIImage *image))callback;
@end

View File

@@ -0,0 +1,98 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTImageLoader.h"
#import <AssetsLibrary/AssetsLibrary.h>
#import <Photos/PHAsset.h>
#import <Photos/PHFetchResult.h>
#import <Photos/PHImageManager.h>
#import <UIKit/UIKit.h>
#import "RCTConvert.h"
#import "RCTImageDownloader.h"
#import "RCTLog.h"
NSError *errorWithMessage(NSString *message) {
NSDictionary *errorInfo = @{NSLocalizedDescriptionKey: message};
NSError *error = [[NSError alloc] initWithDomain:RCTErrorDomain code:0 userInfo:errorInfo];
return error;
}
@implementation RCTImageLoader
+ (ALAssetsLibrary *)assetsLibrary
{
static ALAssetsLibrary *assetsLibrary = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
assetsLibrary = [[ALAssetsLibrary alloc] init];
});
return assetsLibrary;
}
+ (void)loadImageWithTag:(NSString *)imageTag callback:(void (^)(NSError *error, UIImage *image))callback
{
if ([imageTag hasPrefix:@"assets-library"]) {
[[RCTImageLoader assetsLibrary] assetForURL:[NSURL URLWithString:imageTag] resultBlock:^(ALAsset *asset) {
if (asset) {
ALAssetRepresentation *representation = [asset defaultRepresentation];
ALAssetOrientation orientation = [representation orientation];
UIImage *image = [UIImage imageWithCGImage:[representation fullResolutionImage] scale:1.0f orientation:(UIImageOrientation)orientation];
callback(nil, image);
} else {
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageTag];
NSError *error = errorWithMessage(errorText);
callback(error, nil);
}
} failureBlock:^(NSError *loadError) {
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageTag, loadError];
NSError *error = errorWithMessage(errorText);
callback(error, nil);
}];
} else if ([imageTag hasPrefix:@"ph://"]) {
// Using PhotoKit for iOS 8+
// 'ph://' prefix is used by FBMediaKit to differentiate between assets-library. It is prepended to the local ID so that it
// is in the form of NSURL which is what assets-library is based on.
// This means if we use any FB standard photo picker, we will get this prefix =(
NSString *phAssetID = [imageTag substringFromIndex:[@"ph://" length]];
PHFetchResult *results = [PHAsset fetchAssetsWithLocalIdentifiers:@[phAssetID] options:nil];
if (results.count == 0) {
NSString *errorText = [NSString stringWithFormat:@"Failed to fetch PHAsset with local identifier %@ with no error message.", phAssetID];
NSError *error = errorWithMessage(errorText);
callback(error, nil);
return;
}
PHAsset *asset = [results firstObject];
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:nil resultHandler:^(UIImage *result, NSDictionary *info) {
if (result) {
callback(nil, result);
} else {
NSString *errorText = [NSString stringWithFormat:@"Failed to load PHAsset with local identifier %@ with no error message.", phAssetID];
NSError *error = errorWithMessage(errorText);
callback(error, nil);
return;
}
}];
} else if ([imageTag hasPrefix:@"http"]) {
NSURL *url = [NSURL URLWithString:imageTag];
if (!url) {
NSString *errorMessage = [NSString stringWithFormat:@"Invalid URL: %@", imageTag];
callback(errorWithMessage(errorMessage), nil);
return;
}
[[RCTImageDownloader sharedInstance] downloadDataForURL:url block:^(NSData *data, NSError *error) {
if (error) {
callback(error, nil);
} else {
callback(nil, [UIImage imageWithData:data]);
}
}];
} else {
NSString *errorMessage = [NSString stringWithFormat:@"Unrecognized tag protocol: %@", imageTag];
NSError *error = errorWithMessage(errorMessage);
callback(error, nil);
}
}
@end

View File

@@ -24,7 +24,7 @@
// Apply trilinear filtering to smooth out mis-sized images
self.layer.minificationFilter = kCAFilterTrilinear;
self.layer.magnificationFilter = kCAFilterTrilinear;
super.image = image;
}

View File

@@ -6,6 +6,7 @@
#import "RCTConvert.h"
#import "RCTGIFImage.h"
#import "RCTImageLoader.h"
#import "RCTStaticImage.h"
@implementation RCTStaticImageManager
@@ -39,5 +40,19 @@ RCT_CUSTOM_VIEW_PROPERTY(tintColor, RCTStaticImage *)
view.tintColor = defaultView.tintColor;
}
}
RCT_CUSTOM_VIEW_PROPERTY(imageTag, RCTStaticImage *)
{
if (json) {
[RCTImageLoader loadImageWithTag:[RCTConvert NSString:json] callback:^(NSError *error, UIImage *image) {
if (error) {
RCTLogWarn(@"%@", error.localizedDescription);
} else {
view.image = image;
}
}];
} else {
view.image = defaultView.image;
}
}
@end