mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Make CameraRoll NativeModules TurboModule-compatible
Summary: Aside from RCTCameraRollManager, this diff makes the following NativeModules TurboModule-compatible: - RCTAssetsLibraryRequestHandler - RCTImagePickerManager - RCTPhotoLibraryImageLoader I couldn't convert CameraRollManager to a TurboModule because its NativeMoudle spec is located in fb-internal code. We should probably just move all these NativeModules outside of react-native-github. See: T54882565. Reviewed By: PeteTheHeat Differential Revision: D17678033 fbshipit-source-id: 4d10b7b1ad4e167bb9e46ff2bfd1559a5092e201
This commit is contained in:
committed by
Facebook Github Bot
parent
3908702de9
commit
601981a67d
+21
-10
@@ -7,8 +7,9 @@
|
||||
|
||||
#import "RCTAssetsLibraryRequestHandler.h"
|
||||
|
||||
#import <stdatomic.h>
|
||||
#import <atomic>
|
||||
#import <dlfcn.h>
|
||||
#import <memory>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import <Photos/Photos.h>
|
||||
@@ -17,6 +18,12 @@
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTNetworking.h>
|
||||
#import <React/RCTUtils.h>
|
||||
#import <ReactCommon/RCTTurboModule.h>
|
||||
|
||||
#import "RCTCameraRollPlugins.h"
|
||||
|
||||
@interface RCTAssetsLibraryRequestHandler() <RCTTurboModule>
|
||||
@end
|
||||
|
||||
@implementation RCTAssetsLibraryRequestHandler
|
||||
|
||||
@@ -38,9 +45,9 @@ RCT_EXPORT_MODULE()
|
||||
- (id)sendRequest:(NSURLRequest *)request
|
||||
withDelegate:(id<RCTURLRequestDelegate>)delegate
|
||||
{
|
||||
__block atomic_bool cancelled = ATOMIC_VAR_INIT(NO);
|
||||
auto cancelled = std::make_shared<std::atomic<bool>>(false);
|
||||
void (^cancellationBlock)(void) = ^{
|
||||
atomic_store(&cancelled, YES);
|
||||
cancelled->store(true);
|
||||
};
|
||||
|
||||
NSURL *requestURL = request.URL;
|
||||
@@ -48,15 +55,15 @@ RCT_EXPORT_MODULE()
|
||||
if (isPHUpload) {
|
||||
requestURL = [NSURL URLWithString:[@"ph" stringByAppendingString:[requestURL.absoluteString substringFromIndex:RCTNetworkingPHUploadHackScheme.length]]];
|
||||
}
|
||||
|
||||
|
||||
if (!requestURL) {
|
||||
NSString *const msg = [NSString stringWithFormat:@"Cannot send request without URL"];
|
||||
[delegate URLRequest:cancellationBlock didCompleteWithError:RCTErrorWithMessage(msg)];
|
||||
return cancellationBlock;
|
||||
}
|
||||
|
||||
|
||||
PHFetchResult<PHAsset *> *fetchResult;
|
||||
|
||||
|
||||
if ([requestURL.scheme caseInsensitiveCompare:@"ph"] == NSOrderedSame) {
|
||||
// Fetch assets using PHAsset localIdentifier (recommended)
|
||||
NSString *const localIdentifier = [requestURL.absoluteString substringFromIndex:@"ph://".length];
|
||||
@@ -70,7 +77,7 @@ RCT_EXPORT_MODULE()
|
||||
[delegate URLRequest:cancellationBlock didCompleteWithError:RCTErrorWithMessage(msg)];
|
||||
return cancellationBlock;
|
||||
}
|
||||
|
||||
|
||||
if (![fetchResult firstObject]) {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Failed to load asset"
|
||||
" at URL %@ with no error message.", requestURL];
|
||||
@@ -78,8 +85,8 @@ RCT_EXPORT_MODULE()
|
||||
[delegate URLRequest:cancellationBlock didCompleteWithError:error];
|
||||
return cancellationBlock;
|
||||
}
|
||||
|
||||
if (atomic_load(&cancelled)) {
|
||||
|
||||
if (cancelled->load()) {
|
||||
return cancellationBlock;
|
||||
}
|
||||
|
||||
@@ -149,7 +156,7 @@ RCT_EXPORT_MODULE()
|
||||
[delegate URLRequest:cancellationBlock didCompleteWithError:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
return cancellationBlock;
|
||||
}
|
||||
|
||||
@@ -159,3 +166,7 @@ RCT_EXPORT_MODULE()
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Class RCTAssetsLibraryRequestHandlerCls(void) {
|
||||
return RCTAssetsLibraryRequestHandler.class;
|
||||
}
|
||||
+19
-13
@@ -7,6 +7,7 @@
|
||||
|
||||
#import "RCTCameraRollManager.h"
|
||||
|
||||
#import <FBReactNativeSpec/FBReactNativeSpec.h>
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
@@ -21,6 +22,7 @@
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
#import "RCTCameraRollPlugins.h"
|
||||
#import "RCTAssetsLibraryRequestHandler.h"
|
||||
|
||||
@implementation RCTConvert (PHAssetCollectionSubtype)
|
||||
@@ -135,7 +137,7 @@ RCT_EXPORT_METHOD(saveToCameraRoll:(NSURLRequest *)request
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
||||
|
||||
void (^loadBlock)(void) = ^void() {
|
||||
if ([type isEqualToString:@"video"]) {
|
||||
inputURI = request.URL;
|
||||
@@ -146,13 +148,13 @@ RCT_EXPORT_METHOD(saveToCameraRoll:(NSURLRequest *)request
|
||||
reject(kErrorUnableToLoad, nil, error);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
inputImage = image;
|
||||
saveBlock();
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
requestPhotoLibraryAccess(reject, loadBlock);
|
||||
}
|
||||
|
||||
@@ -214,13 +216,13 @@ RCT_EXPORT_METHOD(getPhotos:(NSDictionary *)params
|
||||
if (groupName != nil) {
|
||||
collectionFetchOptions.predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"localizedTitle == '%@'", groupName]];
|
||||
}
|
||||
|
||||
|
||||
requestPhotoLibraryAccess(reject, ^{
|
||||
PHFetchResult<PHAssetCollection *> *const assetCollectionFetchResult = [PHAssetCollection fetchAssetCollectionsWithType:collectionType subtype:collectionSubtype options:collectionFetchOptions];
|
||||
[assetCollectionFetchResult enumerateObjectsUsingBlock:^(PHAssetCollection * _Nonnull assetCollection, NSUInteger collectionIdx, BOOL * _Nonnull stopCollections) {
|
||||
// Enumerate assets within the collection
|
||||
PHFetchResult<PHAsset *> *const assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:assetFetchOptions];
|
||||
|
||||
|
||||
[assetsFetchResult enumerateObjectsUsingBlock:^(PHAsset * _Nonnull asset, NSUInteger assetIdx, BOOL * _Nonnull stopAssets) {
|
||||
NSString *const uri = [NSString stringWithFormat:@"ph://%@", [asset localIdentifier]];
|
||||
if (afterCursor && !foundAfter) {
|
||||
@@ -229,18 +231,18 @@ RCT_EXPORT_METHOD(getPhotos:(NSDictionary *)params
|
||||
}
|
||||
return; // skip until we get to the first one
|
||||
}
|
||||
|
||||
|
||||
// Get underlying resources of an asset - this includes files as well as details about edited PHAssets
|
||||
if ([mimeTypes count] > 0) {
|
||||
NSArray<PHAssetResource *> *const assetResources = [PHAssetResource assetResourcesForAsset:asset];
|
||||
if (![assetResources firstObject]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
PHAssetResource *const _Nonnull resource = [assetResources firstObject];
|
||||
CFStringRef const uti = (__bridge CFStringRef _Nonnull)(resource.uniformTypeIdentifier);
|
||||
NSString *const mimeType = (NSString *)CFBridgingRelease(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType));
|
||||
|
||||
|
||||
BOOL __block mimeTypeFound = NO;
|
||||
[mimeTypes enumerateObjectsUsingBlock:^(NSString * _Nonnull mimeTypeFilter, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if ([mimeType isEqualToString:mimeTypeFilter]) {
|
||||
@@ -248,12 +250,12 @@ RCT_EXPORT_METHOD(getPhotos:(NSDictionary *)params
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
if (!mimeTypeFound) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If we've accumulated enough results to resolve a single promise
|
||||
if (first == assets.count) {
|
||||
*stopAssets = YES;
|
||||
@@ -264,7 +266,7 @@ RCT_EXPORT_METHOD(getPhotos:(NSDictionary *)params
|
||||
resolvedPromise = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NSString *const assetMediaTypeLabel = (asset.mediaType == PHAssetMediaTypeVideo
|
||||
? @"video"
|
||||
: (asset.mediaType == PHAssetMediaTypeImage
|
||||
@@ -273,7 +275,7 @@ RCT_EXPORT_METHOD(getPhotos:(NSDictionary *)params
|
||||
? @"audio"
|
||||
: @"unknown")));
|
||||
CLLocation *const loc = asset.location;
|
||||
|
||||
|
||||
// A note on isStored: in the previous code that used ALAssets, isStored
|
||||
// was always set to YES, probably because iCloud-synced images were never returned (?).
|
||||
// To get the "isStored" information and filename, we would need to actually request the
|
||||
@@ -304,7 +306,7 @@ RCT_EXPORT_METHOD(getPhotos:(NSDictionary *)params
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
|
||||
|
||||
// If we get this far and haven't resolved the promise yet, we reached the end of the list of photos
|
||||
if (!resolvedPromise) {
|
||||
hasNextPage = NO;
|
||||
@@ -345,3 +347,7 @@ static void checkPhotoLibraryConfig()
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Class RCTCameraRollManagerCls(void) {
|
||||
return RCTCameraRollManager.class;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated by an internal plugin build system
|
||||
*/
|
||||
|
||||
#ifdef RN_DISABLE_OSS_PLUGIN_HEADER
|
||||
|
||||
// FB Internal: Plugins.h is autogenerated by the build system.
|
||||
#import "Plugins.h"
|
||||
|
||||
#else
|
||||
|
||||
// OSS-compatibility layer
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wreturn-type-c-linkage"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// RCTTurboModuleManagerDelegate should call this to resolve module classes.
|
||||
Class RCTCameraRollClassProvider(const char *name);
|
||||
|
||||
// Lookup functions
|
||||
Class RCTAssetsLibraryRequestHandlerCls(void);
|
||||
Class RCTCameraRollManagerCls(void);
|
||||
Class RCTImagePickerManagerCls(void);
|
||||
Class RCTPhotoLibraryImageLoaderCls(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif // RN_DISABLE_OSS_PLUGIN_HEADER
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated by an internal plugin build system
|
||||
*/
|
||||
|
||||
#ifndef RN_DISABLE_OSS_PLUGIN_HEADER
|
||||
|
||||
// OSS-compatibility layer
|
||||
|
||||
#import "RCTCameraRollPlugins.h"
|
||||
|
||||
#import <string>
|
||||
#import <unordered_map>
|
||||
|
||||
Class RCTCameraRollClassProvider(const char *name) {
|
||||
static std::unordered_map<std::string, Class (*)(void)> sCoreModuleClassMap = {
|
||||
{"AssetsLibraryRequestHandler", RCTAssetsLibraryRequestHandlerCls},
|
||||
{"CameraRollManager", RCTCameraRollManagerCls},
|
||||
{"ImagePickerIOS", RCTImagePickerManagerCls},
|
||||
{"PhotoLibraryImageLoader", RCTPhotoLibraryImageLoaderCls},
|
||||
};
|
||||
|
||||
auto p = sCoreModuleClassMap.find(name);
|
||||
if (p != sCoreModuleClassMap.end()) {
|
||||
auto classFunc = p->second;
|
||||
return classFunc();
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
#endif // RN_DISABLE_OSS_PLUGIN_HEADER
|
||||
+19
-8
@@ -8,6 +8,7 @@
|
||||
|
||||
#import "RCTImagePickerManager.h"
|
||||
|
||||
#import <FBReactNativeSpec/FBReactNativeSpec.h>
|
||||
#import <MobileCoreServices/UTCoreTypes.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@@ -16,6 +17,8 @@
|
||||
#import <React/RCTRootView.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
#import "RCTCameraRollPlugins.h"
|
||||
|
||||
@interface RCTImagePickerController : UIImagePickerController
|
||||
|
||||
@property (nonatomic, assign) BOOL unmirrorFrontFacingCamera;
|
||||
@@ -26,8 +29,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTImagePickerManager () <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
|
||||
|
||||
@interface RCTImagePickerManager () <UIImagePickerControllerDelegate, UINavigationControllerDelegate, NativeImagePickerIOSSpec>
|
||||
@end
|
||||
|
||||
@implementation RCTImagePickerManager
|
||||
@@ -78,7 +80,7 @@ RCT_EXPORT_METHOD(canUseCamera:(RCTResponseSenderBlock)callback)
|
||||
callback(@[@([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(openCameraDialog:(NSDictionary *)config
|
||||
RCT_EXPORT_METHOD(openCameraDialog:(JS::NativeImagePickerIOS::SpecOpenCameraDialogConfig &)config
|
||||
successCallback:(RCTResponseSenderBlock)callback
|
||||
cancelCallback:(RCTResponseSenderBlock)cancelCallback)
|
||||
{
|
||||
@@ -92,9 +94,9 @@ RCT_EXPORT_METHOD(openCameraDialog:(NSDictionary *)config
|
||||
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
|
||||
NSArray<NSString *> *availableMediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
|
||||
imagePicker.mediaTypes = availableMediaTypes;
|
||||
imagePicker.unmirrorFrontFacingCamera = [RCTConvert BOOL:config[@"unmirrorFrontFacingCamera"]];
|
||||
imagePicker.unmirrorFrontFacingCamera = config.unmirrorFrontFacingCamera() ? YES : NO;
|
||||
|
||||
if ([RCTConvert BOOL:config[@"videoMode"]]) {
|
||||
if (config.videoMode()) {
|
||||
imagePicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo;
|
||||
}
|
||||
|
||||
@@ -103,7 +105,7 @@ RCT_EXPORT_METHOD(openCameraDialog:(NSDictionary *)config
|
||||
cancelCallback:cancelCallback];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(openSelectDialog:(NSDictionary *)config
|
||||
RCT_EXPORT_METHOD(openSelectDialog:(JS::NativeImagePickerIOS::SpecOpenSelectDialogConfig &)config
|
||||
successCallback:(RCTResponseSenderBlock)callback
|
||||
cancelCallback:(RCTResponseSenderBlock)cancelCallback)
|
||||
{
|
||||
@@ -117,10 +119,10 @@ RCT_EXPORT_METHOD(openSelectDialog:(NSDictionary *)config
|
||||
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
|
||||
|
||||
NSMutableArray<NSString *> *allowedTypes = [NSMutableArray new];
|
||||
if ([RCTConvert BOOL:config[@"showImages"]]) {
|
||||
if (config.showImages()) {
|
||||
[allowedTypes addObject:(NSString *)kUTTypeImage];
|
||||
}
|
||||
if ([RCTConvert BOOL:config[@"showVideos"]]) {
|
||||
if (config.showVideos()) {
|
||||
[allowedTypes addObject:(NSString *)kUTTypeMovie];
|
||||
}
|
||||
|
||||
@@ -222,4 +224,13 @@ didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info
|
||||
}
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModuleWithJsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
||||
{
|
||||
return std::make_shared<facebook::react::NativeImagePickerIOSSpecJSI>(self, jsInvoker);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Class RCTImagePickerManagerCls(void) {
|
||||
return RCTImagePickerManager.class;
|
||||
}
|
||||
+11
-1
@@ -8,8 +8,14 @@
|
||||
#import "RCTPhotoLibraryImageLoader.h"
|
||||
|
||||
#import <Photos/Photos.h>
|
||||
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTUtils.h>
|
||||
#import <ReactCommon/RCTTurboModule.h>
|
||||
|
||||
#import "RCTCameraRollPlugins.h"
|
||||
|
||||
@interface RCTPhotoLibraryImageLoader () <RCTTurboModule>
|
||||
@end
|
||||
|
||||
@implementation RCTPhotoLibraryImageLoader
|
||||
|
||||
@@ -109,3 +115,7 @@ RCT_EXPORT_MODULE()
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Class RCTPhotoLibraryImageLoaderCls(void) {
|
||||
return RCTPhotoLibraryImageLoader.class;
|
||||
}
|
||||
Reference in New Issue
Block a user